@monocloud/auth-core 0.1.7 → 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 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
- this.jwksCacheExpiry = 0;
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");
@@ -744,7 +802,7 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
744
802
  method: "POST",
745
803
  body: body.toString(),
746
804
  headers
747
- });
805
+ }, this.fetcher);
748
806
  if (response.status === 400) {
749
807
  const standardBodyError = await deserializeJson(response);
750
808
  throw new MonoCloudOPError(standardBodyError.error ?? "revocation_failed", standardBodyError.error_description ?? "Token revocation failed");
@@ -812,29 +870,190 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
812
870
  if (!(Array.isArray(claims.aud) ? claims.aud : [claims.aud]).includes(this.clientId)) throw new MonoCloudTokenError("Invalid audience claim");
813
871
  return claims;
814
872
  }
873
+ };
874
+
875
+ //#endregion
876
+ //#region src/monocloud-oidc-backend-client.ts
877
+ /**
878
+ * @category Classes
879
+ */
880
+ var MonoCloudOidcBackendClient = class extends MonoCloudOidcClientBase {
815
881
  /**
816
- * Decodes the payload of a JSON Web Token (JWT) and returns it as an object.
882
+ * Creates a new instance of MonoCloudOidcBackendClient.
817
883
  *
818
- * >Note: THIS METHOD DOES NOT VERIFY JWT TOKENS.
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).
819
902
  *
820
- * @param jwt - JWT to decode.
903
+ * @param accessToken - The access token string to introspect.
904
+ * @param options - Claims validation options.
821
905
  *
822
- * @returns Decoded payload.
906
+ * @returns Validated access token claims (without the `active` field).
823
907
  *
824
- * @throws {@link MonoCloudTokenError} - If decoding fails
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.
825
918
  *
826
919
  */
827
- static decodeJwt(jwt) {
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;
828
971
  try {
829
- const [, payload] = jwt.split(".");
830
- if (!payload?.trim()) throw new MonoCloudTokenError("JWT does not contain payload");
831
- const decoded = require_utils_internal.decodeBase64Url(payload);
832
- if (!decoded.startsWith("{")) throw new MonoCloudTokenError("Payload is not an object");
833
- return JSON.parse(decoded);
834
- } catch (e) {
835
- if (e instanceof MonoCloudAuthBaseError) throw e;
836
- throw new MonoCloudTokenError("Could not parse payload. Malformed payload");
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");
837
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)");
838
1057
  }
839
1058
  };
840
1059
 
@@ -842,7 +1061,9 @@ var MonoCloudOidcClient = class MonoCloudOidcClient {
842
1061
  exports.MonoCloudAuthBaseError = MonoCloudAuthBaseError;
843
1062
  exports.MonoCloudHttpError = MonoCloudHttpError;
844
1063
  exports.MonoCloudOPError = MonoCloudOPError;
1064
+ exports.MonoCloudOidcBackendClient = MonoCloudOidcBackendClient;
845
1065
  exports.MonoCloudOidcClient = MonoCloudOidcClient;
1066
+ exports.MonoCloudOidcClientBase = MonoCloudOidcClientBase;
846
1067
  exports.MonoCloudTokenError = MonoCloudTokenError;
847
1068
  exports.MonoCloudValidationError = MonoCloudValidationError;
848
1069
  //# sourceMappingURL=index.cjs.map