Skip to content

Commit b37e736

Browse files
chore: DSPX-1226 use key ids per attribute (#634)
* chore: DSPX-1236 allow using proper endpoints for OIDC * Use platformUrl instead of policyEndpoint * Add additional key types. * Use updated protos. * Refactor KAS info cache * Fix tests. * Add fetch tests to support branch coverage. * Add keys to cache when they are able to be pre-loaded. * Update cache key joining to use separators. * Add tests and update key function.
1 parent fad1f26 commit b37e736

27 files changed

+2416
-345
lines changed

lib/src/auth/oidc-clientcredentials-provider.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export class OIDCClientCredentialsProvider implements AuthProvider {
99
clientId,
1010
clientSecret,
1111
oidcOrigin,
12+
oidcTokenEndpoint,
13+
oidcUserInfoEndpoint,
1214
}: Partial<ClientSecretCredentials> & Omit<ClientSecretCredentials, 'exchange'>) {
1315
if (!clientId || !clientSecret) {
1416
throw new ConfigurationError('clientId & clientSecret required for client credentials flow');
@@ -19,6 +21,8 @@ export class OIDCClientCredentialsProvider implements AuthProvider {
1921
clientId,
2022
clientSecret,
2123
oidcOrigin,
24+
oidcTokenEndpoint,
25+
oidcUserInfoEndpoint,
2226
});
2327
}
2428

