@auth0/auth0-spa-js 2.1.3 → 2.2.0

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,5 @@
1
- import { Auth0ClientOptions, RedirectLoginOptions, PopupLoginOptions, PopupConfigOptions, RedirectLoginResult, GetTokenSilentlyOptions, GetTokenWithPopupOptions, LogoutOptions, User, IdToken, GetTokenSilentlyVerboseResponse } from './global';
1
+ import { Auth0ClientOptions, RedirectLoginOptions, PopupLoginOptions, PopupConfigOptions, RedirectLoginResult, GetTokenSilentlyOptions, GetTokenWithPopupOptions, LogoutOptions, User, IdToken, GetTokenSilentlyVerboseResponse, TokenEndpointResponse } from './global';
2
+ import { CustomTokenExchangeOptions } from './TokenExchange';
2
3
  /**
3
4
  * Auth0 SDK for Single Page Applications using [Authorization Code Grant Flow with PKCE](https://auth0.com/docs/api-auth/tutorials/authorization-code-grant-pkce).
4
5
  */
@@ -186,4 +187,43 @@ export declare class Auth0Client {
186
187
  */
187
188
  private _releaseLockOnPageHide;
188
189
  private _requestToken;
190
+ /**
191
+ * Exchanges an external subject token for an Auth0 token via a token exchange request.
192
+ *
193
+ * @param {CustomTokenExchangeOptions} options - The options required to perform the token exchange.
194
+ *
195
+ * @returns {Promise<TokenEndpointResponse>} A promise that resolves to the token endpoint response,
196
+ * which contains the issued Auth0 tokens.
197
+ *
198
+ * This method implements the token exchange grant as specified in RFC 8693 by first validating
199
+ * the provided subject token type and then constructing a token request to the /oauth/token endpoint.
200
+ * The request includes the following parameters:
201
+ *
202
+ * - `grant_type`: Hard-coded to "urn:ietf:params:oauth:grant-type:token-exchange".
203
+ * - `subject_token`: The external token provided via the options.
204
+ * - `subject_token_type`: The type of the external token (validated by this function).
205
+ * - `scope`: A unique set of scopes, generated by merging the scopes supplied in the options
206
+ * with the SDK’s default scopes.
207
+ * - `audience`: The target audience, as determined by the SDK's authorization configuration.
208
+ *
209
+ * **Example Usage:**
210
+ *
211
+ * ```
212
+ * // Define the token exchange options
213
+ * const options: CustomTokenExchangeOptions = {
214
+ * subject_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...',
215
+ * subject_token_type: 'urn:acme:legacy-system-token',
216
+ * scope: ['openid', 'profile']
217
+ * };
218
+ *
219
+ * // Exchange the external token for Auth0 tokens
220
+ * try {
221
+ * const tokenResponse = await instance.exchangeToken(options);
222
+ * console.log('Token response:', tokenResponse);
223
+ * } catch (error) {
224
+ * console.error('Token exchange failed:', error);
225
+ * }
226
+ * ```
227
+ */
228
+ exchangeToken(options: CustomTokenExchangeOptions): Promise<TokenEndpointResponse>;
189
229
  }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Represents the configuration options required for initiating a Custom Token Exchange request
