@monocloud/auth-core 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +324 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +164 -36
- package/dist/index.mjs +324 -113
- package/dist/index.mjs.map +1 -1
- package/dist/{types-Dox1pap7.d.mts → types-txr_P9Iq.d.mts} +183 -32
- package/dist/utils/index.cjs +1 -1
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.mts +1 -1
- package/dist/utils/index.mjs +2 -2
- package/dist/utils/index.mjs.map +1 -1
- package/dist/utils/internal.cjs +36 -0
- package/dist/utils/internal.cjs.map +1 -1
- package/dist/utils/internal.d.mts +23 -2
- package/dist/utils/internal.mjs +35 -1
- package/dist/utils/internal.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_utils_internal = require('./utils/internal.cjs');
|
|
3
|
+
const require_utils_index = require('./utils/index.cjs');
|
|
3
4
|
|
|
4
5
|
//#region src/errors/monocloud-auth-base-error.ts
|
|
5
6
|
/**
|
|
@@ -57,6 +58,129 @@ var MonoCloudTokenError = class extends MonoCloudAuthBaseError {};
|
|
|
57
58
|
*/
|
|
58
59
|
var MonoCloudValidationError = class extends MonoCloudAuthBaseError {};
|
|
59
60
|
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/helper.ts
|
|
63
|
+
const JWT_ASSERTION_CLOCK_SKEW = 5;
|
|
64
|
+
function assertMetadataProperty(metadata, property) {
|
|
65
|
+
if (metadata[property] === void 0 || metadata[property] === null) throw new MonoCloudValidationError(`${property} endpoint is required but not available in the issuer metadata`);
|
|
66
|
+
}
|
|
67
|
+
const innerFetch = async (input, reqInit = {}, customFetch) => {
|
|
68
|
+
try {
|
|
69
|
+
return await (customFetch ?? fetch)(input, reqInit);
|
|
70
|
+
} catch (e) {
|
|
71
|
+
/* v8 ignore next -- @preserve */
|
|
72
|
+
throw new MonoCloudHttpError(e.message ?? "Unexpected Network Error");
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const deserializeJson = async (res) => {
|
|
76
|
+
try {
|
|
77
|
+
return await res.json();
|
|
78
|
+
} catch (e) {
|
|
79
|
+
throw new MonoCloudHttpError(
|
|
80
|
+
/* v8 ignore next -- @preserve */
|
|
81
|
+
`Failed to parse response body as JSON ${e.message ? `: ${e.message}` : ""}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/monocloud-oidc-client-base.ts
|
|
88
|
+
/**
|
|
89
|
+
* @category Classes
|
|
90
|
+
*/
|
|
91
|
+
var MonoCloudOidcClientBase = class {
|
|
92
|
+
/**
|
|
93
|
+
* Creates a new instance of MonoCloudOidcClientBase.
|
|
94
|
+
*
|
|
95
|
+
* @param tenantDomain - The tenant domain URL.
|
|
96
|
+
* @param metadataCacheDuration - Duration (in seconds) to cache OpenID Connect discovery metadata. Defaults to 300 (5 minutes).
|
|
97
|
+
* @param jwksCacheDuration - Duration (in seconds) to cache the JSON Web Key Set (JWKS). Defaults to 300 (5 minutes).
|
|
98
|
+
* @param fetcher - Custom `fetch` implementation used for making HTTP requests. Falls back to the global `fetch` if not provided.
|
|
99
|
+
*/
|
|
100
|
+
constructor(tenantDomain, metadataCacheDuration, jwksCacheDuration, fetcher) {
|
|
101
|
+
this.jwksCacheExpiry = 0;
|
|
102
|
+
this.jwksCacheDuration = 300;
|
|
103
|
+
this.metadataCacheExpiry = 0;
|
|
104
|
+
this.metadataCacheDuration = 300;
|
|
105
|
+
tenantDomain ??= "";
|
|
106
|
+
/* v8 ignore next -- @preserve */
|
|
107
|
+
this.tenantDomain = `${!tenantDomain.startsWith("https://") ? "https://" : ""}${tenantDomain.endsWith("/") ? tenantDomain.slice(0, -1) : tenantDomain}`;
|
|
108
|
+
if (metadataCacheDuration !== void 0) this.metadataCacheDuration = metadataCacheDuration;
|
|
109
|
+
if (jwksCacheDuration !== void 0) this.jwksCacheDuration = jwksCacheDuration;
|
|
110
|
+
this.fetcher = fetcher;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Fetches the authorization server metadata from the .well-known endpoint.
|
|
114
|
+
* The metadata is cached for 5 minutes by default.
|
|
115
|
+
*
|
|
116
|
+
* @param forceRefresh - If `true`, bypasses the cache and fetches fresh metadata from the server.
|
|
117
|
+
*
|
|
118
|
+
* @returns The issuer metadata for the tenant, retrieved from the OpenID Connect discovery endpoint.
|
|
119
|
+
*
|
|
120
|
+
* @throws {@link MonoCloudHttpError} - Thrown if there is a network error during the request or
|
|
121
|
+
* unexpected status code during the request or a serialization error while processing the response.
|
|
122
|
+
*
|
|
123
|
+
*/
|
|
124
|
+
async getMetadata(forceRefresh = false) {
|
|
125
|
+
if (!forceRefresh && this.metadata && this.metadataCacheExpiry > require_utils_internal.now()) return this.metadata;
|
|
126
|
+
this.metadata = void 0;
|
|
127
|
+
const response = await innerFetch(`${this.tenantDomain}/.well-known/openid-configuration`, void 0, this.fetcher);
|
|
128
|
+
if (response.status !== 200) throw new MonoCloudHttpError(`Error while fetching metadata. Unexpected status code: ${response.status}`);
|
|
129
|
+
const metadata = await deserializeJson(response);
|
|
130
|
+
this.metadata = metadata;
|
|
131
|
+
this.metadataCacheExpiry = require_utils_internal.now() + this.metadataCacheDuration;
|
|
132
|
+
return metadata;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Fetches the JSON Web Keys used to sign the ID token.
|
|
136
|
+
* The JWKS is cached for 5 minutes by default.
|
|
137
|
+
*
|
|
138
|
+
* @param forceRefresh - If `true`, bypasses the cache and fetches fresh set of JWKS from the server.
|
|
139
|
+
*
|
|
140
|
+
* @returns The JSON Web Key Set containing the public keys for token verification.
|
|
141
|
+
*
|
|
142
|
+
* @throws {@link MonoCloudHttpError} - Thrown if there is a network error during the request or
|
|
143
|
+
* unexpected status code during the request or a serialization error while processing the response.
|
|
144
|
+
*
|
|
145
|
+
*/
|
|
146
|
+
async getJwks(forceRefresh = false) {
|
|
147
|
+
if (!forceRefresh && this.jwks && this.jwksCacheExpiry > require_utils_internal.now()) return this.jwks;
|
|
148
|
+
this.jwks = void 0;
|
|
149
|
+
const metadata = await this.getMetadata();
|
|
150
|
+
assertMetadataProperty(metadata, "jwks_uri");
|
|
151
|
+
const response = await innerFetch(metadata.jwks_uri, void 0, this.fetcher);
|
|
152
|
+
if (response.status !== 200) throw new MonoCloudHttpError(`Error while fetching JWKS. Unexpected status code: ${response.status}`);
|
|
153
|
+
const jwks = await deserializeJson(response);
|
|
154
|
+
this.jwks = jwks;
|
|
155
|
+
this.jwksCacheExpiry = require_utils_internal.now() + this.jwksCacheDuration;
|
|
156
|
+
return jwks;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Decodes the payload of a JSON Web Token (JWT) and returns it as an object.
|
|
160
|
+
*
|
|
161
|
+
* >Note: THIS METHOD DOES NOT VERIFY JWT TOKENS.
|
|
162
|
+
*
|
|
163
|
+
* @param jwt - JWT to decode.
|
|
164
|
+
*
|
|
165
|
+
* @returns Decoded payload.
|
|
166
|
+
*
|
|
167
|
+
* @throws {@link MonoCloudTokenError} - If decoding fails
|
|
168
|
+
*
|
|
169
|
+
*/
|
|
170
|
+
static decodeJwt(jwt) {
|
|
171
|
+
try {
|
|
172
|
+
const [, payload] = jwt.split(".");
|
|
173
|
+
if (!payload?.trim()) throw new MonoCloudTokenError("JWT does not contain payload");
|
|
174
|
+
const decoded = require_utils_internal.decodeBase64Url(payload);
|
|
175
|
+
if (!decoded.startsWith("{")) throw new MonoCloudTokenError("Payload is not an object");
|
|
176
|
+
return JSON.parse(decoded);
|
|
177
|
+
} catch (e) {
|
|
178
|
+
if (e instanceof MonoCloudAuthBaseError) throw e;
|
|
179
|
+
throw new MonoCloudTokenError("Could not parse payload. Malformed payload");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
60
184
|
//#endregion
|
|
61
185
|
//#region src/client-auth.ts
|
|
62
186
|
const algToSubtle = (alg) => {
|
|
@@ -215,13 +339,15 @@ const clientAuth = async (clientId, clientSecret, method, issuer, headers, body,
|
|
|
215
339
|
case method === "private_key_jwt" && typeof clientSecret === "object" && clientSecret.kty !== "oct" && !!issuer && !!body:
|
|
216
340
|
await jwtAssertionGenerator(issuer, clientId, clientSecret, body, jwtAssertionSkew ?? 0);
|
|
217
341
|
break;
|
|
342
|
+
case (method === "tls_client_auth" || method === "self_signed_tls_client_auth") && !!body:
|
|
343
|
+
body.set("client_id", clientId);
|
|
344
|
+
break;
|
|
218
345
|
default: throw new Error("Invalid Client Authentication Method");
|
|
219
346
|
}
|
|
220
347
|
};
|
|
221
348
|
|
|
222
349
|
//#endregion
|
|
223
350
|
//#region src/monocloud-oidc-client.ts
|
|
224
|
-
const JWT_ASSERTION_CLOCK_SKEW = 5;
|
|
225
351
|
const FILTER_ID_TOKEN_CLAIMS = [
|
|
226
352
|
"iss",
|
|
227
353
|
"exp",
|
|
@@ -234,45 +360,23 @@ const FILTER_ID_TOKEN_CLAIMS = [
|
|
|
234
360
|
"at_hash",
|
|
235
361
|
"s_hash"
|
|
236
362
|
];
|
|
237
|
-
function assertMetadataProperty(metadata, property) {
|
|
238
|
-
if (metadata[property] === void 0 || metadata[property] === null) throw new MonoCloudValidationError(`${property} endpoint is required but not available in the issuer metadata`);
|
|
239
|
-
}
|
|
240
|
-
const innerFetch = async (input, reqInit = {}) => {
|
|
241
|
-
try {
|
|
242
|
-
return await fetch(input, reqInit);
|
|
243
|
-
} catch (e) {
|
|
244
|
-
/* v8 ignore next -- @preserve */
|
|
245
|
-
throw new MonoCloudHttpError(e.message ?? "Unexpected Network Error");
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
const deserializeJson = async (res) => {
|
|
249
|
-
try {
|
|
250
|
-
return await res.json();
|
|
251
|
-
} catch (e) {
|
|
252
|
-
throw new MonoCloudHttpError(
|
|
253
|
-
/* v8 ignore next -- @preserve */
|
|
254
|
-
`Failed to parse response body as JSON ${e.message ? `: ${e.message}` : ""}`
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
363
|
/**
|
|
259
364
|
* @category Classes
|
|
260
365
|
*/
|
|
261
|
-
var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
366
|
+
var MonoCloudOidcClient = class MonoCloudOidcClient extends MonoCloudOidcClientBase {
|
|
367
|
+
/**
|
|
368
|
+
* Creates a new instance of MonoCloudOidcClient.
|
|
369
|
+
*
|
|
370
|
+
* @param tenantDomain - The tenant domain URL.
|
|
371
|
+
* @param clientId - Client id of the application registered in MonoCloud.
|
|
372
|
+
* @param options - Additional client configuration options.
|
|
373
|
+
*/
|
|
262
374
|
constructor(tenantDomain, clientId, options) {
|
|
263
|
-
|
|
264
|
-
this.jwksCacheDuration = 300;
|
|
265
|
-
this.metadataCacheExpiry = 0;
|
|
266
|
-
this.metadataCacheDuration = 300;
|
|
267
|
-
tenantDomain ??= "";
|
|
268
|
-
/* v8 ignore next -- @preserve */
|
|
269
|
-
this.tenantDomain = `${!tenantDomain.startsWith("https://") ? "https://" : ""}${tenantDomain.endsWith("/") ? tenantDomain.slice(0, -1) : tenantDomain}`;
|
|
375
|
+
super(tenantDomain, options?.metadataCacheDuration, options?.jwksCacheDuration, options?.fetcher);
|
|
270
376
|
this.clientId = clientId;
|
|
271
377
|
this.clientSecret = options?.clientSecret;
|
|
272
378
|
this.authMethod = options?.clientAuthMethod ?? "client_secret_basic";
|
|
273
379
|
this.idTokenSigningAlgorithm = options?.idTokenSigningAlgorithm ?? "RS256";
|
|
274
|
-
if (options?.jwksCacheDuration) this.jwksCacheDuration = options.jwksCacheDuration;
|
|
275
|
-
if (options?.metadataCacheDuration) this.metadataCacheDuration = options.metadataCacheDuration;
|
|
276
380
|
}
|
|
277
381
|
/**
|
|
278
382
|
* Generates an authorization URL with specified parameters.
|
|
@@ -318,52 +422,6 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
318
422
|
return `${metadata.authorization_endpoint}?${queryParams.toString()}`;
|
|
319
423
|
}
|
|
320
424
|
/**
|
|
321
|
-
* Fetches the authorization server metadata from the .well-known endpoint.
|
|
322
|
-
* The metadata is cached for 1 minute.
|
|
323
|
-
*
|
|
324
|
-
* @param forceRefresh - If `true`, bypasses the cache and fetches fresh metadata from the server.
|
|
325
|
-
*
|
|
326
|
-
* @returns The issuer metadata for the tenant, retrieved from the OpenID Connect discovery endpoint.
|
|
327
|
-
*
|
|
328
|
-
* @throws {@link MonoCloudHttpError} - Thrown if there is a network error during the request or
|
|
329
|
-
* unexpected status code during the request or a serialization error while processing the response.
|
|
330
|
-
*
|
|
331
|
-
*/
|
|
332
|
-
async getMetadata(forceRefresh = false) {
|
|
333
|
-
if (!forceRefresh && this.metadata && this.metadataCacheExpiry > require_utils_internal.now()) return this.metadata;
|
|
334
|
-
this.metadata = void 0;
|
|
335
|
-
const response = await innerFetch(`${this.tenantDomain}/.well-known/openid-configuration`);
|
|
336
|
-
if (response.status !== 200) throw new MonoCloudHttpError(`Error while fetching metadata. Unexpected status code: ${response.status}`);
|
|
337
|
-
const metadata = await deserializeJson(response);
|
|
338
|
-
this.metadata = metadata;
|
|
339
|
-
this.metadataCacheExpiry = require_utils_internal.now() + this.metadataCacheDuration;
|
|
340
|
-
return metadata;
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Fetches the JSON Web Keys used to sign the ID token.
|
|
344
|
-
* The JWKS is cached for 1 minute.
|
|
345
|
-
*
|
|
346
|
-
* @param forceRefresh - If `true`, bypasses the cache and fetches fresh set of JWKS from the server.
|
|
347
|
-
*
|
|
348
|
-
* @returns The JSON Web Key Set containing the public keys for token verification.
|
|
349
|
-
*
|
|
350
|
-
* @throws {@link MonoCloudHttpError} - Thrown if there is a network error during the request or
|
|
351
|
-
* unexpected status code during the request or a serialization error while processing the response.
|
|
352
|
-
*
|
|
353
|
-
*/
|
|
354
|
-
async getJwks(forceRefresh = false) {
|
|
355
|
-
if (!forceRefresh && this.jwks && this.jwksCacheExpiry > require_utils_internal.now()) return this.jwks;
|
|
356
|
-
this.jwks = void 0;
|
|
357
|
-
const metadata = await this.getMetadata();
|
|
358
|
-
assertMetadataProperty(metadata, "jwks_uri");
|
|
359
|
-
const response = await innerFetch(metadata.jwks_uri);
|
|
360
|
-
if (response.status !== 200) throw new MonoCloudHttpError(`Error while fetching JWKS. Unexpected status code: ${response.status}`);
|
|
361
|
-
const jwks = await deserializeJson(response);
|
|
362
|
-
this.jwks = jwks;
|
|
363
|
-
this.jwksCacheExpiry = require_utils_internal.now() + this.jwksCacheDuration;
|
|
364
|
-
return jwks;
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
425
|
* Performs a pushed authorization request.
|
|
368
426
|
*
|
|
369
427
|
* @param params - Authorization Parameters.
|
|
@@ -412,7 +470,7 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
412
470
|
body: body.toString(),
|
|
413
471
|
method: "POST",
|
|
414
472
|
headers
|
|
415
|
-
});
|
|
473
|
+
}, this.fetcher);
|
|
416
474
|
if (response.status === 400) {
|
|
417
475
|
const standardBodyError = await deserializeJson(response);
|
|
418
476
|
throw new MonoCloudOPError(standardBodyError.error ?? "par_request_failed", standardBodyError.error_description ?? "Pushed Authorization Request Failed");
|
|
@@ -444,7 +502,7 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
444
502
|
const response = await innerFetch(metadata.userinfo_endpoint, {
|
|
445
503
|
method: "GET",
|
|
446
504
|
headers: { authorization: `Bearer ${accessToken}` }
|
|
447
|
-
});
|
|
505
|
+
}, this.fetcher);
|
|
448
506
|
if (response.status === 401) {
|
|
449
507
|
const authenticateError = response.headers.get("WWW-Authenticate");
|
|
450
508
|
if (authenticateError) {
|
|
@@ -518,7 +576,7 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
518
576
|
method: "POST",
|
|
519
577
|
body: body.toString(),
|
|
520
578
|
headers
|
|
521
|
-
});
|
|
579
|
+
}, this.fetcher);
|
|
522
580
|
if (response.status === 400) {
|
|
523
581
|
const standardBodyError = await deserializeJson(response);
|
|
524
582
|
throw new MonoCloudOPError(standardBodyError.error ?? "code_grant_failed", standardBodyError.error_description ?? "Authorization code grant failed");
|
|
@@ -560,7 +618,7 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
560
618
|
method: "POST",
|
|
561
619
|
body: body.toString(),
|
|
562
620
|
headers
|
|
563
|
-
});
|
|
621
|
+
}, this.fetcher);
|
|
564
622
|
if (response.status === 400) {
|
|
565
623
|
const standardBodyError = await deserializeJson(response);
|
|
566
624
|
throw new MonoCloudOPError(standardBodyError.error ?? "refresh_grant_failed", standardBodyError.error_description ?? "Refresh token grant failed");
|
|
@@ -609,10 +667,7 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
609
667
|
delete idTokenClaims[x];
|
|
610
668
|
});
|
|
611
669
|
const session = {
|
|
612
|
-
user:
|
|
613
|
-
...idTokenClaims,
|
|
614
|
-
...userinfo ?? {}
|
|
615
|
-
},
|
|
670
|
+
user: require_utils_internal.profileSync(void 0, idTokenClaims, userinfo, true),
|
|
616
671
|
idToken: tokens.id_token,
|
|
617
672
|
refreshToken: tokens.refresh_token,
|
|
618
673
|
authorizedScopes: requestedScopes,
|
|
@@ -629,7 +684,7 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
629
684
|
}
|
|
630
685
|
/**
|
|
631
686
|
* Refetches user information for an existing session using the userinfo endpoint.
|
|
632
|
-
* Updates the session's user object with the latest user information
|
|
687
|
+
* Updates the session's user object with the latest user information.
|
|
633
688
|
*
|
|
634
689
|
* @param accessToken - Access token used to fetch the userinfo.
|
|
635
690
|
* @param session - The current MonoCloudSession.
|
|
@@ -651,10 +706,8 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
651
706
|
async refetchUserInfo(accessToken, session, options) {
|
|
652
707
|
if (!accessToken.scopes?.includes("openid")) throw new MonoCloudValidationError("Fetching userinfo requires the openid scope");
|
|
653
708
|
const userinfo = await this.userinfo(accessToken.accessToken);
|
|
654
|
-
session.
|
|
655
|
-
|
|
656
|
-
...userinfo
|
|
657
|
-
};
|
|
709
|
+
const idTokenClaims = session.idToken && options?.strictProfileSync ? MonoCloudOidcClient.decodeJwt(session.idToken) : void 0;
|
|
710
|
+
session.user = require_utils_internal.profileSync(session.user, idTokenClaims, userinfo, options?.strictProfileSync);
|
|
658
711
|
await options?.onSessionCreating?.(session, void 0, userinfo);
|
|
659
712
|
return session;
|
|
660
713
|
}
|
|
@@ -692,6 +745,7 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
692
745
|
const jwks = options?.jwks ?? await this.getJwks();
|
|
693
746
|
idTokenClaims = await this.validateIdToken(tokens.id_token, jwks.keys, options?.idTokenClockSkew ?? 0, options?.idTokenClockTolerance ?? 0);
|
|
694
747
|
} else idTokenClaims = MonoCloudOidcClient.decodeJwt(tokens.id_token);
|
|
748
|
+
else if (session.idToken) idTokenClaims = MonoCloudOidcClient.decodeJwt(session.idToken);
|
|
695
749
|
(options?.filteredIdTokenClaims ?? FILTER_ID_TOKEN_CLAIMS).forEach((x) => {
|
|
696
750
|
delete idTokenClaims[x];
|
|
697
751
|
});
|
|
@@ -699,11 +753,7 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
699
753
|
let scopes = options?.refreshGrantOptions?.scopes;
|
|
700
754
|
if (!resource && !scopes) scopes = session.authorizedScopes;
|
|
701
755
|
const accessToken = require_utils_internal.findToken(session.accessTokens, resource, scopes);
|
|
702
|
-
const user =
|
|
703
|
-
...session.user,
|
|
704
|
-
...idTokenClaims,
|
|
705
|
-
...userinfo ?? {}
|
|
706
|
-
};
|
|
756
|
+
const user = require_utils_internal.profileSync(session.user, idTokenClaims, userinfo, options?.strictProfileSync);
|
|
707
757
|
const newTokens = session.accessTokens?.filter((t) => t !== accessToken) ?? [];
|
|
708
758
|
newTokens.push({
|
|
709
759
|
scopes: tokens.scope,
|
|
@@ -752,7 +802,7 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
752
802
|
method: "POST",
|
|
753
803
|
body: body.toString(),
|
|
754
804
|
headers
|
|
755
|
-
});
|
|
805
|
+
}, this.fetcher);
|
|
756
806
|
if (response.status === 400) {
|
|
757
807
|
const standardBodyError = await deserializeJson(response);
|
|
758
808
|
throw new MonoCloudOPError(standardBodyError.error ?? "revocation_failed", standardBodyError.error_description ?? "Token revocation failed");
|
|
@@ -820,29 +870,190 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
820
870
|
if (!(Array.isArray(claims.aud) ? claims.aud : [claims.aud]).includes(this.clientId)) throw new MonoCloudTokenError("Invalid audience claim");
|
|
821
871
|
return claims;
|
|
822
872
|
}
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
//#endregion
|
|
876
|
+
//#region src/monocloud-oidc-backend-client.ts
|
|
877
|
+
/**
|
|
878
|
+
* @category Classes
|
|
879
|
+
*/
|
|
880
|
+
var MonoCloudOidcBackendClient = class extends MonoCloudOidcClientBase {
|
|
823
881
|
/**
|
|
824
|
-
*
|
|
882
|
+
* Creates a new instance of MonoCloudOidcBackendClient.
|
|
825
883
|
*
|
|
826
|
-
*
|
|
884
|
+
* @param tenantDomain - The tenant domain URL.
|
|
885
|
+
* @param audience - The expected audience value used to validate the `aud` claim in access tokens.
|
|
886
|
+
* @param options - Additional client configuration options.
|
|
887
|
+
*/
|
|
888
|
+
constructor(tenantDomain, audience, options) {
|
|
889
|
+
super(tenantDomain, options?.metadataCacheDuration, options?.jwksCacheDuration, options?.fetcher);
|
|
890
|
+
this.clockSkew = 0;
|
|
891
|
+
this.clockTolerance = 300;
|
|
892
|
+
this.audience = audience;
|
|
893
|
+
if (options?.clientId) this.clientId = options.clientId;
|
|
894
|
+
this.clientSecret = options?.clientSecret;
|
|
895
|
+
this.authMethod = options?.clientAuthMethod ?? "client_secret_basic";
|
|
896
|
+
this.groupOptions = options?.groupOptions;
|
|
897
|
+
if (options?.clockSkew !== void 0) this.clockSkew = options.clockSkew;
|
|
898
|
+
if (options?.clockTolerance !== void 0) this.clockTolerance = options.clockTolerance;
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Validates an opaque access token using the OAuth 2.0 Token Introspection endpoint (RFC 7662).
|
|
827
902
|
*
|
|
828
|
-
* @param
|
|
903
|
+
* @param accessToken - The access token string to introspect.
|
|
904
|
+
* @param options - Claims validation options.
|
|
829
905
|
*
|
|
830
|
-
* @returns
|
|
906
|
+
* @returns Validated access token claims (without the `active` field).
|
|
831
907
|
*
|
|
832
|
-
* @throws {@link MonoCloudTokenError} - If
|
|
908
|
+
* @throws {@link MonoCloudTokenError} - If the token is not active or claim validation fails.
|
|
909
|
+
*
|
|
910
|
+
* @throws {@link MonoCloudOPError} - When the introspection endpoint returns a standardized
|
|
911
|
+
* OAuth 2.0 error response.
|
|
912
|
+
*
|
|
913
|
+
* @throws {@link MonoCloudHttpError} - Thrown if there is a network error during the request or
|
|
914
|
+
* unexpected status code during the request or a serialization error while processing the response.
|
|
915
|
+
*
|
|
916
|
+
* @throws {@link MonoCloudValidationError} - When the access token is empty or the introspection
|
|
917
|
+
* endpoint is not available in the issuer metadata or claims validation fails.
|
|
833
918
|
*
|
|
834
919
|
*/
|
|
835
|
-
|
|
920
|
+
async introspectAccessToken(accessToken, options) {
|
|
921
|
+
if (!this.clientId) throw new MonoCloudValidationError("The clientId option must be configured to introspect access tokens");
|
|
922
|
+
if (typeof accessToken !== "string" || accessToken.trim().length === 0) throw new MonoCloudValidationError("Access token must be a valid non-empty string");
|
|
923
|
+
const metadata = await this.getMetadata();
|
|
924
|
+
assertMetadataProperty(metadata, "introspection_endpoint");
|
|
925
|
+
const body = new URLSearchParams();
|
|
926
|
+
body.set("token", accessToken);
|
|
927
|
+
body.set("token_type_hint", "access_token");
|
|
928
|
+
const headers = {
|
|
929
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
930
|
+
accept: "application/json"
|
|
931
|
+
};
|
|
932
|
+
await clientAuth(this.clientId, this.clientSecret, this.authMethod, this.tenantDomain, headers, body, JWT_ASSERTION_CLOCK_SKEW);
|
|
933
|
+
const response = await innerFetch(metadata.introspection_endpoint, {
|
|
934
|
+
method: "POST",
|
|
935
|
+
body: body.toString(),
|
|
936
|
+
headers
|
|
937
|
+
}, this.fetcher);
|
|
938
|
+
if (response.status === 400 || response.status === 401) {
|
|
939
|
+
const standardBodyError = await deserializeJson(response);
|
|
940
|
+
throw new MonoCloudOPError(standardBodyError.error ?? "introspection_failed", standardBodyError.error_description ?? "Token introspection failed");
|
|
941
|
+
}
|
|
942
|
+
if (response.status !== 200) throw new MonoCloudHttpError(`Error while performing token introspection. Unexpected status code: ${response.status}`);
|
|
943
|
+
const introspectionResponse = await deserializeJson(response);
|
|
944
|
+
if (!introspectionResponse.active) throw new MonoCloudTokenError("Token is not active");
|
|
945
|
+
const { active: _, ...claims } = introspectionResponse;
|
|
946
|
+
this.validateAccessTokenClaims(claims, options?.scopes, options?.groups);
|
|
947
|
+
if (options?.validateCertificateBinding) await this.validateCertificateBinding(claims, options.clientCertificate);
|
|
948
|
+
return claims;
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Validates a JWT access token by verifying the signature and claims.
|
|
952
|
+
*
|
|
953
|
+
* @param accessToken - The access token JWT string to validate.
|
|
954
|
+
* @param options - Validation options.
|
|
955
|
+
*
|
|
956
|
+
* @returns Validated access token claims.
|
|
957
|
+
*
|
|
958
|
+
* @throws {@link MonoCloudTokenError} - If JWT parsing, signature verification, or claim validation fails.
|
|
959
|
+
*
|
|
960
|
+
* @throws {@link MonoCloudHttpError} - Thrown if there is a network error during the request or
|
|
961
|
+
* unexpected status code during the request or a serialization error while processing the response.
|
|
962
|
+
*
|
|
963
|
+
* @throws {@link MonoCloudValidationError} - When the access token is empty or claims validation fails.
|
|
964
|
+
*
|
|
965
|
+
*/
|
|
966
|
+
async validateJwtAccessToken(accessToken, options) {
|
|
967
|
+
if (typeof accessToken !== "string" || accessToken.trim().length === 0) throw new MonoCloudValidationError("Access token must be a valid non-empty string");
|
|
968
|
+
const { 0: protectedHeader, 1: payload, 2: encodedSignature, length } = accessToken.split(".");
|
|
969
|
+
if (length !== 3) throw new MonoCloudTokenError("JWT access token must have a header, payload and signature");
|
|
970
|
+
let header;
|
|
836
971
|
try {
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
972
|
+
header = JSON.parse(require_utils_internal.decodeBase64Url(protectedHeader));
|
|
973
|
+
} catch {
|
|
974
|
+
throw new MonoCloudTokenError("Failed to parse JWT Header");
|
|
975
|
+
}
|
|
976
|
+
if (header === null || typeof header !== "object" || Array.isArray(header)) throw new MonoCloudTokenError("JWT Header must be a top level object");
|
|
977
|
+
if (header.crit !== void 0) throw new MonoCloudTokenError("Unexpected JWT \"crit\" header parameter");
|
|
978
|
+
if (header.typ && header.typ !== "at+jwt") throw new MonoCloudTokenError("Invalid token type");
|
|
979
|
+
const binary = require_utils_internal.decodeBase64Url(encodedSignature);
|
|
980
|
+
const signature = new Uint8Array(binary.length);
|
|
981
|
+
for (let i = 0; i < binary.length; i++) signature[i] = binary.charCodeAt(i);
|
|
982
|
+
const key = await require_utils_internal.getPublicSigKeyFromIssuerJwks((options?.jwks ?? await this.getJwks()).keys, header);
|
|
983
|
+
const input = `${protectedHeader}.${payload}`;
|
|
984
|
+
if (!await crypto.subtle.verify(keyToSubtle(key), key, signature, require_utils_internal.stringToArrayBuffer(input))) throw new MonoCloudTokenError("JWT signature verification failed");
|
|
985
|
+
let claims;
|
|
986
|
+
try {
|
|
987
|
+
claims = JSON.parse(require_utils_internal.decodeBase64Url(payload));
|
|
988
|
+
} catch {
|
|
989
|
+
throw new MonoCloudTokenError("Failed to parse JWT Payload");
|
|
990
|
+
}
|
|
991
|
+
if (claims === null || typeof claims !== "object" || Array.isArray(claims)) throw new MonoCloudTokenError("JWT Payload must be a top level object");
|
|
992
|
+
this.validateAccessTokenClaims(claims, options?.scopes, options?.groups);
|
|
993
|
+
if (options?.validateCertificateBinding) await this.validateCertificateBinding(claims, options.clientCertificate);
|
|
994
|
+
return claims;
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Sets clock skew used for access token time-based claim validation.
|
|
998
|
+
*
|
|
999
|
+
* @param clockSkew - Number of seconds to adjust the current time to account for clock differences.
|
|
1000
|
+
*/
|
|
1001
|
+
setClockSkew(clockSkew) {
|
|
1002
|
+
this.clockSkew = clockSkew;
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Sets clock tolerance used for access token time-based claim validation.
|
|
1006
|
+
*
|
|
1007
|
+
* @param clockTolerance - Additional time tolerance in seconds for time-based claim validation.
|
|
1008
|
+
*/
|
|
1009
|
+
setClockTolerance(clockTolerance) {
|
|
1010
|
+
this.clockTolerance = clockTolerance;
|
|
1011
|
+
}
|
|
1012
|
+
validateAccessTokenClaims(claims, scopes, groups) {
|
|
1013
|
+
const current = require_utils_internal.now() + this.clockSkew;
|
|
1014
|
+
if (claims.iss !== this.tenantDomain) throw new MonoCloudTokenError("Invalid Issuer");
|
|
1015
|
+
if (claims.sub && typeof claims.sub !== "string") throw new MonoCloudTokenError("Invalid subject");
|
|
1016
|
+
if (!(Array.isArray(claims.aud) ? claims.aud : [claims.aud]).includes(this.audience)) throw new MonoCloudTokenError("Invalid audience claim");
|
|
1017
|
+
if (claims.exp !== void 0) {
|
|
1018
|
+
if (typeof claims.exp !== "number") throw new MonoCloudTokenError("Unexpected \"exp\" (expiration time) claim type");
|
|
1019
|
+
if (claims.exp <= current - this.clockTolerance) throw new MonoCloudTokenError("Unexpected \"exp\" (expiration time) claim value, timestamp is <= now()");
|
|
1020
|
+
}
|
|
1021
|
+
if (claims.nbf !== void 0) {
|
|
1022
|
+
if (typeof claims.nbf !== "number") throw new MonoCloudTokenError("Unexpected \"nbf\" (not before) claim type");
|
|
1023
|
+
if (claims.nbf > current + this.clockTolerance) throw new MonoCloudTokenError("Unexpected \"nbf\" (not before) claim value, timestamp is > now()");
|
|
1024
|
+
}
|
|
1025
|
+
if (scopes && scopes.length > 0) {
|
|
1026
|
+
const tokenScopes = new Set(require_utils_internal.parseSpaceSeparated(claims.scope));
|
|
1027
|
+
for (const requiredScope of scopes) if (!tokenScopes.has(requiredScope)) throw new MonoCloudTokenError("Token is missing required scopes");
|
|
1028
|
+
}
|
|
1029
|
+
if (groups) {
|
|
1030
|
+
if (!require_utils_index.isUserInGroup(claims, groups, this.groupOptions?.groupsClaim, this.groupOptions?.matchAll)) throw new MonoCloudTokenError("Token is missing required groups");
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
async validateCertificateBinding(accessTokenClaims, certificate) {
|
|
1034
|
+
if (typeof certificate !== "string" || certificate.trim().length === 0) throw new MonoCloudTokenError("Client certificate is not present");
|
|
1035
|
+
const encodedCertificate = (/-----BEGIN CERTIFICATE-----([\s\S]+?)-----END CERTIFICATE-----/.exec(certificate)?.[1] ?? certificate).replace(/\s+/g, "");
|
|
1036
|
+
let certificateBinary;
|
|
1037
|
+
try {
|
|
1038
|
+
certificateBinary = atob(encodedCertificate);
|
|
1039
|
+
} catch {
|
|
1040
|
+
throw new MonoCloudTokenError("Client certificate is malformed");
|
|
1041
|
+
}
|
|
1042
|
+
const certificateBytes = new Uint8Array(certificateBinary.length);
|
|
1043
|
+
for (let i = 0; i < certificateBinary.length; i++) certificateBytes[i] = certificateBinary.charCodeAt(i);
|
|
1044
|
+
const certificateDigest = await crypto.subtle.digest("SHA-256", certificateBytes);
|
|
1045
|
+
const clientCertHash = require_utils_internal.arrayBufferToBase64(new Uint8Array(certificateDigest));
|
|
1046
|
+
let cnfClaimValue = accessTokenClaims.cnf;
|
|
1047
|
+
if (cnfClaimValue === void 0 || cnfClaimValue === null) throw new MonoCloudTokenError("Access token does not contain a 'cnf' (confirmation) claim for certificate binding");
|
|
1048
|
+
if (typeof cnfClaimValue === "string") try {
|
|
1049
|
+
cnfClaimValue = JSON.parse(cnfClaimValue);
|
|
1050
|
+
} catch {
|
|
1051
|
+
throw new MonoCloudTokenError("Malformed 'cnf' claim for certificate binding");
|
|
845
1052
|
}
|
|
1053
|
+
if (cnfClaimValue === null || typeof cnfClaimValue !== "object" || Array.isArray(cnfClaimValue)) throw new MonoCloudTokenError("The 'cnf' claim could not be parsed");
|
|
1054
|
+
const certHash = cnfClaimValue["x5t#S256"];
|
|
1055
|
+
if (typeof certHash !== "string" || certHash.length === 0) throw new MonoCloudTokenError("The 'cnf' claim does not contain an 'x5t#S256' member specifying the certificate hash for binding");
|
|
1056
|
+
if (certHash !== clientCertHash) throw new MonoCloudTokenError("The certificate hash in the access token does not match the presented client certificate (certificate binding validation failed)");
|
|
846
1057
|
}
|
|
847
1058
|
};
|
|
848
1059
|
|
|
@@ -850,7 +1061,9 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
|
|
|
850
1061
|
exports.MonoCloudAuthBaseError = MonoCloudAuthBaseError;
|
|
851
1062
|
exports.MonoCloudHttpError = MonoCloudHttpError;
|
|
852
1063
|
exports.MonoCloudOPError = MonoCloudOPError;
|
|
1064
|
+
exports.MonoCloudOidcBackendClient = MonoCloudOidcBackendClient;
|
|
853
1065
|
exports.MonoCloudOidcClient = MonoCloudOidcClient;
|
|
1066
|
+
exports.MonoCloudOidcClientBase = MonoCloudOidcClientBase;
|
|
854
1067
|
exports.MonoCloudTokenError = MonoCloudTokenError;
|
|
855
1068
|
exports.MonoCloudValidationError = MonoCloudValidationError;
|
|
856
1069
|
//# sourceMappingURL=index.cjs.map
|