@thunderid/javascript 1.4.0 → 1.4.1

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.
@@ -1,4 +1,6 @@
1
- import * as jose from "jose";
1
+ import * as base64url from "jose/base64url";
2
+ import { importJWK } from "jose/key/import";
3
+ import { jwtVerify } from "jose/jwt/verify";
2
4
 
3
5
  //#region rolldown:runtime
4
6
  var __defProp = Object.defineProperty;
@@ -87,6 +89,16 @@ var IsomorphicCrypto = class {
87
89
  return this.cryptoUtils.base64URLEncode(this.cryptoUtils.generateRandomBytes(32));
88
90
  }
89
91
  /**
92
+ * Generates the random part of an authorization request `state` from 32 random bytes. Only
93
+ * letters and digits are kept, so the value never contains the `_` that separates the parts of
94
+ * the state.
95
+ *
96
+ * @returns A random string of about 42 characters.
97
+ */
98
+ generateState() {
99
+ return this.cryptoUtils.base64URLEncode(this.cryptoUtils.generateRandomBytes(32)).replace(/[^A-Za-z0-9]/g, "");
100
+ }
101
+ /**
90
102
  * Derive code challenge from the code verifier.
91
103
  *
92
104
  * @param verifier - Code verifier.
@@ -2225,7 +2237,10 @@ const OIDCRequestConstants = {
2225
2237
  SIGN_OUT_SUCCESS: "sign_out_success",
2226
2238
  STATE: "state"
2227
2239
  },
2228
- SignIn: { Payload: { DEFAULT_SCOPES: [ScopeConstants_default.OPENID, ScopeConstants_default.PROFILE] } },
2240
+ SignIn: {
2241
+ Payload: { DEFAULT_SCOPES: [ScopeConstants_default.OPENID, ScopeConstants_default.PROFILE] },
2242
+ Storage: { StorageKeys: { STATE: "authorization_state" } }
2243
+ },
2229
2244
  SignOut: { Storage: { StorageKeys: { SIGN_OUT_URL: "sign_out_url" } } }
2230
2245
  };
2231
2246
  var OIDCRequestConstants_default = OIDCRequestConstants;
@@ -2682,11 +2697,11 @@ var DefaultCacheStore = class {
2682
2697
  */