3
+ * following RFC 8693 specifications.
4
+ *
5
+ * @see {@link https://www.rfc-editor.org/rfc/rfc8693 | RFC 8693: OAuth 2.0 Token Exchange}
6
+ */
7
+ export type CustomTokenExchangeOptions = {
8
+ /**
9
+ * The type identifier for the subject token being exchanged
10
+ *
11
+ * @pattern
12
+ * - Must be a namespaced URI under your organization's control
13
+ * - Forbidden patterns:
14
+ * - `^urn:ietf:params:oauth:*` (IETF reserved)
15
+ * - `^https:\/\/auth0\.com/*` (Auth0 reserved)
16
+ * - `^urn:auth0:*` (Auth0 reserved)
17
+ *
18
+ * @example
19
+ * "urn:acme:legacy-system-token"
20
+ * "https://api.yourcompany.com/token-type/v1"
21
+ */
22
+ subject_token_type: string;
23
+ /**
24
+ * The opaque token value being exchanged for Auth0 tokens
25
+ *
26
+ * @security
27
+ * - Must be validated in Auth0 Actions using strong cryptographic verification
28
+ * - Implement replay attack protection
29
+ * - Recommended validation libraries: `jose`, `jsonwebtoken`
30
+ *
31
+ * @example
32
+ * "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
33
+ */
34
+ subject_token: string;
35
+ /**
36
+ * The target audience for the requested Auth0 token
37
+ *
38
+ * @remarks
39
+ * Must match exactly with an API identifier configured in your Auth0 tenant
40
+ *
41
+ * @example
42
+ * "https://api.your-service.com/v1"
43
+ */
44
+ audience: string;
45
+ /**
46
+ * Space-separated list of OAuth 2.0 scopes being requested
47
+ *
48
+ * @remarks
49
+ * Subject to API authorization policies configured in Auth0
50
+ *
51
+ * @example
52
+ * "openid profile email read:data write:data"
53
+ */
54
+ scope?: string;
55
+ /**
56
+ * Additional custom parameters for Auth0 Action processing
57
+ *
58
+ * @remarks
59
+ * Accessible in Action code via `event.request.body`
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * {
64
+ * custom_parameter: "session_context",
65
+ * device_fingerprint: "a3d8f7...",
66
+ * }
67
+ * ```
68
+ */
69
+ [key: string]: unknown;
70
+ };
@@ -1,4 +1,10 @@
1
1
  /**
2
2
  * @ignore
3
3
  */
4
+ /**
5
+ * Returns a string of unique scopes by removing duplicates and unnecessary whitespace.
6
+ *
7
+ * @param {...(string | undefined)[]} scopes - A list of scope strings or undefined values.
8
+ * @returns {string} A string containing unique scopes separated by a single space.
9
+ */
4
10
  export declare const getUniqueScopes: (...scopes: (string | undefined)[]) => string;
@@ -1,2 +1,2 @@
1
- declare const _default: "2.1.3";
1
+ declare const _default: "2.2.0";
2
2
  export default _default;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "@auth0/auth0-spa-js",
4
4
  "description": "Auth0 SDK for Single Page Applications using Authorization Code Grant Flow with PKCE",
5
5
  "license": "MIT",
6
- "version": "2.1.3",
6
+ "version": "2.2.0",
7
7
  "main": "dist/lib/auth0-spa-js.cjs.js",
8
8
  "types": "dist/typings/index.d.ts",
9
9
  "module": "dist/auth0-spa-js.production.esm.js",
@@ -39,7 +39,7 @@
39
39
  "@typescript-eslint/eslint-plugin-tslint": "^5.33.1",
40
40
  "@typescript-eslint/parser": "^5.33.1",
41
41
  "browser-tabs-lock": "^1.2.15",
42
- "browserstack-cypress-cli": "1.27.0",
42
+ "browserstack-cypress-cli": "1.28.0",
43
43
  "cli-table": "^0.3.6",
44
44
  "concurrently": "^7.3.0",
45
45
  "cypress": "13.6.1",
@@ -92,6 +92,7 @@ import {
92
92
  OLD_IS_AUTHENTICATED_COOKIE_NAME,
93
93
  patchOpenUrlWithOnRedirect
94
94
  } from './Auth0Client.utils';
95
+ import { CustomTokenExchangeOptions } from './TokenExchange';
95
96
 
96
97
  /**
97
98
  * @ignore
@@ -1097,7 +1098,10 @@ export class Auth0Client {
1097
1098
  };
1098
1099
 
1099
1100
  private async _requestToken(
1100
- options: PKCERequestTokenOptions | RefreshTokenRequestTokenOptions,
1101
+ options:
1102
+ | PKCERequestTokenOptions
1103
+ | RefreshTokenRequestTokenOptions
1104
+ | TokenExchangeRequestOptions,
1101
1105
  additionalParameters?: RequestTokenAdditionalParameters
1102
1106
  ) {
1103
1107
  const { nonceIn, organization } = additionalParameters || {};
@@ -1137,6 +1141,68 @@ export class Auth0Client {
1137
1141
 
1138
1142
  return { ...authResult, decodedToken };
1139
1143
  }
1144
+
1145
+ /*
1146
+ Custom Token Exchange
1147
+ * **Implementation Notes:**
1148
+ * - Ensure that the `subject_token` provided has been securely obtained and is valid according
1149
+ * to your external identity provider's policies before invoking this function.
1150
+ * - The function leverages internal helper methods:
1151
+ * - `validateTokenType` confirms that the `subject_token_type` is supported.
1152
+ * - `getUniqueScopes` merges and de-duplicates scopes between the provided options and
1153
+ * the instance's default scopes.
1154
+ * - `_requestToken` performs the actual HTTP request to the token endpoint.
1155
+ */
1156
+
1157
+ /**
1158
+ * Exchanges an external subject token for an Auth0 token via a token exchange request.
1159
+ *
1160
+ * @param {CustomTokenExchangeOptions} options - The options required to perform the token exchange.
1161
+ *
1162
+ * @returns {Promise<TokenEndpointResponse>} A promise that resolves to the token endpoint response,
1163
+ * which contains the issued Auth0 tokens.
1164
+ *
1165
+ * This method implements the token exchange grant as specified in RFC 8693 by first validating
1166
+ * the provided subject token type and then constructing a token request to the /oauth/token endpoint.
1167
+ * The request includes the following parameters:
1168
+ *
1169
+ * - `grant_type`: Hard-coded to "urn:ietf:params:oauth:grant-type:token-exchange".
1170
+ * - `subject_token`: The external token provided via the options.
1171
+ * - `subject_token_type`: The type of the external token (validated by this function).
1172
+ * - `scope`: A unique set of scopes, generated by merging the scopes supplied in the options
1173
+ * with the SDK’s default scopes.
1174
+ * - `audience`: The target audience, as determined by the SDK's authorization configuration.
1175
+ *
1176
+ * **Example Usage:**
1177
+ *
1178
+ * ```
1179
+ * // Define the token exchange options
1180
+ * const options: CustomTokenExchangeOptions = {
1181
+ * subject_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...',
1182
+ * subject_token_type: 'urn:acme:legacy-system-token',
1183
+ * scope: ['openid', 'profile']
1184
+ * };
1185
+ *
1186
+ * // Exchange the external token for Auth0 tokens
1187
+ * try {
1188
+ * const tokenResponse = await instance.exchangeToken(options);
1189
+ * console.log('Token response:', tokenResponse);
1190
+ * } catch (error) {
1191
+ * console.error('Token exchange failed:', error);
1192
+ * }
1193
+ * ```
1194
+ */
1195
+ async exchangeToken(
1196
+ options: CustomTokenExchangeOptions
1197
+ ): Promise<TokenEndpointResponse> {
1198
+ return this._requestToken({
1199
+ grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
1200
+ subject_token: options.subject_token,
1201
+ subject_token_type: options.subject_token_type,
1202
+ scope: getUniqueScopes(options.scope, this.scope),
1203
+ audience: this.options.authorizationParams.audience
1204
+ });
1205
+ }
1140
1206
  }