lib/src/auth/oidc-externaljwt-provider.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export class OIDCExternalJwtProvider implements AuthProvider {
1010
clientId,
1111
externalJwt,
1212
oidcOrigin,
13+
oidcTokenEndpoint,
14+
oidcUserInfoEndpoint,
1315
}: Partial<ExternalJwtCredentials> & Omit<ExternalJwtCredentials, 'exchange'>) {
1416
if (!clientId || !externalJwt) {
1517
throw new ConfigurationError('external JWT exchange reequires client id and jwt');
@@ -18,8 +20,10 @@ export class OIDCExternalJwtProvider implements AuthProvider {
1820
this.oidcAuth = new AccessToken({
1921
exchange: 'external',
2022
clientId,
21-
oidcOrigin,
2223
externalJwt,
24+
oidcOrigin,
25+
oidcTokenEndpoint,
26+
oidcUserInfoEndpoint,
2327
});
2428

2529
this.externalJwt = externalJwt;

lib/src/auth/oidc-refreshtoken-provider.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export class OIDCRefreshTokenProvider implements AuthProvider {
2424
clientId,
2525
refreshToken,
2626
oidcOrigin,
27+
oidcTokenEndpoint,
28+
oidcUserInfoEndpoint,
2729
}: Partial<RefreshTokenCredentials> & Omit<RefreshTokenCredentials, 'exchange'>) {
2830
if (!clientId || !refreshToken) {
2931
throw new ConfigurationError('refresh token or client id missing');
@@ -32,8 +34,10 @@ export class OIDCRefreshTokenProvider implements AuthProvider {
3234
this.oidcAuth = new AccessToken({
3335
exchange: 'refresh',
3436
clientId,
35-
refreshToken: refreshToken,
37+
refreshToken,
3638
oidcOrigin,
39+
oidcTokenEndpoint,
40+
oidcUserInfoEndpoint,
3741
});
3842
this.refreshToken = refreshToken;
3943
}

lib/src/auth/oidc.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export type CommonCredentials = {
1212
clientId: string;
1313
/** The endpoint of the OIDC IdP to authenticate against, ex. 'https://virtru.com/auth' */
1414
oidcOrigin: string;
15+
oidcTokenEndpoint?: string;
16+
oidcUserInfoEndpoint?: string;
1517
/** Whether or not DPoP is enabled. */
1618
dpopEnabled?: boolean;
1719

@@ -89,6 +91,8 @@ export class AccessToken {
8991
data?: AccessTokenResponse;
9092

9193
baseUrl: string;
94+
tokenEndpoint: string;
95+
userInfoEndpoint: string;
9296

9397
signingKey?: CryptoKeyPair;
9498

@@ -119,6 +123,9 @@ export class AccessToken {
119123
this.config = cfg;
120124
this.request = request;
121125
this.baseUrl = rstrip(cfg.oidcOrigin, '/');
126+
this.tokenEndpoint = cfg.oidcTokenEndpoint || `${this.baseUrl}/protocol/openid-connect/token`;
127+
this.userInfoEndpoint =
128+
cfg.oidcUserInfoEndpoint || `${this.baseUrl}/protocol/openid-connect/userinfo`;
122129
this.signingKey = cfg.signingKey;
123130
}
124131

@@ -128,21 +135,20 @@ export class AccessToken {
128135
* @returns
129136
*/
130137
async info(accessToken: string): Promise<unknown> {
131-
const url = `${this.baseUrl}/protocol/openid-connect/userinfo`;
132138
const headers = {
133139
...this.extraHeaders,
134140
Authorization: `Bearer ${accessToken}`,
135141
} as Record<string, string>;
136142
if (this.config.dpopEnabled && this.signingKey) {
137-
headers.DPoP = await dpopFn(this.signingKey, url, 'POST');
143+
headers.DPoP = await dpopFn(this.signingKey, this.userInfoEndpoint, 'POST');
138144
}
139-
const response = await (this.request || fetch)(url, {
145+
const response = await (this.request || fetch)(this.userInfoEndpoint, {
140146
headers,
141147
});
142148
if (!response.ok) {
143149
console.error(await response.text());
144150
throw new TdfError(
145-
`auth info fail: GET [${url}] => ${response.status} ${response.statusText}`
151+
`auth info fail: GET [${this.userInfoEndpoint}] => ${response.status} ${response.statusText}`
146152
);
147153
}
148154

@@ -171,7 +177,6 @@ export class AccessToken {
171177
}
172178

173179
async accessTokenLookup(cfg: OIDCCredentials) {
174-
const url = `${this.baseUrl}/protocol/openid-connect/token`;
175180
let body;
176181
switch (cfg.exchange) {
177182
case 'client':
@@ -198,11 +203,11 @@ export class AccessToken {
198203
};
199204
break;
200205
}
201-
const response = await this.doPost(url, body);
206+
const response = await this.doPost(this.tokenEndpoint, body);
202207
if (!response.ok) {
203208
console.error(await response.text());
204209
throw new TdfError(
205-
`token/code exchange fail: POST [${url}] => ${response.status} ${response.statusText}`
210+
`token/code exchange fail: POST [${this.tokenEndpoint}] => ${response.status} ${response.statusText}`
206211
);
207212
}
208213
return response.json();

lib/src/auth/providers.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export const clientSecretAuthProvider = async (
3636
clientId: clientConfig.clientId,
3737
clientSecret: clientConfig.clientSecret,
3838
oidcOrigin: clientConfig.oidcOrigin,
39+
oidcTokenEndpoint: clientConfig.oidcTokenEndpoint,
40+
oidcUserInfoEndpoint: clientConfig.oidcUserInfoEndpoint,
3941
});
4042
};
4143

@@ -62,6 +64,8 @@ export const externalAuthProvider = async (
6264
clientId: clientConfig.clientId,
6365
externalJwt: clientConfig.externalJwt,
6466
oidcOrigin: clientConfig.oidcOrigin,
67+
oidcTokenEndpoint: clientConfig.oidcTokenEndpoint,
68+
oidcUserInfoEndpoint: clientConfig.oidcUserInfoEndpoint,
6569
});
6670
};
6771

@@ -86,6 +90,8 @@ export const refreshAuthProvider = async (
8690
clientId: clientConfig.clientId,
8791
refreshToken: clientConfig.refreshToken,
8892
oidcOrigin: clientConfig.oidcOrigin,
93+
oidcTokenEndpoint: clientConfig.oidcTokenEndpoint,
94+
oidcUserInfoEndpoint: clientConfig.oidcUserInfoEndpoint,
8995
});
9096
};
9197

lib/src/opentdf.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ export type MimeType = `${string}/${string}`;
112112
export type SplitStep = {
113113
/** Which KAS to use to rewrap this segment of the key. */
114114
kas: string;
115-
116115
/**
117116
* An identifier for a key segment.
118117
* Leave empty to share the key.
@@ -389,6 +388,7 @@ export class OpenTDF {
389388
authProvider,
390389
dpopKeys,
391390
kasEndpoint: this.platformUrl || 'https://disallow.all.invalid',
391+
platformUrl,
392392
policyEndpoint,
393393
});
394394
this.dpopKeys =

lib/src/platform/entityresolution/entity_resolution_pb.ts

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/src/platform/policy/attributes/attributes_pb.ts

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)