@auth0/auth0-spa-js 2.7.0 → 2.8.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.
@@ -22,6 +22,7 @@ export declare class Auth0Client {
22
22
  private readonly userCache;
23
23
  private readonly myAccountApi;
24
24
  private worker?;
25
+ private readonly activeLockKeys;
25
26
  private readonly defaultOptions;
26
27
  constructor(options: Auth0ClientOptions);
27
28
  private _url;
@@ -207,7 +208,7 @@ export declare class Auth0Client {
207
208
  private _getIdTokenFromCache;
208
209
  private _getEntryFromCache;
209
210
  /**
210
- * Releases any lock acquired by the current page that's not released yet
211
+ * Releases any locks acquired by the current page that are not released yet
211
212
  *
212
213
  * Get's called on the `pagehide` event.
213
214
  * https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
@@ -1,9 +1,13 @@
1
1
  import { ICache } from './cache';
2
- import { Auth0ClientOptions, AuthorizationParams, AuthorizeOptions, LogoutOptions } from './global';
2
+ import { Auth0ClientOptions, AuthorizationParams, AuthorizeOptions, ClientAuthorizationParams, LogoutOptions } from './global';
3
3
  /**
4
4
  * @ignore
5
5
  */
6
6
  export declare const GET_TOKEN_SILENTLY_LOCK_KEY = "auth0.lock.getTokenSilently";
7
+ /**
8
+ * @ignore
9
+ */
10
+ export declare const buildGetTokenSilentlyLockKey: (clientId: string, audience: string) => string;
7
11
  /**
8
12
  * @ignore
9
13
  */
@@ -24,8 +28,10 @@ export declare const cacheFactory: (location: string) => () => ICache;
24
28
  * @ignore
25
29
  */
26
30
  export declare const getAuthorizeParams: (clientOptions: Auth0ClientOptions & {
27
- authorizationParams: AuthorizationParams;
28
- }, scope: string, authorizationParams: AuthorizationParams, state: string, nonce: string, code_challenge: string, redirect_uri: string | undefined, response_mode: string | undefined, thumbprint: string | undefined) => AuthorizeOptions;
31
+ authorizationParams: ClientAuthorizationParams;
32
+ }, scope: Record<string, string>, authorizationParams: AuthorizationParams & {
33
+ scope?: string | undefined;
34
+ }, state: string, nonce: string, code_challenge: string, redirect_uri: string | undefined, response_mode: string | undefined, thumbprint: string | undefined) => AuthorizeOptions;
29
35
  /**
30
36
  * @ignore
31
37
  *
@@ -10,6 +10,7 @@ export declare class CacheManager {
10
10
  get(cacheKey: CacheKey, expiryAdjustmentSeconds?: number, useMrrt?: boolean, cacheMode?: string): Promise<Partial<CacheEntry> | undefined>;
11
11
  private modifiedCachedEntry;
12
12
  set(entry: CacheEntry): Promise<void>;
13
+ remove(client_id: string, audience?: string, scope?: string): Promise<void>;
13
14
  clear(clientId?: string): Promise<void>;
14
15
  private wrapCacheEntry;
15
16
  private getCacheKeys;
@@ -45,3 +45,4 @@ export declare const DEFAULT_AUTH0_CLIENT: {
45
45
  version: string;
46
46
  };
47
47
  export declare const DEFAULT_NOW_PROVIDER: () => number;
48
+ export declare const DEFAULT_AUDIENCE = "default";
@@ -97,6 +97,9 @@ export interface AuthorizationParams {
97
97
  */
98
98
  [key: string]: any;
99
99
  }
100
+ export interface ClientAuthorizationParams extends Omit<AuthorizationParams, 'scope'> {
101
+ scope?: string | Record<string, string>;
102
+ }
100
103
  interface BaseLoginOptions {
101
104
  /**
102
105
  * URL parameters that will be sent back to the Authorization Server. This can be known parameters
@@ -104,7 +107,7 @@ interface BaseLoginOptions {
104
107
  */
105
108
  authorizationParams?: AuthorizationParams;
106
109
  }