1141
1207
 
1142
1208
  interface BaseRequestTokenOptions {
@@ -1157,6 +1223,14 @@ interface RefreshTokenRequestTokenOptions extends BaseRequestTokenOptions {
1157
1223
  refresh_token?: string;
1158
1224
  }
1159
1225
 
1226
+ interface TokenExchangeRequestOptions extends BaseRequestTokenOptions {
1227
+ grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange';
1228
+ subject_token: string;
1229
+ subject_token_type: string;
1230
+ actor_token?: string;
1231
+ actor_token_type?: string;
1232
+ }
1233
+
1160
1234
  interface RequestTokenAdditionalParameters {
1161
1235
  nonceIn?: string;
1162
1236
  organization?: string;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Represents the configuration options required for initiating a Custom Token Exchange request
3
+ * following RFC 8693 specifications.
4
+ *
5
+ * @see {@link https://www.rfc-editor.org/rfc/rfc8693 | RFC 8693: OAuth 2.0 Token Exchange}
6
+ */
7
+ export type CustomTokenExchangeOptions = {
8
+ /**
9
+ * The type identifier for the subject token being exchanged
10
+ *
11
+ * @pattern
12
+ * - Must be a namespaced URI under your organization's control
13
+ * - Forbidden patterns:
14
+ * - `^urn:ietf:params:oauth:*` (IETF reserved)
15
+ * - `^https:\/\/auth0\.com/*` (Auth0 reserved)
16
+ * - `^urn:auth0:*` (Auth0 reserved)
17
+ *
18
+ * @example
19
+ * "urn:acme:legacy-system-token"
20
+ * "https://api.yourcompany.com/token-type/v1"
21
+ */
22
+ subject_token_type: string;
23
+
24
+ /**
25
+ * The opaque token value being exchanged for Auth0 tokens
26
+ *
27
+ * @security
28
+ * - Must be validated in Auth0 Actions using strong cryptographic verification
29
+ * - Implement replay attack protection
30
+ * - Recommended validation libraries: `jose`, `jsonwebtoken`
31
+ *
32
+ * @example
33
+ * "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
34
+ */
35
+ subject_token: string;
36
+
37
+ /**
38
+ * The target audience for the requested Auth0 token
39
+ *
40
+ * @remarks
41
+ * Must match exactly with an API identifier configured in your Auth0 tenant
42
+ *
43
+ * @example
44
+ * "https://api.your-service.com/v1"
45
+ */
46
+ audience: string;
47
+
48
+ /**
49
+ * Space-separated list of OAuth 2.0 scopes being requested
50
+ *
51
+ * @remarks
52
+ * Subject to API authorization policies configured in Auth0
53
+ *
54
+ * @example
55
+ * "openid profile email read:data write:data"
56
+ */
57
+ scope?: string;
58
+
59
+ /**
60
+ * Additional custom parameters for Auth0 Action processing
61
+ *
62
+ * @remarks
63
+ * Accessible in Action code via `event.request.body`
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * {
68
+ * custom_parameter: "session_context",
69
+ * device_fingerprint: "a3d8f7...",
70
+ * }
71
+ * ```
72
+ */
73
+ [key: string]: unknown;
74
+ };
package/src/scope.ts CHANGED
@@ -6,6 +6,12 @@ const dedupe = (arr: string[]) => Array.from(new Set(arr));
6
6
  /**
7
7
  * @ignore
8
8
  */
9
+ /**
10
+ * Returns a string of unique scopes by removing duplicates and unnecessary whitespace.
11
+ *
12
+ * @param {...(string | undefined)[]} scopes - A list of scope strings or undefined values.
13
+ * @returns {string} A string containing unique scopes separated by a single space.
14
+ */
9
15
  export const getUniqueScopes = (...scopes: (string | undefined)[]) => {
10
16
  return dedupe(scopes.filter(Boolean).join(' ').trim().split(/\s+/)).join(' ');
11
17
  };
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export default '2.1.3';
1
+ export default '2.2.0';