2683
2698
  var DefaultCrypto = class {
2684
2699
  base64URLDecode(value) {
2685
- const decodedArray = jose.base64url.decode(value);
2700
+ const decodedArray = base64url.decode(value);
2686
2701
  return new TextDecoder().decode(decodedArray);
2687
2702
  }
2688
2703
  base64URLEncode(value) {
2689
- return jose.base64url.encode(value);
2704
+ return base64url.encode(value);
2690
2705
  }
2691
2706
  generateRandomBytes(length) {
2692
2707
  return crypto.getRandomValues(new Uint8Array(length));
@@ -2697,8 +2712,7 @@ var DefaultCrypto = class {
2697
2712
  return new Uint8Array(hashBuffer);
2698
2713
  }
2699
2714
  async verifyJwt(idToken, jwk, algorithms, clientId, issuer, subject, clockTolerance, validateJwtIssuer = true) {
2700
- const key = await jose.importJWK(jwk);
2701
- await jose.jwtVerify(idToken, key, {
2715
+ await jwtVerify(idToken, await importJWK(jwk), {
2702
2716
  algorithms,
2703
2717
  audience: [clientId],
2704
2718
  clockTolerance,
@@ -2785,6 +2799,7 @@ var StorageManager = class StorageManager {
2785
2799
  return `${store}-${this.id}`;
2786
2800
  }
2787
2801
  static isLocalStorageAvailable() {
2802
+ if (typeof window === "undefined") return false;
2788
2803
  try {
2789
2804
  const testValue = "__AUTH_SDK_STORAGE_TEST__";
2790
2805
  localStorage.setItem(testValue, testValue);
@@ -3424,7 +3439,7 @@ var AuthenticationHelper = class {
3424
3439
  } else sessionData = await this.storageManager.getSessionData(userId);
3425
3440
  const scope = processOpenIDScopes_default(configData.scopes);
3426
3441
  if (typeof text !== "string") return text;
3427
- return text.replace(TokenExchangeConstants_default.Placeholders.ACCESS_TOKEN, sessionData.access_token).replace(TokenExchangeConstants_default.Placeholders.USERNAME, this.getAuthenticatedUserInfo(sessionData.id_token).username ?? "").replace(TokenExchangeConstants_default.Placeholders.SCOPES, scope).replace(TokenExchangeConstants_default.Placeholders.CLIENT_ID, configData.clientId ?? "").replace(TokenExchangeConstants_default.Placeholders.CLIENT_SECRET, configData.clientSecret ?? "");
3442
+ return text.replace(TokenExchangeConstants_default.Placeholders.ACCESS_TOKEN, () => sessionData?.access_token ?? "").replace(TokenExchangeConstants_default.Placeholders.USERNAME, () => sessionData?.id_token ? this.getAuthenticatedUserInfo(sessionData.id_token).username ?? "" : "").replace(TokenExchangeConstants_default.Placeholders.SCOPES, scope).replace(TokenExchangeConstants_default.Placeholders.CLIENT_ID, configData.clientId ?? "").replace(TokenExchangeConstants_default.Placeholders.CLIENT_SECRET, configData.clientSecret ?? "");
3428
3443
  }
3429
3444
  /**
3430
3445
  * Clears all temporary and session data for the given user.
@@ -3479,7 +3494,7 @@ var AuthenticationHelper_default = AuthenticationHelper;
3479
3494
  /**
3480
3495
  * Encodes a string to standard base64 using `jose` (already a package dependency).
3481
3496
  *
3482
- * `jose.base64url.encode` is environment-agnostic (browser, Node.js, Deno, Bun,
3497
+ * jose's `base64url.encode` is environment-agnostic (browser, Node.js, Deno, Bun,
3483
3498
  * edge/service-worker runtimes). It produces base64url output, which is then
3484
3499
  * converted to standard base64 by restoring the `+`/`/` characters and adding
3485
3500
  * `=` padding.
@@ -3493,7 +3508,7 @@ var AuthenticationHelper_default = AuthenticationHelper;
3493
3508
  * ```
3494
3509
  */
3495
3510
  const base64Encode = (value) => {
3496
- const b64url = jose.base64url.encode(new TextEncoder().encode(value));
3511
+ const b64url = base64url.encode(new TextEncoder().encode(value));
3497
3512
  const rem = b64url.length % 4;
3498
3513
  return (rem === 0 ? b64url : b64url + "=".repeat(4 - rem)).replace(/-/g, "+").replace(/_/g, "/");
3499
3514
  };
@@ -3628,7 +3643,7 @@ var generateStateParamForRequestCorrelation_default = generateStateParamForReque
3628
3643
  * This utility ensures the `openid` scope is always included, handles both string and array forms of the `scope` parameter,
3629
3644
  * and supports PKCE and custom parameters. Throws if a code challenge is provided without a code challenge method.
3630
3645
  *
3631
- * @param options - The main options for the authorization request, including redirectUri, clientId, scope, responseMode, codeChallenge, codeChallengeMethod, and prompt.
3646
+ * @param options - The main options for the authorization request, including redirectUri, clientId, scope, responseMode, codeChallenge, codeChallengeMethod, prompt, and randomState (a value generated from random bytes that makes the `state` unique to this request).
3632
3647
  * @param pkceOptions - PKCE options, including the PKCE key for state correlation.
3633
3648
  * @param customParams - Optional custom parameters to include in the request (excluding the `state` param, which is handled separately).
3634
3649
  * @returns A Map of key-value pairs representing the authorization request URL parameters.
@@ -3673,14 +3688,67 @@ const getAuthorizeRequestUrlParams = (options, pkceOptions, customParams) => {
3673
3688
  const customState = customParams?.[OIDCRequestConstants_default.Params.STATE]?.toString() ?? "";
3674
3689
  let customStateValue = customState;
3675
3690
  if (options.instanceId) customStateValue = AUTH_INSTANCE_PREFIX + options.instanceId + (customState ? `_${customState}` : "");
3691
+ if (options.randomState) customStateValue = customStateValue ? `${customStateValue}_${options.randomState}` : options.randomState;
3676
3692
  authorizeRequestParams.set(OIDCRequestConstants_default.Params.STATE, generateStateParamForRequestCorrelation_default(pkceKey, customStateValue));
3677
3693
  return authorizeRequestParams;
3678
3694
  };
3679
3695
  var getAuthorizeRequestUrlParams_default = getAuthorizeRequestUrlParams;
3680
3696
 
3697
+ //#endregion
3698
+ //#region src/utils/getOAuthErrorMessage.ts
3699
+ /**
3700
+ * Builds a readable message from the body of an OAuth 2.0 error response (RFC 6749 section 5.2).
3701
+ *
3702
+ * Only the `error` and `error_description` members are read, so nothing else in the body, such as
3703
+ * a token, can end up in the message.
3704
+ *
3705
+ * @param body - The parsed response body, or its raw text.
3706
+ * @param fallback - The message to use when the body carries neither member.
3707
+ * @returns `"<error>: <error_description>"`, whichever of the two is present, or `fallback`.
3708
+ *
3709
+ * @example
3710
+ * getOAuthErrorMessage({error: 'invalid_grant', error_description: 'The code has expired.'}, 'Failed');
3711
+ * // Returns: "invalid_grant: The code has expired."
3712
+ */
3713
+ const getOAuthErrorMessage = (body, fallback) => {
3714
+ let parsed = body;
3715
+ if (typeof body === "string") try {
3716
+ parsed = JSON.parse(body);
3717
+ } catch {
3718
+ return fallback;
3719
+ }
3720
+ if (!parsed || typeof parsed !== "object") return fallback;
3721
+ const { error: error$1, error_description: description } = parsed;
3722
+ const parts = [error$1, description].filter((part) => typeof part === "string" && part.trim().length > 0);
3723
+ return parts.length > 0 ? parts.join(": ") : fallback;
3724
+ };
3725
+ var getOAuthErrorMessage_default = getOAuthErrorMessage;
3726
+
3681
3727
  //#endregion
3682
3728
  //#region src/ThunderIDJavaScriptClient.ts
3683
3729
  const WELL_KNOWN_PATH = "/.well-known/openid-configuration";
3730
+ /**
3731
+ * The issuer a discovery document must name: the configured `endpoints.issuer`, else the URL the
3732
+ * well-known path was appended to (OpenID Connect Discovery 1.0, section 4.3).
3733
+ */
3734
+ const getExpectedIssuer = (configuredIssuer, wellKnownOverride, baseUrl) => {
3735
+ if (configuredIssuer) return configuredIssuer;
3736
+ if (wellKnownOverride) return wellKnownOverride.endsWith(WELL_KNOWN_PATH) ? wellKnownOverride.slice(0, -33) : void 0;
3737
+ return baseUrl || void 0;
3738
+ };
3739
+ /**
3740
+ * Compares two issuers exactly, apart from a trailing slash on either side.
3741
+ */
3742
+ const issuersMatch = (discovered, expected) => typeof discovered === "string" && discovered.replace(/\/+$/, "") === expected.replace(/\/+$/, "");
3743
+ /**
3744
+ * Reads a readable message from the body of a failed token, revocation, or custom grant response.
3745
+ */
3746
+ const readErrorMessage = async (response, fallback) => getOAuthErrorMessage_default(await response.json().catch(() => void 0), fallback);
3747
+ /**
3748
+ * The storage key of the `state` sent with the authorization request whose PKCE key is given. Both
3749
+ * share the request index, so the callback finds the state from the index at the end of `state`.
3750
+ */
3751
+ const getStateStorageKey = (pkceKey) => pkceKey.replace(PKCEConstants_default.Storage.StorageKeys.CODE_VERIFIER, OIDCRequestConstants_default.SignIn.Storage.StorageKeys.STATE);
3684
3752
  const REVOKE_ACCESS_TOKEN_REQUEST_TIMEOUT_MS = 1e4;
3685
3753
  const DEFAULT_CONFIG = {
3686
3754
  enablePKCE: true,
@@ -3738,12 +3806,19 @@ var ThunderIDJavaScriptClient = class {
3738
3806
  await this.loadOpenIDProviderConfiguration(true);
3739
3807
  return true;
3740
3808
  }
3741
- getConfiguration() {
3742
- return this.storageManager.getConfigData();
3809
+ async getConfiguration() {
3810
+ return await this.storageManager.getConfigData();
3743
3811
  }
3812
+ /**
3813
+ * Returns the signed-in user, read from the claims of the session's ID token.
3814
+ *
3815
+ * @param userId - The session to read, on a server.
3816
+ * @throws {ThunderIDRuntimeError} With code `JS-AUTH_CORE-GU-NF01` when the session holds no ID token.
3817
+ */
3744
3818
  async getUser(userId) {
3745
3819
  const sessionData = await this.storageManager.getSessionData(userId);
3746
- const authenticatedUser = this.authHelper.getAuthenticatedUserInfo(sessionData?.id_token);
3820
+ if (!sessionData?.id_token) throw new ThunderIDRuntimeError("No signed-in session was found.", "JS-AUTH_CORE-GU-NF01", "javascript");
3821
+ const authenticatedUser = this.authHelper.getAuthenticatedUserInfo(sessionData.id_token);
3747
3822
  Object.keys(authenticatedUser).forEach((key) => {
3748
3823
  if (authenticatedUser[key] === void 0 || authenticatedUser[key] === "" || authenticatedUser[key] === null) delete authenticatedUser[key];
3749
3824
  });
@@ -3812,7 +3887,7 @@ var ThunderIDJavaScriptClient = class {
3812
3887
  };
3813
3888
  if (config.attachToken) requestHeaders = {
3814
3889
  ...requestHeaders,
3815
- Authorization: `Bearer ${(await this.storageManager.getSessionData(sessionId)).access_token}`
3890
+ Authorization: `Bearer ${(await this.storageManager.getSessionData(sessionId))?.access_token}`
3816
3891
  };
3817
3892
  const requestConfig = {
3818
3893
  body: data.join("&"),
@@ -3826,7 +3901,7 @@ var ThunderIDJavaScriptClient = class {
3826
3901
  } catch (error$1) {
3827
3902
  throw new ThunderIDAuthException("JS-AUTH_CORE-RCG-NE02", "The custom grant request failed.", error$1 ?? "The request sent to get the custom grant failed.");
3828
3903
  }
3829
- if (response.status !== 200 || !response.ok) throw new ThunderIDAuthException("JS-AUTH_CORE-RCG-HE03", `Invalid response status received for the custom grant request. (${response.statusText})`, await response.json());
3904
+ if (response.status !== 200 || !response.ok) throw new ThunderIDAuthException("JS-AUTH_CORE-RCG-HE03", `Invalid response status received for the custom grant request. (${response.statusText})`, await readErrorMessage(response, "The custom grant request was rejected."));
3830
3905
  if (config.returnsSession) return this.authHelper.handleTokenResponse(response, sessionId);
3831
3906
  return await response.json();
3832
3907
  }
@@ -3858,7 +3933,8 @@ var ThunderIDJavaScriptClient = class {
3858
3933
  const configData = await this.configProvider();
3859
3934
  if (!forceInit && await this.storageManager.getTemporaryDataParameter(OIDCDiscoveryConstants_default.Storage.StorageKeys.OPENID_PROVIDER_CONFIG_INITIATED)) return;
3860
3935
  const { discovery, baseUrl, endpoints } = configData;
3861
- const resolvedWellKnownEndpoint = endpoints?.wellKnown || (discovery?.wellKnown?.enabled !== false && baseUrl ? `${baseUrl}${WELL_KNOWN_PATH}` : void 0);
3936
+ const wellKnownOverride = endpoints?.wellKnown;
3937
+ const resolvedWellKnownEndpoint = wellKnownOverride || (discovery?.wellKnown?.enabled !== false && baseUrl ? `${baseUrl}${WELL_KNOWN_PATH}` : void 0);
3862
3938
  if (resolvedWellKnownEndpoint) {
3863
3939
  let response;
3864
3940
  try {
@@ -3873,8 +3949,19 @@ var ThunderIDJavaScriptClient = class {
3873
3949
  }
3874
3950
  if (response) {
3875
3951
  let discoveryResolved = false;
3952
+ let discovered;
3953
+ try {
3954
+ discovered = await response.json();
3955
+ } catch {
3956
+ discovered = void 0;
3957
+ }
3958
+ if (discovered && typeof discovered === "object") {
3959
+ const expectedIssuer = getExpectedIssuer(endpoints?.issuer, wellKnownOverride, baseUrl);
3960
+ if (expectedIssuer !== void 0 && !issuersMatch(discovered.issuer, expectedIssuer)) throw new ThunderIDRuntimeError("The discovered issuer does not match the expected issuer. Set endpoints.issuer if your server uses a custom issuer.", "JS-AUTH_CORE-GOPMD-IV03", "javascript");
3961
+ }
3876
3962
  try {
3877
- await this.storageManager.setOIDCProviderMetaData(await this.authHelper.resolveEndpoints(await response.json()));
3963
+ if (!discovered || typeof discovered !== "object") throw new Error("The discovery response is not a JSON object.");
3964
+ await this.storageManager.setOIDCProviderMetaData(await this.authHelper.resolveEndpoints(discovered));
3878
3965
  discoveryResolved = true;
3879
3966
  } catch (error$1) {
3880
3967
  logger_default.warn(`ThunderIDJavaScriptClient: Could not resolve endpoints from the discovery response of ${resolvedWellKnownEndpoint}; falling back to endpoints derived from the base URL.`, error$1);
@@ -3922,10 +4009,12 @@ var ThunderIDJavaScriptClient = class {
3922
4009
  codeChallengeMethod: PKCEConstants_default.DEFAULT_CODE_CHALLENGE_METHOD,
3923
4010
  instanceId: this.getInstanceId().toString(),
3924
4011
  prompt: configData.prompt,
4012
+ randomState: this.cryptoHelper.generateState(),
3925
4013
  redirectUri: configData.afterSignInUrl ?? "",
3926
4014
  responseMode: configData.responseMode,
3927
4015
  scopes: processOpenIDScopes_default(configData.scopes)
3928
4016
  }).filter(([, v]) => v !== void 0)), { key: pkceKey }, authRequestConfig);
4017
+ await this.storageManager.setHybridDataParameter(getStateStorageKey(pkceKey), authorizeRequestParams.get(OIDCRequestConstants_default.Params.STATE) ?? "", userId);
3929
4018
  Array.from(authorizeRequestParams.entries()).forEach(([paramKey, paramValue]) => {
3930
4019
  authorizeRequest.searchParams.append(paramKey, paramValue);
3931
4020
  });
@@ -3936,6 +4025,10 @@ var ThunderIDJavaScriptClient = class {
3936
4025
  }
3937
4026
  async requestAccessToken(authorizationCode, state, userId, tokenRequestConfig) {
3938
4027
  if (!await this.storageManager.getTemporaryDataParameter(OIDCDiscoveryConstants_default.Storage.StorageKeys.OPENID_PROVIDER_CONFIG_INITIATED)) await this.loadOpenIDProviderConfiguration(false);
4028
+ const stateStorageKey = getStateStorageKey(extractPkceStorageKeyFromState_default(state ?? ""));
4029
+ const expectedState = await this.storageManager.getHybridDataParameter(stateStorageKey, userId);
4030
+ if (!state || typeof expectedState !== "string" || !expectedState || state !== expectedState) throw new ThunderIDRuntimeError("The state returned to the callback does not match the state of the authorization request.", "JS-AUTH_CORE-RAT1-IV04", "javascript");
4031
+ await this.storageManager.removeHybridDataParameter(stateStorageKey, userId);
3939
4032
  const tokenEndpoint = (await this.oidcProviderMetaDataProvider()).token_endpoint;
3940
4033
  const configData = await this.configProvider();
3941
4034
  if (!tokenEndpoint || tokenEndpoint.trim().length === 0) throw new ThunderIDAuthException("JS-AUTH_CORE-RAT1-NF01", "Token endpoint not found.", "No token endpoint was found in the OIDC provider meta data returned by the well-known endpoint or the token endpoint passed to the SDK is empty.");
@@ -3970,7 +4063,7 @@ var ThunderIDJavaScriptClient = class {
3970
4063
  } catch (error$1) {
3971
4064
  throw new ThunderIDAuthException("JS-AUTH_CORE-RAT1-NE02", "Requesting access token failed", error$1 ?? "The request to get the access token from the server failed.");
3972
4065
  }
3973
- if (!tokenResponse.ok) throw new ThunderIDAuthException("JS-AUTH_CORE-RAT1-HE03", `Requesting access token failed with ${tokenResponse.statusText}`, await tokenResponse.json());
4066
+ if (!tokenResponse.ok) throw new ThunderIDAuthException("JS-AUTH_CORE-RAT1-HE03", `Requesting access token failed with ${tokenResponse.statusText}`, await readErrorMessage(tokenResponse, "The token request was rejected."));
3974
4067
  return this.authHelper.handleTokenResponse(tokenResponse, userId);
3975
4068
  }
3976
4069
  async getSignOutUrl(userId) {
@@ -3987,7 +4080,11 @@ var ThunderIDJavaScriptClient = class {
3987
4080
  queryParams.set("state", OIDCRequestConstants_default.Params.SIGN_OUT_SUCCESS);
3988
4081
  return `${logoutEndpoint}?${queryParams.toString()}`;
3989
4082
  }
4083
+ /**
4084
+ * Returns the endpoints the SDK calls. Loads the discovery document first when no other call has.
4085
+ */
3990
4086
  async getOpenIDProviderEndpoints() {
4087
+ await this.loadOpenIDProviderConfiguration(false);
3991
4088
  const meta = await this.oidcProviderMetaDataProvider();
3992
4089
  return {
3993
4090
  authorizationEndpoint: meta.authorization_endpoint ?? "",
@@ -4001,16 +4098,27 @@ var ThunderIDJavaScriptClient = class {
4001
4098
  userinfoEndpoint: meta.userinfo_endpoint ?? ""
4002
4099
  };
4003
4100
  }
4101
+ /**
4102
+ * Returns the discovery document. Loads it first when no other call has. Resolves to `null`
4103
+ * before `initialize()`.
4104
+ */
4004
4105
  async getDiscoveryResponse() {
4005
4106
  if (!this.storageManager) return null;
4107
+ await this.loadOpenIDProviderConfiguration(false);
4006
4108
  return this.storageManager.loadOpenIDProviderConfiguration();
4007
4109
  }
4110
+ /**
4111
+ * Returns the claims of the session's ID token, or of `idToken` when the session holds none.
4112
+ *
4113
+ * @throws {ThunderIDRuntimeError} With code `JS-AUTH_CORE-GDIT-NF01` when there is no ID token to decode.
4114
+ */
4008
4115
  async getDecodedIdToken(userId, idToken) {
4009
- const storedIdToken = (await this.storageManager.getSessionData(userId)).id_token;
4010
- return this.cryptoHelper.decodeJwtToken(storedIdToken ?? idToken);
4116
+ const token = (await this.storageManager.getSessionData(userId))?.id_token ?? idToken;
4117
+ if (!token) throw new ThunderIDRuntimeError("No signed-in session was found.", "JS-AUTH_CORE-GDIT-NF01", "javascript");
4118
+ return this.cryptoHelper.decodeJwtToken(token);
4011
4119
  }
4012
4120
  async getIdToken(userId) {
4013
- return (await this.storageManager.getSessionData(userId)).id_token;
4121
+ return (await this.storageManager.getSessionData(userId))?.id_token;
4014
4122
  }
4015
4123
  async getUserSession(userId) {
4016
4124
  return { scopes: (await this.storageManager.getSessionData(userId))?.scope?.split(" ") };
@@ -4041,7 +4149,7 @@ var ThunderIDJavaScriptClient = class {
4041
4149
  } catch (error$1) {
4042
4150
  throw new ThunderIDAuthException("JS-AUTH_CORE-RAT2-NR03", "Refresh access token request failed.", error$1 ?? "The request to refresh the access token failed.");
4043
4151
  }
4044
- if (!tokenResponse.ok) throw new ThunderIDAuthException("JS-AUTH_CORE-RAT2-HE04", `Refreshing access token failed with ${tokenResponse.statusText}`, await tokenResponse.json());
4152
+ if (!tokenResponse.ok) throw new ThunderIDAuthException("JS-AUTH_CORE-RAT2-HE04", `Refreshing access token failed with ${tokenResponse.statusText}`, await readErrorMessage(tokenResponse, "The refresh token request was rejected."));
4045
4153
  return this.authHelper.handleTokenResponse(tokenResponse, userId);
4046
4154
  }
4047
4155
  /**
@@ -4080,7 +4188,7 @@ var ThunderIDJavaScriptClient = class {
4080
4188
  } catch (error$1) {
4081
4189
  throw new ThunderIDAuthException("JS-AUTH_CORE-RCCT-NE02", "Requesting client credentials token failed.", error$1 ?? "The request to get the client credentials access token failed.");
4082
4190
  }
4083
- if (!tokenResponse.ok) throw new ThunderIDAuthException("JS-AUTH_CORE-RCCT-HE03", `Requesting client credentials token failed with ${tokenResponse.statusText}`, await tokenResponse.json());
4191
+ if (!tokenResponse.ok) throw new ThunderIDAuthException("JS-AUTH_CORE-RCCT-HE03", `Requesting client credentials token failed with ${tokenResponse.statusText}`, await readErrorMessage(tokenResponse, "The client credentials token request was rejected."));
4084
4192
  const parsedResponse = await tokenResponse.json();
4085
4193
  parsedResponse.created_at = (/* @__PURE__ */ new Date()).getTime();
4086
4194
  await this.storageManager.setSessionData(parsedResponse, sessionId);
@@ -4124,7 +4232,7 @@ var ThunderIDJavaScriptClient = class {
4124
4232
  } catch (error$1) {
4125
4233
  throw new ThunderIDAuthException("JS-AUTH_CORE-RAT3-NE02", "The request to revoke access token failed.", error$1 ?? "The request sent to revoke the access token failed.");
4126
4234
  }
4127
- if (response.status !== 200 || !response.ok) throw new ThunderIDAuthException("JS-AUTH_CORE-RAT3-HE03", `Invalid response status received for revoke access token request (${response.statusText}).`, await response.json());
4235
+ if (response.status !== 200 || !response.ok) throw new ThunderIDAuthException("JS-AUTH_CORE-RAT3-HE03", `Invalid response status received for revoke access token request (${response.statusText}).`, await readErrorMessage(response, "The revocation request was rejected."));
4128
4236
  return response;
4129
4237
  }
4130
4238
  async revokeAccessToken(userId) {
@@ -4180,7 +4288,7 @@ var ThunderIDJavaScriptClient = class {
4180
4288
  } catch {
4181
4289
  parsedBody = rawBody;
4182
4290
  }
4183
- if (!response.ok) throw new ThunderIDAPIError(parsedBody, "JS-AUTH_CORE-CIBA1-HE03", "javascript", response.status, response.statusText, "CIBA backchannel authentication request failed");
4291
+ if (!response.ok) throw new ThunderIDAPIError(getOAuthErrorMessage_default(parsedBody, "The backchannel authentication request was rejected."), "JS-AUTH_CORE-CIBA1-HE03", "javascript", response.status, response.statusText, "CIBA backchannel authentication request failed");
4184
4292
  const payload = parsedBody;
4185
4293
  if (!payload.auth_req_id) throw new ThunderIDAPIError("The server response did not include an auth_req_id.", "JS-AUTH_CORE-CIBA1-PR04", "javascript");
4186
4294
  return {
@@ -4812,9 +4920,9 @@ var arrayBufferToBase64url_default = arrayBufferToBase64url;
4812
4920
  *
4813
4921
  * @see {@link arrayBufferToBase64url} - The inverse function for encoding ArrayBuffer to base64url
4814
4922
  */
4815
- const base64urlToArrayBuffer = (base64url) => {
4816
- const padding = "=".repeat((4 - base64url.length % 4) % 4);
4817
- const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/") + padding;
4923
+ const base64urlToArrayBuffer = (base64url$1) => {
4924
+ const padding = "=".repeat((4 - base64url$1.length % 4) % 4);
4925
+ const base64 = base64url$1.replace(/-/g, "+").replace(/_/g, "/") + padding;
4818
4926
  const binaryString = atob(base64);
4819
4927
  const bytes = new Uint8Array(binaryString.length);
4820
4928
  for (let i = 0; i < binaryString.length; i += 1) bytes[i] = binaryString.charCodeAt(i);