107
- export interface Auth0ClientOptions extends BaseLoginOptions {
110
+ export interface Auth0ClientOptions {
108
111
  /**
109
112
  * Your Auth0 account domain such as `'example.auth0.com'`,
110
113
  * `'example.eu.auth0.com'` or , `'example.mycompany.com'`
@@ -256,6 +259,11 @@ export interface Auth0ClientOptions extends BaseLoginOptions {
256
259
  * The default setting is `false`.
257
260
  */
258
261
  useDpop?: boolean;
262
+ /**
263
+ * URL parameters that will be sent back to the Authorization Server. This can be known parameters
264
+ * defined by Auth0 or custom parameters that you define.
265
+ */
266
+ authorizationParams?: ClientAuthorizationParams;
259
267
  }
260
268
  /**
261
269
  * The possible locations where tokens can be stored
@@ -8,3 +8,28 @@
8
8
  * @returns {string} A string containing unique scopes separated by a single space.
9
9
  */
10
10
  export declare const getUniqueScopes: (...scopes: (string | undefined)[]) => string;
11
+ /**
12
+ * @ignore
13
+ */
14
+ /**
15
+ * We will check if the developer has created the client with a string or object of audience:scopes. We will inject
16
+ * the base scopes to each audience, and store the base ones inside default key. As well, if the developer created the Auth0Client
17
+ * with a string of scopes, we will store the requested ones with the base scopes inside the default key as well.
18
+ * @param authScopes The scopes requested by the user when creating the Auth0Client
19
+ * @param openIdScope openId scope
20
+ * @param extraScopes Other scopes to accumulate such as offline_access
21
+ * @returns {Record<string, string>} An object with all scopes that are going to be accumulated.
22
+ */
23
+ export declare const injectDefaultScopes: (authScopes: string | Record<string, string> | undefined, openIdScope: string, ...extraScopes: string[]) => Record<string, string>;
24
+ /**
25
+ * @ignore
26
+ */
27
+ /**
28
+ * Will return a string of scopes. If a specific audience was requested and it exist inside the scopes object, we will return those
29
+ * related to that audience that we want to accumulate. If not, we will return the ones stored inside the default key.
30
+ * @param authScopes Object of audience:scopes that are going to be accumulated
31
+ * @param methodScopes The scopes requested for the developer in a specific request
32
+ * @param audience The audience the developer requested for an specific request or the one they configured in the Auth0Client
33
+ * @returns {string} A combination of Auth0Client scopes and the ones requested by the developer for a specific request
34
+ */
35
+ export declare const scopesToRequest: (authScopes: Record<string, string>, methodScopes: string | undefined, audience: string | undefined) => string;
@@ -7,6 +7,12 @@ export declare const getCrypto: () => Crypto;
7
7
  export declare const createRandomString: () => string;
8
8
  export declare const encode: (value: string) => string;
9
9
  export declare const decode: (value: string) => string;
10
+ /**
11
+ * Strips any property that is not present in ALLOWED_AUTH0CLIENT_PROPERTIES
12
+ * @param auth0Client - The full auth0Client object
13
+ * @returns The stripped auth0Client object
14
+ */
15
+ export declare const stripAuth0Client: (auth0Client: any) => any;
10
16
  export declare const createQueryParams: ({ clientId: client_id, ...params }: any) => string;
11
17
  export declare const sha256: (s: string) => Promise<any>;
12
18
  export declare const urlDecodeB64: (input: string) => string;
@@ -1,2 +1,2 @@
1
- declare const _default: "2.7.0";
1
+ declare const _default: "2.8.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.7.0",
6
+ "version": "2.8.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",
@@ -18,7 +18,7 @@ import {
18
18
 
19
19
  import { oauthToken } from './api';
20
20
 
21
- import { getUniqueScopes } from './scope';
21
+ import { injectDefaultScopes, scopesToRequest } from './scope';
22
22
 
23
23
  import {
24
24
  InMemoryCache,
@@ -59,7 +59,8 @@ import {
59
59
  DEFAULT_AUTH0_CLIENT,
60
60
  INVALID_REFRESH_TOKEN_ERROR_MESSAGE,
61
61
  DEFAULT_NOW_PROVIDER,
62
- DEFAULT_FETCH_TIMEOUT_MS
62
+ DEFAULT_FETCH_TIMEOUT_MS,
63
+ DEFAULT_AUDIENCE
63
64
  } from './constants';
64
65
 
65
66
  import {
@@ -82,7 +83,8 @@ import {
82
83
  AuthenticationResult,
83
84
  ConnectAccountRedirectResult,
84
85
  RedirectConnectAccountOptions,
85
- ResponseType
86
+ ResponseType,
87
+ ClientAuthorizationParams,
86
88
  } from './global';
87
89
 
88
90
  // @ts-ignore
@@ -94,7 +96,7 @@ import {
94
96
  buildOrganizationHintCookieName,
95
97
  cacheFactory,
96
98
  getAuthorizeParams,
97
- GET_TOKEN_SILENTLY_LOCK_KEY,
99
+ buildGetTokenSilentlyLockKey,
98
100
  OLD_IS_AUTHENTICATED_COOKIE_NAME,
99
101
  patchOpenUrlWithOnRedirect,
100
102
  getScopeToRequest,
@@ -134,7 +136,7 @@ export class Auth0Client {
134
136
  private readonly cacheManager: CacheManager;
135
137
  private readonly domainUrl: string;
136
138
  private readonly tokenIssuer: string;
137
- private readonly scope: string;
139
+ private readonly scope: Record<string, string>;
138
140
  private readonly cookieStorage: ClientStorage;
139
141
  private readonly dpop: Dpop | undefined;
140
142
  private readonly sessionCheckExpiryDays: number;
@@ -143,12 +145,14 @@ export class Auth0Client {
143
145
  private readonly nowProvider: () => number | Promise<number>;
144
146
  private readonly httpTimeoutMs: number;
145
147
  private readonly options: Auth0ClientOptions & {
146
- authorizationParams: AuthorizationParams;
148
+ authorizationParams: ClientAuthorizationParams,
147
149
  };
148
150
  private readonly userCache: ICache = new InMemoryCache().enclosedCache;
149
151
  private readonly myAccountApi: MyAccountApiClient;
150
152
 
151
153
  private worker?: Worker;
154
+ private readonly activeLockKeys: Set<string> = new Set();
155
+
152
156
  private readonly defaultOptions: Partial<Auth0ClientOptions> = {
153
157
  authorizationParams: {
154
158
  scope: DEFAULT_SCOPE
@@ -218,9 +222,9 @@ export class Auth0Client {
218
222
  // 1. Always include `openid`
219
223
  // 2. Include the scopes provided in `authorizationParams. This defaults to `profile email`
220
224
  // 3. Add `offline_access` if `useRefreshTokens` is enabled
221
- this.scope = getUniqueScopes(
222
- 'openid',
225
+ this.scope = injectDefaultScopes(
223
226
  this.options.authorizationParams.scope,
227
+ 'openid',
224
228
  this.options.useRefreshTokens ? 'offline_access' : ''
225
229
  );
226
230
 
@@ -362,7 +366,7 @@ export class Auth0Client {
362
366
  nonce,
363
367
  code_verifier,
364
368
  scope: params.scope,
365
- audience: params.audience || 'default',
369
+ audience: params.audience || DEFAULT_AUDIENCE,
366
370
  redirect_uri: params.redirect_uri,
367
371
  state,
368
372
  url
@@ -782,7 +786,11 @@ export class Auth0Client {
782
786
  authorizationParams: {
783
787
  ...this.options.authorizationParams,
784
788
  ...options.authorizationParams,
785
- scope: getUniqueScopes(this.scope, options.authorizationParams?.scope)
789
+ scope: scopesToRequest(
790
+ this.scope,
791
+ options.authorizationParams?.scope,
792
+ options.authorizationParams?.audience || this.options.authorizationParams.audience,
793
+ )
786
794
  }
787
795
  };
788
796
 
@@ -806,7 +814,7 @@ export class Auth0Client {
806
814
  if (cacheMode !== 'off') {
807
815
  const entry = await this._getEntryFromCache({
808
816
  scope: getTokenOptions.authorizationParams.scope,
809
- audience: getTokenOptions.authorizationParams.audience || 'default',
817
+ audience: getTokenOptions.authorizationParams.audience || DEFAULT_AUDIENCE,
810
818
  clientId: this.options.clientId,
811
819
  cacheMode,
812
820
  });
@@ -820,21 +828,26 @@ export class Auth0Client {
820
828
  return;
821
829
  }
822
830
 
823
- if (
824
- await retryPromise(
825
- () => lock.acquireLock(GET_TOKEN_SILENTLY_LOCK_KEY, 5000),
826
- 10
827
- )
828
- ) {
829
- try {
830
- window.addEventListener('pagehide', this._releaseLockOnPageHide);
831
+ // Generate lock key based on client ID and audience for better isolation
832
+ const lockKey = buildGetTokenSilentlyLockKey(
833
+ this.options.clientId,
834
+ getTokenOptions.authorizationParams.audience || 'default'
835
+ );
831
836
 
837
+ if (await retryPromise(() => lock.acquireLock(lockKey, 5000), 10)) {
838
+ this.activeLockKeys.add(lockKey);
839
+
840
+ // Add event listener only if this is the first active lock
841
+ if (this.activeLockKeys.size === 1) {
842
+ window.addEventListener('pagehide', this._releaseLockOnPageHide);
843
+ }
844
+ try {
832
845
  // Check the cache a second time, because it may have been populated
833
846
  // by a previous call while this call was waiting to acquire the lock.
834
847
  if (cacheMode !== 'off') {
835
848
  const entry = await this._getEntryFromCache({
836
849
  scope: getTokenOptions.authorizationParams.scope,
837
- audience: getTokenOptions.authorizationParams.audience || 'default',
850
+ audience: getTokenOptions.authorizationParams.audience || DEFAULT_AUDIENCE,
838
851
  clientId: this.options.clientId
839
852
  });
840
853
 
@@ -863,8 +876,12 @@ export class Auth0Client {
863
876
  expires_in
864
877
  };
865
878
  } finally {
866
- await lock.releaseLock(GET_TOKEN_SILENTLY_LOCK_KEY);
867
- window.removeEventListener('pagehide', this._releaseLockOnPageHide);
879
+ await lock.releaseLock(lockKey);
880
+ this.activeLockKeys.delete(lockKey);
881
+ // If we have no more locks, we can remove the event listener to clean up
882
+ if (this.activeLockKeys.size === 0) {
883
+ window.removeEventListener('pagehide', this._releaseLockOnPageHide);
884
+ }
868
885
  }
869
886
  } else {
870
887
  throw new TimeoutError();
@@ -892,7 +909,11 @@ export class Auth0Client {
892
909
  authorizationParams: {
893
910
  ...this.options.authorizationParams,
894
911
  ...options.authorizationParams,
895
- scope: getUniqueScopes(this.scope, options.authorizationParams?.scope)
912
+ scope: scopesToRequest(
913
+ this.scope,
914
+ options.authorizationParams?.scope,
915
+ options.authorizationParams?.audience || this.options.authorizationParams.audience
916
+ )
896
917
  }
897
918
  };
898
919
 
@@ -906,7 +927,7 @@ export class Auth0Client {
906
927
  const cache = await this.cacheManager.get(
907
928
  new CacheKey({
908
929
  scope: localOptions.authorizationParams.scope,
909
- audience: localOptions.authorizationParams.audience || 'default',
930
+ audience: localOptions.authorizationParams.audience || DEFAULT_AUDIENCE,
910
931
  clientId: this.options.clientId
911
932
  }),
912
933
  undefined,
@@ -1095,7 +1116,7 @@ export class Auth0Client {
1095
1116
  const cache = await this.cacheManager.get(
1096
1117
  new CacheKey({
1097
1118
  scope: options.authorizationParams.scope,
1098
- audience: options.authorizationParams.audience || 'default',
1119
+ audience: options.authorizationParams.audience || DEFAULT_AUDIENCE,
1099
1120
  clientId: this.options.clientId
1100
1121
  }),
1101
1122
  undefined,
@@ -1112,7 +1133,7 @@ export class Auth0Client {
1112
1133
  }
1113
1134
 
1114
1135
  throw new MissingRefreshTokenError(
1115
- options.authorizationParams.audience || 'default',
1136
+ options.authorizationParams.audience || DEFAULT_AUDIENCE,
1116
1137
  options.authorizationParams.scope
1117
1138
  );
1118
1139
  }
@@ -1179,6 +1200,14 @@ export class Auth0Client {
1179
1200
  return await this._getTokenFromIFrame(options);
1180
1201
  }
1181
1202
 
1203
+ // Before throwing MissingScopesError, we have to remove the previously created entry
1204
+ // to avoid storing wrong data
1205
+ await this.cacheManager.remove(
1206
+ this.options.clientId,
1207
+ options.authorizationParams.audience,
1208
+ options.authorizationParams.scope,
1209
+ );
1210
+
1182
1211
  const missingScopes = getMissingScopes(
1183
1212
  scopesToRequest,
1184
1213
  tokenResult.scope,
@@ -1196,7 +1225,7 @@ export class Auth0Client {
1196
1225
  ...tokenResult,
1197
1226
  scope: options.authorizationParams.scope,
1198
1227
  oauthTokenScope: tokenResult.scope,
1199
- audience: options.authorizationParams.audience || 'default'
1228
+ audience: options.authorizationParams.audience || DEFAULT_AUDIENCE
1200
1229
  };
1201
1230
  } catch (e) {
1202
1231
  if (
@@ -1236,13 +1265,14 @@ export class Auth0Client {
1236
1265
  }
1237
1266
 
1238
1267
  private async _getIdTokenFromCache() {
1239
- const audience = this.options.authorizationParams.audience || 'default';
1268
+ const audience = this.options.authorizationParams.audience || DEFAULT_AUDIENCE;
1269
+ const scope = this.scope[audience];
1240
1270
 
1241
1271
  const cache = await this.cacheManager.getIdToken(
1242
1272
  new CacheKey({
1243
1273
  clientId: this.options.clientId,
1244
1274
  audience,
1245
- scope: this.scope
1275
+ scope,
1246
1276
  })
1247
1277
  );
1248
1278
 
@@ -1299,13 +1329,18 @@ export class Auth0Client {
1299
1329
  }
1300
1330
 
1301
1331
  /**
1302
- * Releases any lock acquired by the current page that's not released yet
1332
+ * Releases any locks acquired by the current page that are not released yet
1303
1333
  *
1304
1334
  * Get's called on the `pagehide` event.
1305
1335
  * https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
1306
1336
  */
1307
1337
  private _releaseLockOnPageHide = async () => {
1308
- await lock.releaseLock(GET_TOKEN_SILENTLY_LOCK_KEY);
1338
+ // Release all active locks
1339
+ const lockKeysToRelease = Array.from(this.activeLockKeys);
1340
+ for (const lockKey of lockKeysToRelease) {
1341
+ await lock.releaseLock(lockKey);
1342
+ }
1343
+ this.activeLockKeys.clear();
1309
1344
 
1310
1345
  window.removeEventListener('pagehide', this._releaseLockOnPageHide);
1311
1346
  };
@@ -1343,7 +1378,7 @@ export class Auth0Client {
1343
1378
  ...authResult,
1344
1379
  decodedToken,
1345
1380
  scope: options.scope,
1346
- audience: options.audience || 'default',
1381
+ audience: options.audience || DEFAULT_AUDIENCE,
1347
1382
  ...(authResult.scope ? { oauthTokenScope: authResult.scope } : null),
1348
1383
  client_id: this.options.clientId
1349
1384
  });
@@ -1415,7 +1450,11 @@ export class Auth0Client {
1415
1450
  grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
1416
1451
  subject_token: options.subject_token,
1417
1452
  subject_token_type: options.subject_token_type,
1418
- scope: getUniqueScopes(options.scope, this.scope),
1453
+ scope: scopesToRequest(
1454
+ this.scope,
1455
+ options.scope,
1456
+ options.audience || this.options.authorizationParams.audience
1457
+ ),
1419
1458
  audience: options.audience || this.options.authorizationParams.audience
1420
1459
  });
1421
1460
  }
@@ -3,15 +3,24 @@ import {
3
3
  Auth0ClientOptions,
4
4
  AuthorizationParams,
5
5
  AuthorizeOptions,
6
+ ClientAuthorizationParams,
6
7
  LogoutOptions
7
8
  } from './global';
8
- import { getUniqueScopes } from './scope';
9
+ import { scopesToRequest } from './scope';
9
10
 
10
11
  /**
11
12
  * @ignore
12
13
  */
13
14
  export const GET_TOKEN_SILENTLY_LOCK_KEY = 'auth0.lock.getTokenSilently';
14
15
 
16
+ /**
17
+ * @ignore
18
+ */
19
+ export const buildGetTokenSilentlyLockKey = (
20
+ clientId: string,
21
+ audience: string
22
+ ) => `${GET_TOKEN_SILENTLY_LOCK_KEY}.${clientId}.${audience}`;
23
+
15
24
  /**
16
25
  * @ignore
17
26
  */
@@ -49,10 +58,10 @@ export const cacheFactory = (location: string) => {
49
58
  */
50
59
  export const getAuthorizeParams = (
51
60
  clientOptions: Auth0ClientOptions & {
52
- authorizationParams: AuthorizationParams;
61
+ authorizationParams: ClientAuthorizationParams;
53
62
  },
54
- scope: string,
55
- authorizationParams: AuthorizationParams,
63
+ scope: Record<string, string>,
64
+ authorizationParams: AuthorizationParams & { scope?: string },
56
65
  state: string,
57
66
  nonce: string,
58
67
  code_challenge: string,
@@ -64,7 +73,7 @@ export const getAuthorizeParams = (
64
73
  client_id: clientOptions.clientId,
65
74
  ...clientOptions.authorizationParams,
66
75
  ...authorizationParams,
67
- scope: getUniqueScopes(scope, authorizationParams.scope),
76
+ scope: scopesToRequest(scope, authorizationParams.scope, authorizationParams.audience),
68
77
  response_type: 'code',
69
78
  response_mode: response_mode || 'query',
70
79
  state,
package/src/api.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { TokenEndpointOptions, TokenEndpointResponse } from './global';
2
- import { DEFAULT_AUTH0_CLIENT } from './constants';
2
+ import { DEFAULT_AUTH0_CLIENT, DEFAULT_AUDIENCE } from './constants';
3
3
  import * as dpopUtils from './dpop/utils';
4
4
  import { getJSON } from './http';
5
- import { createQueryParams } from './utils';
5
+ import { createQueryParams, stripAuth0Client } from './utils';
6
6
 
7
7
  export async function oauthToken(
8
8
  {
@@ -21,8 +21,7 @@ export async function oauthToken(
21
21
  const isTokenExchange =
22
22
  options.grant_type === 'urn:ietf:params:oauth:grant-type:token-exchange';
23
23
 
24
- const refreshWithMrrt =
25
- options.grant_type === 'refresh_token' && useMrrt;
24
+ const refreshWithMrrt = options.grant_type === 'refresh_token' && useMrrt;
26
25
 
27
26
  const allParams = {
28
27
  ...options,
@@ -40,7 +39,7 @@ export async function oauthToken(
40
39
  return await getJSON<TokenEndpointResponse>(
41
40
  `${baseUrl}/oauth/token`,
42
41
  timeout,
43
- audience || 'default',
42
+ audience || DEFAULT_AUDIENCE,
44
43
  scope,
45
44
  {
46
45
  method: 'POST',
@@ -50,7 +49,7 @@ export async function oauthToken(
50
49
  ? 'application/x-www-form-urlencoded'
51
50
  : 'application/json',
52
51
  'Auth0-Client': btoa(
53
- JSON.stringify(auth0Client || DEFAULT_AUTH0_CLIENT)
52
+ JSON.stringify(stripAuth0Client(auth0Client || DEFAULT_AUTH0_CLIENT))
54
53
  )
55
54
  }
56
55
  },
@@ -149,6 +149,20 @@ export class CacheManager {
149
149
  await this.keyManifest?.add(cacheKey.toKey());
150
150
  }
151
151
 
152
+ async remove(
153
+ client_id: string,
154
+ audience?: string,
155
+ scope?: string,
156
+ ): Promise<void> {
157
+ const cacheKey = new CacheKey({
158
+ clientId: client_id,
159
+ scope: scope,
160
+ audience: audience
161
+ });
162
+
163
+ await this.cache.remove(cacheKey.toKey());
164
+ }
165
+
152
166
  async clear(clientId?: string): Promise<void> {
153
167
  const keys = await this.getCacheKeys();
154
168
 
package/src/constants.ts CHANGED
@@ -60,3 +60,5 @@ export const DEFAULT_AUTH0_CLIENT = {
60
60
  };
61
61
 
62
62
  export const DEFAULT_NOW_PROVIDER = () => Date.now();
63
+
64
+ export const DEFAULT_AUDIENCE = 'default';
package/src/global.ts CHANGED
@@ -113,6 +113,10 @@ export interface AuthorizationParams {
113
113
  [key: string]: any;
114
114
  }
115
115
 
116
+ export interface ClientAuthorizationParams extends Omit<AuthorizationParams, 'scope'> {
117
+ scope?: string | Record<string, string>
118
+ };
119
+
116
120
  interface BaseLoginOptions {
117
121
  /**
118
122
  * URL parameters that will be sent back to the Authorization Server. This can be known parameters
@@ -121,7 +125,7 @@ interface BaseLoginOptions {
121
125
  authorizationParams?: AuthorizationParams;
122
126
  }
123
127
 
124
- export interface Auth0ClientOptions extends BaseLoginOptions {
128
+ export interface Auth0ClientOptions {
125
129
  /**
126
130
  * Your Auth0 account domain such as `'example.auth0.com'`,
127
131
  * `'example.eu.auth0.com'` or , `'example.mycompany.com'`
@@ -288,6 +292,12 @@ export interface Auth0ClientOptions extends BaseLoginOptions {
288
292
  * The default setting is `false`.
289
293
  */
290
294
  useDpop?: boolean;
295
+
296
+ /**
297
+ * URL parameters that will be sent back to the Authorization Server. This can be known parameters
298
+ * defined by Auth0 or custom parameters that you define.
299
+ */
300
+ authorizationParams?: ClientAuthorizationParams;
291
301
  }
292
302
 
293
303
  /**
package/src/scope.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { DEFAULT_AUDIENCE } from "./constants";
2
+
1
3
  /**
2
4
  * @ignore
3
5
  */
@@ -15,3 +17,58 @@ const dedupe = (arr: string[]) => Array.from(new Set(arr));
15
17
  export const getUniqueScopes = (...scopes: (string | undefined)[]) => {
16
18
  return dedupe(scopes.filter(Boolean).join(' ').trim().split(/\s+/)).join(' ');
17
19
  };
20
+
21
+ /**
22
+ * @ignore
23
+ */
24
+ /**
25
+ * We will check if the developer has created the client with a string or object of audience:scopes. We will inject
26
+ * the base scopes to each audience, and store the base ones inside default key. As well, if the developer created the Auth0Client
27
+ * with a string of scopes, we will store the requested ones with the base scopes inside the default key as well.
28
+ * @param authScopes The scopes requested by the user when creating the Auth0Client
29
+ * @param openIdScope openId scope
30
+ * @param extraScopes Other scopes to accumulate such as offline_access
31
+ * @returns {Record<string, string>} An object with all scopes that are going to be accumulated.
32
+ */
33
+ export const injectDefaultScopes = (authScopes: string | Record<string, string> | undefined, openIdScope: string, ...extraScopes: string[]): Record<string, string> => {
34
+ if (typeof authScopes !== 'object') {
35
+ return { [DEFAULT_AUDIENCE]: getUniqueScopes(openIdScope, authScopes, ...extraScopes) };
36
+ }
37
+
38
+ let requestedScopes: Record<string, string> = {
39
+ [DEFAULT_AUDIENCE]: getUniqueScopes(openIdScope, ...extraScopes),
40
+ };
41
+
42
+ Object.keys(authScopes).forEach((key) => {
43
+ const audienceScopes = authScopes[key];
44
+
45
+ requestedScopes[key] = getUniqueScopes(openIdScope, audienceScopes, ...extraScopes);
46
+ });
47
+
48
+ return requestedScopes;
49
+ }
50
+
51
+ /**
52
+ * @ignore
53
+ */
54
+ /**
55
+ * Will return a string of scopes. If a specific audience was requested and it exist inside the scopes object, we will return those
56
+ * related to that audience that we want to accumulate. If not, we will return the ones stored inside the default key.
57
+ * @param authScopes Object of audience:scopes that are going to be accumulated
58
+ * @param methodScopes The scopes requested for the developer in a specific request
59
+ * @param audience The audience the developer requested for an specific request or the one they configured in the Auth0Client
60
+ * @returns {string} A combination of Auth0Client scopes and the ones requested by the developer for a specific request
61
+ */
62
+ export const scopesToRequest = (authScopes: Record<string, string>, methodScopes: string | undefined, audience: string | undefined): string => {
63
+ let scope: string | undefined;
64
+
65
+ if (audience) {
66
+ scope = authScopes[audience];
67
+ }
68
+
69
+ if (!scope) {
70
+ scope = authScopes[DEFAULT_AUDIENCE];
71
+ }
72
+
73
+ return getUniqueScopes(scope, methodScopes);
74
+ }