@auth0/auth0-spa-js 1.22.3 → 2.0.0-beta.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.
- package/README.md +50 -41
- package/dist/auth0-spa-js.development.js +996 -4901
- package/dist/auth0-spa-js.development.js.map +1 -1
- package/dist/auth0-spa-js.production.esm.js +1 -1
- package/dist/auth0-spa-js.production.esm.js.map +1 -1
- package/dist/auth0-spa-js.production.js +1 -1
- package/dist/auth0-spa-js.production.js.map +1 -1
- package/dist/lib/auth0-spa-js.cjs.js +1768 -5532
- package/dist/lib/auth0-spa-js.cjs.js.map +1 -1
- package/dist/typings/Auth0Client.d.ts +21 -41
- package/dist/typings/Auth0Client.utils.d.ts +26 -0
- package/dist/typings/cache/cache-manager.d.ts +11 -7
- package/dist/typings/cache/shared.d.ts +16 -11
- package/dist/typings/constants.d.ts +0 -7
- package/dist/typings/errors.d.ts +0 -4
- package/dist/typings/global.d.ts +125 -167
- package/dist/typings/index.d.ts +3 -13
- package/dist/typings/transaction-manager.d.ts +1 -1
- package/dist/typings/utils.d.ts +8 -7
- package/dist/typings/version.d.ts +1 -1
- package/package.json +35 -36
- package/src/Auth0Client.ts +407 -562
- package/src/Auth0Client.utils.ts +73 -0
- package/src/cache/cache-localstorage.ts +1 -1
- package/src/cache/cache-manager.ts +58 -29
- package/src/cache/shared.ts +29 -17
- package/src/constants.ts +0 -17
- package/src/errors.ts +10 -14
- package/src/global.ts +132 -183
- package/src/http.ts +0 -5
- package/src/index.ts +15 -14
- package/src/jwt.ts +3 -3
- package/src/storage.ts +1 -1
- package/src/transaction-manager.ts +1 -1
- package/src/utils.ts +37 -42
- package/src/version.ts +1 -1
- package/src/worker/token.worker.ts +4 -3
- package/dist/typings/index.cjs.d.ts +0 -5
- package/dist/typings/user-agent.d.ts +0 -1
- package/src/index.cjs.ts +0 -23
- package/src/user-agent.ts +0 -1
package/src/Auth0Client.ts
CHANGED
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
sha256,
|
|
11
11
|
bufferToBase64UrlEncoded,
|
|
12
12
|
validateCrypto,
|
|
13
|
-
openPopup
|
|
13
|
+
openPopup,
|
|
14
|
+
getDomain,
|
|
15
|
+
getTokenIssuer,
|
|
16
|
+
parseNumber
|
|
14
17
|
} from './utils';
|
|
15
18
|
|
|
16
19
|
import { oauthToken } from './api';
|
|
@@ -20,12 +23,14 @@ import { getUniqueScopes } from './scope';
|
|
|
20
23
|
import {
|
|
21
24
|
InMemoryCache,
|
|
22
25
|
ICache,
|
|
23
|
-
LocalStorageCache,
|
|
24
26
|
CacheKey,
|
|
25
|
-
CacheManager
|
|
27
|
+
CacheManager,
|
|
28
|
+
CacheEntry,
|
|
29
|
+
IdTokenEntry,
|
|
30
|
+
CACHE_KEY_ID_TOKEN_SUFFIX
|
|
26
31
|
} from './cache';
|
|
27
32
|
|
|
28
|
-
import TransactionManager from './transaction-manager';
|
|
33
|
+
import { TransactionManager } from './transaction-manager';
|
|
29
34
|
import { verify as verifyIdToken } from './jwt';
|
|
30
35
|
import {
|
|
31
36
|
AuthenticationError,
|
|
@@ -47,7 +52,6 @@ import {
|
|
|
47
52
|
DEFAULT_AUTHORIZE_TIMEOUT_IN_SECONDS,
|
|
48
53
|
MISSING_REFRESH_TOKEN_ERROR_MESSAGE,
|
|
49
54
|
DEFAULT_SCOPE,
|
|
50
|
-
RECOVERABLE_ERRORS,
|
|
51
55
|
DEFAULT_SESSION_CHECK_EXPIRY_DAYS,
|
|
52
56
|
DEFAULT_AUTH0_CLIENT,
|
|
53
57
|
INVALID_REFRESH_TOKEN_ERROR_MESSAGE,
|
|
@@ -57,19 +61,15 @@ import {
|
|
|
57
61
|
|
|
58
62
|
import {
|
|
59
63
|
Auth0ClientOptions,
|
|
60
|
-
|
|
64
|
+
AuthorizationParams,
|
|
61
65
|
AuthorizeOptions,
|
|
62
66
|
RedirectLoginOptions,
|
|
63
67
|
PopupLoginOptions,
|
|
64
68
|
PopupConfigOptions,
|
|
65
|
-
GetUserOptions,
|
|
66
|
-
GetIdTokenClaimsOptions,
|
|
67
69
|
RedirectLoginResult,
|
|
68
70
|
GetTokenSilentlyOptions,
|
|
69
71
|
GetTokenWithPopupOptions,
|
|
70
72
|
LogoutOptions,
|
|
71
|
-
RefreshTokenOptions,
|
|
72
|
-
OAuthTokenOptions,
|
|
73
73
|
CacheLocation,
|
|
74
74
|
LogoutUrlOptions,
|
|
75
75
|
User,
|
|
@@ -80,9 +80,16 @@ import {
|
|
|
80
80
|
|
|
81
81
|
// @ts-ignore
|
|
82
82
|
import TokenWorker from './worker/token.worker.ts';
|
|
83
|
-
import { isIE11 } from './user-agent';
|
|
84
83
|
import { singlePromise, retryPromise } from './promise-utils';
|
|
85
84
|
import { CacheKeyManifest } from './cache/key-manifest';
|
|
85
|
+
import {
|
|
86
|
+
buildIsAuthenticatedCookieName,
|
|
87
|
+
buildOrganizationHintCookieName,
|
|
88
|
+
cacheFactory,
|
|
89
|
+
getAuthorizeParams,
|
|
90
|
+
GET_TOKEN_SILENTLY_LOCK_KEY,
|
|
91
|
+
OLD_IS_AUTHENTICATED_COOKIE_NAME
|
|
92
|
+
} from './Auth0Client.utils';
|
|
86
93
|
|
|
87
94
|
/**
|
|
88
95
|
* @ignore
|
|
@@ -99,110 +106,14 @@ type GetTokenSilentlyResult = TokenEndpointResponse & {
|
|
|
99
106
|
*/
|
|
100
107
|
const lock = new Lock();
|
|
101
108
|
|
|
102
|
-
/**
|
|
103
|
-
* @ignore
|
|
104
|
-
*/
|
|
105
|
-
const GET_TOKEN_SILENTLY_LOCK_KEY = 'auth0.lock.getTokenSilently';
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* @ignore
|
|
109
|
-
*/
|
|
110
|
-
const buildOrganizationHintCookieName = (clientId: string) =>
|
|
111
|
-
`auth0.${clientId}.organization_hint`;
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* @ignore
|
|
115
|
-
*/
|
|
116
|
-
const OLD_IS_AUTHENTICATED_COOKIE_NAME = 'auth0.is.authenticated';
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* @ignore
|
|
120
|
-
*/
|
|
121
|
-
const buildIsAuthenticatedCookieName = (clientId: string) =>
|
|
122
|
-
`auth0.${clientId}.is.authenticated`;
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* @ignore
|
|
126
|
-
*/
|
|
127
|
-
const cacheLocationBuilders: Record<string, () => ICache> = {
|
|
128
|
-
memory: () => new InMemoryCache().enclosedCache,
|
|
129
|
-
localstorage: () => new LocalStorageCache()
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* @ignore
|
|
134
|
-
*/
|
|
135
|
-
const cacheFactory = (location: string) => {
|
|
136
|
-
return cacheLocationBuilders[location];
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* @ignore
|
|
141
|
-
*/
|
|
142
|
-
const supportWebWorker = () => !isIE11();
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* @ignore
|
|
146
|
-
*/
|
|
147
|
-
const getTokenIssuer = (issuer: string, domainUrl: string) => {
|
|
148
|
-
if (issuer) {
|
|
149
|
-
return issuer.startsWith('https://') ? issuer : `https://${issuer}/`;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return `${domainUrl}/`;
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* @ignore
|
|
157
|
-
*/
|
|
158
|
-
const getDomain = (domainUrl: string) => {
|
|
159
|
-
if (!/^https?:\/\//.test(domainUrl)) {
|
|
160
|
-
return `https://${domainUrl}`;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return domainUrl;
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* @ignore
|
|
168
|
-
*/
|
|
169
|
-
const getCustomInitialOptions = (
|
|
170
|
-
options: Auth0ClientOptions
|
|
171
|
-
): BaseLoginOptions => {
|
|
172
|
-
const {
|
|
173
|
-
advancedOptions,
|
|
174
|
-
audience,
|
|
175
|
-
auth0Client,
|
|
176
|
-
authorizeTimeoutInSeconds,
|
|
177
|
-
cacheLocation,
|
|
178
|
-
cache,
|
|
179
|
-
client_id,
|
|
180
|
-
domain,
|
|
181
|
-
issuer,
|
|
182
|
-
leeway,
|
|
183
|
-
max_age,
|
|
184
|
-
nowProvider,
|
|
185
|
-
redirect_uri,
|
|
186
|
-
scope,
|
|
187
|
-
useRefreshTokens,
|
|
188
|
-
useRefreshTokensFallback,
|
|
189
|
-
useCookiesForTransactions,
|
|
190
|
-
useFormData,
|
|
191
|
-
...customParams
|
|
192
|
-
} = options;
|
|
193
|
-
return customParams;
|
|
194
|
-
};
|
|
195
|
-
|
|
196
109
|
/**
|
|
197
110
|
* Auth0 SDK for Single Page Applications using [Authorization Code Grant Flow with PKCE](https://auth0.com/docs/api-auth/tutorials/authorization-code-grant-pkce).
|
|
198
111
|
*/
|
|
199
|
-
export
|
|
112
|
+
export class Auth0Client {
|
|
200
113
|
private readonly transactionManager: TransactionManager;
|
|
201
114
|
private readonly cacheManager: CacheManager;
|
|
202
|
-
private readonly customOptions: BaseLoginOptions;
|
|
203
115
|
private readonly domainUrl: string;
|
|
204
116
|
private readonly tokenIssuer: string;
|
|
205
|
-
private readonly defaultScope: string;
|
|
206
117
|
private readonly scope: string;
|
|
207
118
|
private readonly cookieStorage: ClientStorage;
|
|
208
119
|
private readonly sessionCheckExpiryDays: number;
|
|
@@ -210,12 +121,31 @@ export default class Auth0Client {
|
|
|
210
121
|
private readonly isAuthenticatedCookieName: string;
|
|
211
122
|
private readonly nowProvider: () => number | Promise<number>;
|
|
212
123
|
private readonly httpTimeoutMs: number;
|
|
213
|
-
private readonly
|
|
124
|
+
private readonly options: Auth0ClientOptions & { authorizationParams: AuthorizationParams };
|
|
125
|
+
private readonly userCache: ICache = new InMemoryCache().enclosedCache;
|
|
214
126
|
|
|
215
127
|
cacheLocation: CacheLocation;
|
|
128
|
+
|
|
216
129
|
private worker: Worker;
|
|
217
130
|
|
|
218
|
-
|
|
131
|
+
private readonly defaultOptions: Partial<Auth0ClientOptions> = {
|
|
132
|
+
authorizationParams: {
|
|
133
|
+
scope: DEFAULT_SCOPE
|
|
134
|
+
},
|
|
135
|
+
useRefreshTokensFallback: false,
|
|
136
|
+
useFormData: true
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
constructor(options: Auth0ClientOptions) {
|
|
140
|
+
this.options = {
|
|
141
|
+
...this.defaultOptions,
|
|
142
|
+
...options,
|
|
143
|
+
authorizationParams: {
|
|
144
|
+
...this.defaultOptions.authorizationParams,
|
|
145
|
+
...options.authorizationParams
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
219
149
|
typeof window !== 'undefined' && validateCrypto();
|
|
220
150
|
|
|
221
151
|
if (options.cache && options.cacheLocation) {
|
|
@@ -248,11 +178,11 @@ export default class Auth0Client {
|
|
|
248
178
|
: CookieStorageWithLegacySameSite;
|
|
249
179
|
|
|
250
180
|
this.orgHintCookieName = buildOrganizationHintCookieName(
|
|
251
|
-
this.options.
|
|
181
|
+
this.options.clientId
|
|
252
182
|
);
|
|
253
183
|
|
|
254
184
|
this.isAuthenticatedCookieName = buildIsAuthenticatedCookieName(
|
|
255
|
-
this.options.
|
|
185
|
+
this.options.clientId
|
|
256
186
|
);
|
|
257
187
|
|
|
258
188
|
this.sessionCheckExpiryDays =
|
|
@@ -262,11 +192,19 @@ export default class Auth0Client {
|
|
|
262
192
|
? this.cookieStorage
|
|
263
193
|
: SessionStorage;
|
|
264
194
|
|
|
265
|
-
|
|
195
|
+
// Construct the scopes based on the following:
|
|
196
|
+
// 1. Always include `openid`
|
|
197
|
+
// 2. Include the scopes provided in `authorizationParams. This defaults to `profile email`
|
|
198
|
+
// 3. Add `offline_access` if `useRefreshTokens` is enabled
|
|
199
|
+
this.scope = getUniqueScopes(
|
|
200
|
+
'openid',
|
|
201
|
+
this.options.authorizationParams.scope,
|
|
202
|
+
this.options.useRefreshTokens ? 'offline_access' : ''
|
|
203
|
+
);
|
|
266
204
|
|
|
267
205
|
this.transactionManager = new TransactionManager(
|
|
268
206
|
transactionStorage,
|
|
269
|
-
this.options.
|
|
207
|
+
this.options.clientId
|
|
270
208
|
);
|
|
271
209
|
|
|
272
210
|
this.nowProvider = this.options.nowProvider || DEFAULT_NOW_PROVIDER;
|
|
@@ -274,7 +212,7 @@ export default class Auth0Client {
|
|
|
274
212
|
this.cacheManager = new CacheManager(
|
|
275
213
|
cache,
|
|
276
214
|
!cache.allKeys
|
|
277
|
-
? new CacheKeyManifest(cache, this.options.
|
|
215
|
+
? new CacheKeyManifest(cache, this.options.clientId)
|
|
278
216
|
: null,
|
|
279
217
|
this.nowProvider
|
|
280
218
|
);
|
|
@@ -282,35 +220,15 @@ export default class Auth0Client {
|
|
|
282
220
|
this.domainUrl = getDomain(this.options.domain);
|
|
283
221
|
this.tokenIssuer = getTokenIssuer(this.options.issuer, this.domainUrl);
|
|
284
222
|
|
|
285
|
-
|
|
286
|
-
'openid',
|
|
287
|
-
this.options?.advancedOptions?.defaultScope !== undefined
|
|
288
|
-
? this.options.advancedOptions.defaultScope
|
|
289
|
-
: DEFAULT_SCOPE
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
// If using refresh tokens, automatically specify the `offline_access` scope.
|
|
293
|
-
// Note we cannot add this to 'defaultScope' above as the scopes are used in the
|
|
294
|
-
// cache keys - changing the order could invalidate the keys
|
|
295
|
-
if (this.options.useRefreshTokens) {
|
|
296
|
-
this.scope = getUniqueScopes(this.scope, 'offline_access');
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Don't use web workers unless using refresh tokens in memory and not IE11
|
|
223
|
+
// Don't use web workers unless using refresh tokens in memory
|
|
300
224
|
if (
|
|
301
225
|
typeof window !== 'undefined' &&
|
|
302
226
|
window.Worker &&
|
|
303
227
|
this.options.useRefreshTokens &&
|
|
304
|
-
this.cacheLocation === CACHE_LOCATION_MEMORY
|
|
305
|
-
supportWebWorker()
|
|
228
|
+
this.cacheLocation === CACHE_LOCATION_MEMORY
|
|
306
229
|
) {
|
|
307
230
|
this.worker = new TokenWorker();
|
|
308
231
|
}
|
|
309
|
-
|
|
310
|
-
this.customOptions = getCustomInitialOptions(options);
|
|
311
|
-
|
|
312
|
-
this.useRefreshTokensFallback =
|
|
313
|
-
this.options.useRefreshTokensFallback !== false;
|
|
314
232
|
}
|
|
315
233
|
|
|
316
234
|
private _url(path: string) {
|
|
@@ -320,52 +238,6 @@ export default class Auth0Client {
|
|
|
320
238
|
return `${this.domainUrl}${path}&auth0Client=${auth0Client}`;
|
|
321
239
|
}
|
|
322
240
|
|
|
323
|
-
private _getParams(
|
|
324
|
-
authorizeOptions: BaseLoginOptions,
|
|
325
|
-
state: string,
|
|
326
|
-
nonce: string,
|
|
327
|
-
code_challenge: string,
|
|
328
|
-
redirect_uri: string
|
|
329
|
-
): AuthorizeOptions {
|
|
330
|
-
// These options should be excluded from the authorize URL,
|
|
331
|
-
// as they're options for the client and not for the IdP.
|
|
332
|
-
// ** IMPORTANT ** If adding a new client option, include it in this destructure list.
|
|
333
|
-
const {
|
|
334
|
-
useRefreshTokens,
|
|
335
|
-
useCookiesForTransactions,
|
|
336
|
-
useFormData,
|
|
337
|
-
auth0Client,
|
|
338
|
-
cacheLocation,
|
|
339
|
-
advancedOptions,
|
|
340
|
-
detailedResponse,
|
|
341
|
-
nowProvider,
|
|
342
|
-
authorizeTimeoutInSeconds,
|
|
343
|
-
legacySameSiteCookie,
|
|
344
|
-
sessionCheckExpiryDays,
|
|
345
|
-
domain,
|
|
346
|
-
leeway,
|
|
347
|
-
httpTimeoutInSeconds,
|
|
348
|
-
...loginOptions
|
|
349
|
-
} = this.options;
|
|
350
|
-
|
|
351
|
-
return {
|
|
352
|
-
...loginOptions,
|
|
353
|
-
...authorizeOptions,
|
|
354
|
-
scope: getUniqueScopes(
|
|
355
|
-
this.defaultScope,
|
|
356
|
-
this.scope,
|
|
357
|
-
authorizeOptions.scope
|
|
358
|
-
),
|
|
359
|
-
response_type: 'code',
|
|
360
|
-
response_mode: 'query',
|
|
361
|
-
state,
|
|
362
|
-
nonce,
|
|
363
|
-
redirect_uri: redirect_uri || this.options.redirect_uri,
|
|
364
|
-
code_challenge,
|
|
365
|
-
code_challenge_method: 'S256'
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
|
|
369
241
|
private _authorizeUrl(authorizeOptions: AuthorizeOptions) {
|
|
370
242
|
return this._url(`/authorize?${createQueryParams(authorizeOptions)}`);
|
|
371
243
|
}
|
|
@@ -379,23 +251,16 @@ export default class Auth0Client {
|
|
|
379
251
|
|
|
380
252
|
return verifyIdToken({
|
|
381
253
|
iss: this.tokenIssuer,
|
|
382
|
-
aud: this.options.
|
|
254
|
+
aud: this.options.clientId,
|
|
383
255
|
id_token,
|
|
384
256
|
nonce,
|
|
385
257
|
organizationId,
|
|
386
258
|
leeway: this.options.leeway,
|
|
387
|
-
max_age:
|
|
259
|
+
max_age: parseNumber(this.options.authorizationParams.max_age),
|
|
388
260
|
now
|
|
389
261
|
});
|
|
390
262
|
}
|
|
391
263
|
|
|
392
|
-
private _parseNumber(value: any): number {
|
|
393
|
-
if (typeof value !== 'string') {
|
|
394
|
-
return value;
|
|
395
|
-
}
|
|
396
|
-
return parseInt(value, 10) || undefined;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
264
|
private _processOrgIdHint(organizationId?: string) {
|
|
400
265
|
if (organizationId) {
|
|
401
266
|
this.cookieStorage.save(this.orgHintCookieName, organizationId, {
|
|
@@ -403,56 +268,55 @@ export default class Auth0Client {
|
|
|
403
268
|
cookieDomain: this.options.cookieDomain
|
|
404
269
|
});
|
|
405
270
|
} else {
|
|
406
|
-
this.cookieStorage.remove(this.orgHintCookieName, {
|
|
271
|
+
this.cookieStorage.remove(this.orgHintCookieName, {
|
|
272
|
+
cookieDomain: this.options.cookieDomain
|
|
273
|
+
});
|
|
407
274
|
}
|
|
408
275
|
}
|
|
409
276
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
const stateIn = encode(createRandomString());
|
|
427
|
-
const nonceIn = encode(createRandomString());
|
|
277
|
+
private async _prepareAuthorizeUrl(
|
|
278
|
+
authorizationParams: AuthorizationParams,
|
|
279
|
+
authorizeOptions?: Partial<AuthorizeOptions>,
|
|
280
|
+
fallbackRedirectUri?: string
|
|
281
|
+
): Promise<{
|
|
282
|
+
scope: string;
|
|
283
|
+
audience: string;
|
|
284
|
+
redirect_uri: string;
|
|
285
|
+
nonce: string;
|
|
286
|
+
code_verifier: string;
|
|
287
|
+
state: string;
|
|
288
|
+
url: string;
|
|
289
|
+
}> {
|
|
290
|
+
const state = encode(createRandomString());
|
|
291
|
+
const nonce = encode(createRandomString());
|
|
428
292
|
const code_verifier = createRandomString();
|
|
429
293
|
const code_challengeBuffer = await sha256(code_verifier);
|
|
430
294
|
const code_challenge = bufferToBase64UrlEncoded(code_challengeBuffer);
|
|
431
|
-
const fragment = options.fragment ? `#${options.fragment}` : '';
|
|
432
295
|
|
|
433
|
-
const params =
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
296
|
+
const params = getAuthorizeParams(
|
|
297
|
+
this.options,
|
|
298
|
+
this.scope,
|
|
299
|
+
authorizationParams,
|
|
300
|
+
state,
|
|
301
|
+
nonce,
|
|
437
302
|
code_challenge,
|
|
438
|
-
redirect_uri
|
|
303
|
+
authorizationParams?.redirect_uri ||
|
|
304
|
+
this.options.authorizationParams.redirect_uri ||
|
|
305
|
+
fallbackRedirectUri,
|
|
306
|
+
authorizeOptions?.response_mode
|
|
439
307
|
);
|
|
440
308
|
|
|
441
309
|
const url = this._authorizeUrl(params);
|
|
442
|
-
const organizationId = options.organization || this.options.organization;
|
|
443
310
|
|
|
444
|
-
|
|
445
|
-
nonce
|
|
311
|
+
return {
|
|
312
|
+
nonce,
|
|
446
313
|
code_verifier,
|
|
447
|
-
appState,
|
|
448
314
|
scope: params.scope,
|
|
449
315
|
audience: params.audience || 'default',
|
|
450
316
|
redirect_uri: params.redirect_uri,
|
|
451
|
-
state
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
return url + fragment;
|
|
317
|
+
state,
|
|
318
|
+
url
|
|
319
|
+
};
|
|
456
320
|
}
|
|
457
321
|
|
|
458
322
|
/**
|
|
@@ -495,27 +359,13 @@ export default class Auth0Client {
|
|
|
495
359
|
}
|
|
496
360
|
}
|
|
497
361
|
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const code_challengeBuffer = await sha256(code_verifier);
|
|
503
|
-
const code_challenge = bufferToBase64UrlEncoded(code_challengeBuffer);
|
|
504
|
-
|
|
505
|
-
const params = this._getParams(
|
|
506
|
-
authorizeOptions,
|
|
507
|
-
stateIn,
|
|
508
|
-
nonceIn,
|
|
509
|
-
code_challenge,
|
|
510
|
-
this.options.redirect_uri || window.location.origin
|
|
362
|
+
const params = await this._prepareAuthorizeUrl(
|
|
363
|
+
options.authorizationParams,
|
|
364
|
+
{ response_mode: 'web_message' },
|
|
365
|
+
window.location.origin
|
|
511
366
|
);
|
|
512
367
|
|
|
513
|
-
|
|
514
|
-
...params,
|
|
515
|
-
response_mode: 'web_message'
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
config.popup.location.href = url;
|
|
368
|
+
config.popup.location.href = params.url;
|
|
519
369
|
|
|
520
370
|
const codeResult = await runPopup({
|
|
521
371
|
...config,
|
|
@@ -525,51 +375,28 @@ export default class Auth0Client {
|
|
|
525
375
|
DEFAULT_AUTHORIZE_TIMEOUT_IN_SECONDS
|
|
526
376
|
});
|
|
527
377
|
|
|
528
|
-
if (
|
|
378
|
+
if (params.state !== codeResult.state) {
|
|
529
379
|
throw new Error('Invalid state');
|
|
530
380
|
}
|
|
531
381
|
|
|
532
|
-
const
|
|
382
|
+
const organizationId =
|
|
383
|
+
options.authorizationParams?.organization ||
|
|
384
|
+
this.options.authorizationParams.organization;
|
|
385
|
+
|
|
386
|
+
await this._requestToken(
|
|
533
387
|
{
|
|
534
388
|
audience: params.audience,
|
|
535
389
|
scope: params.scope,
|
|
536
|
-
|
|
537
|
-
client_id: this.options.client_id,
|
|
538
|
-
code_verifier,
|
|
539
|
-
code: codeResult.code,
|
|
390
|
+
code_verifier: params.code_verifier,
|
|
540
391
|
grant_type: 'authorization_code',
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const organizationId = options.organization || this.options.organization;
|
|
550
|
-
|
|
551
|
-
const decodedToken = await this._verifyIdToken(
|
|
552
|
-
authResult.id_token,
|
|
553
|
-
nonceIn,
|
|
554
|
-
organizationId
|
|
392
|
+
code: codeResult.code,
|
|
393
|
+
redirect_uri: params.redirect_uri
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
nonceIn: params.nonce,
|
|
397
|
+
organizationId
|
|
398
|
+
}
|
|
555
399
|
);
|
|
556
|
-
|
|
557
|
-
const cacheEntry = {
|
|
558
|
-
...authResult,
|
|
559
|
-
decodedToken,
|
|
560
|
-
scope: params.scope,
|
|
561
|
-
audience: params.audience || 'default',
|
|
562
|
-
client_id: this.options.client_id
|
|
563
|
-
};
|
|
564
|
-
|
|
565
|
-
await this.cacheManager.set(cacheEntry);
|
|
566
|
-
|
|
567
|
-
this.cookieStorage.save(this.isAuthenticatedCookieName, true, {
|
|
568
|
-
daysUntilExpire: this.sessionCheckExpiryDays,
|
|
569
|
-
cookieDomain: this.options.cookieDomain
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
this._processOrgIdHint(decodedToken.claims.org_id);
|
|
573
400
|
}
|
|
574
401
|
|
|
575
402
|
/**
|
|
@@ -580,28 +407,12 @@ export default class Auth0Client {
|
|
|
580
407
|
* Returns the user information if available (decoded
|
|
581
408
|
* from the `id_token`).
|
|
582
409
|
*
|
|
583
|
-
* If you provide an audience or scope, they should match an existing Access Token
|
|
584
|
-
* (the SDK stores a corresponding ID Token with every Access Token, and uses the
|
|
585
|
-
* scope and audience to look up the ID Token)
|
|
586
|
-
*
|
|
587
410
|
* @typeparam TUser The type to return, has to extend {@link User}.
|
|
588
|
-
* @param options
|
|
589
411
|
*/
|
|
590
|
-
public async getUser<TUser extends User>(
|
|
591
|
-
|
|
592
|
-
): Promise<TUser | undefined> {
|
|
593
|
-
const audience = options.audience || this.options.audience || 'default';
|
|
594
|
-
const scope = getUniqueScopes(this.defaultScope, this.scope, options.scope);
|
|
595
|
-
|
|
596
|
-
const cache = await this.cacheManager.get(
|
|
597
|
-
new CacheKey({
|
|
598
|
-
client_id: this.options.client_id,
|
|
599
|
-
audience,
|
|
600
|
-
scope
|
|
601
|
-
})
|
|
602
|
-
);
|
|
412
|
+
public async getUser<TUser extends User>(): Promise<TUser | undefined> {
|
|
413
|
+
const cache = await this._getIdTokenFromCache();
|
|
603
414
|
|
|
604
|
-
return cache
|
|
415
|
+
return cache?.decodedToken?.user as TUser;
|
|
605
416
|
}
|
|
606
417
|
|
|
607
418
|
/**
|
|
@@ -610,28 +421,11 @@ export default class Auth0Client {
|
|
|
610
421
|
* ```
|
|
611
422
|
*
|
|
612
423
|
* Returns all claims from the id_token if available.
|
|
613
|
-
*
|
|
614
|
-
* If you provide an audience or scope, they should match an existing Access Token
|
|
615
|
-
* (the SDK stores a corresponding ID Token with every Access Token, and uses the
|
|
616
|
-
* scope and audience to look up the ID Token)
|
|
617
|
-
*
|
|
618
|
-
* @param options
|
|
619
424
|
*/
|
|
620
|
-
public async getIdTokenClaims(
|
|
621
|
-
|
|
622
|
-
): Promise<IdToken | undefined> {
|
|
623
|
-
const audience = options.audience || this.options.audience || 'default';
|
|
624
|
-
const scope = getUniqueScopes(this.defaultScope, this.scope, options.scope);
|
|
425
|
+
public async getIdTokenClaims(): Promise<IdToken | undefined> {
|
|
426
|
+
const cache = await this._getIdTokenFromCache();
|
|
625
427
|
|
|
626
|
-
|
|
627
|
-
new CacheKey({
|
|
628
|
-
client_id: this.options.client_id,
|
|
629
|
-
audience,
|
|
630
|
-
scope
|
|
631
|
-
})
|
|
632
|
-
);
|
|
633
|
-
|
|
634
|
-
return cache && cache.decodedToken && cache.decodedToken.claims;
|
|
428
|
+
return cache?.decodedToken?.claims;
|
|
635
429
|
}
|
|
636
430
|
|
|
637
431
|
/**
|
|
@@ -648,9 +442,29 @@ export default class Auth0Client {
|
|
|
648
442
|
public async loginWithRedirect<TAppState = any>(
|
|
649
443
|
options: RedirectLoginOptions<TAppState> = {}
|
|
650
444
|
) {
|
|
651
|
-
const {
|
|
652
|
-
|
|
653
|
-
|
|
445
|
+
const { onRedirect, fragment, appState, ...urlOptions } = options;
|
|
446
|
+
|
|
447
|
+
const organizationId =
|
|
448
|
+
urlOptions.authorizationParams?.organization ||
|
|
449
|
+
this.options.authorizationParams.organization;
|
|
450
|
+
|
|
451
|
+
const { url, ...transaction } = await this._prepareAuthorizeUrl(
|
|
452
|
+
urlOptions.authorizationParams
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
this.transactionManager.create({
|
|
456
|
+
...transaction,
|
|
457
|
+
appState,
|
|
458
|
+
...(organizationId && { organizationId })
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
const urlWithFragment = fragment ? `${url}#${fragment}` : url;
|
|
462
|
+
|
|
463
|
+
if (onRedirect) {
|
|
464
|
+
await onRedirect(urlWithFragment);
|
|
465
|
+
} else {
|
|
466
|
+
window.location.assign(urlWithFragment);
|
|
467
|
+
}
|
|
654
468
|
}
|
|
655
469
|
|
|
656
470
|
/**
|
|
@@ -697,48 +511,22 @@ export default class Auth0Client {
|
|
|
697
511
|
throw new Error('Invalid state');
|
|
698
512
|
}
|
|
699
513
|
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
baseUrl: this.domainUrl,
|
|
704
|
-
client_id: this.options.client_id,
|
|
705
|
-
code_verifier: transaction.code_verifier,
|
|
706
|
-
grant_type: 'authorization_code',
|
|
707
|
-
code,
|
|
708
|
-
auth0Client: this.options.auth0Client,
|
|
709
|
-
useFormData: this.options.useFormData,
|
|
710
|
-
timeout: this.httpTimeoutMs
|
|
711
|
-
} as OAuthTokenOptions;
|
|
712
|
-
// some old versions of the SDK might not have added redirect_uri to the
|
|
713
|
-
// transaction, we dont want the key to be set to undefined.
|
|
714
|
-
if (undefined !== transaction.redirect_uri) {
|
|
715
|
-
tokenOptions.redirect_uri = transaction.redirect_uri;
|
|
716
|
-
}
|
|
514
|
+
const organizationId = transaction.organizationId;
|
|
515
|
+
const nonceIn = transaction.nonce;
|
|
516
|
+
const redirect_uri = transaction.redirect_uri;
|
|
717
517
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
518
|
+
await this._requestToken(
|
|
519
|
+
{
|
|
520
|
+
audience: transaction.audience,
|
|
521
|
+
scope: transaction.scope,
|
|
522
|
+
code_verifier: transaction.code_verifier,
|
|
523
|
+
grant_type: 'authorization_code',
|
|
524
|
+
code,
|
|
525
|
+
...(redirect_uri ? { redirect_uri } : {})
|
|
526
|
+
},
|
|
527
|
+
{ nonceIn, organizationId }
|
|
724
528
|
);
|
|
725
529
|
|
|
726
|
-
await this.cacheManager.set({
|
|
727
|
-
...authResult,
|
|
728
|
-
decodedToken,
|
|
729
|
-
audience: transaction.audience,
|
|
730
|
-
scope: transaction.scope,
|
|
731
|
-
...(authResult.scope ? { oauthTokenScope: authResult.scope } : null),
|
|
732
|
-
client_id: this.options.client_id
|
|
733
|
-
});
|
|
734
|
-
|
|
735
|
-
this.cookieStorage.save(this.isAuthenticatedCookieName, true, {
|
|
736
|
-
daysUntilExpire: this.sessionCheckExpiryDays,
|
|
737
|
-
cookieDomain: this.options.cookieDomain
|
|
738
|
-
});
|
|
739
|
-
|
|
740
|
-
this._processOrgIdHint(decodedToken.claims.org_id);
|
|
741
|
-
|
|
742
530
|
return {
|
|
743
531
|
appState: transaction.appState
|
|
744
532
|
};
|
|
@@ -786,11 +574,7 @@ export default class Auth0Client {
|
|
|
786
574
|
|
|
787
575
|
try {
|
|
788
576
|
await this.getTokenSilently(options);
|
|
789
|
-
} catch (
|
|
790
|
-
if (!RECOVERABLE_ERRORS.includes(error.error)) {
|
|
791
|
-
throw error;
|
|
792
|
-
}
|
|
793
|
-
}
|
|
577
|
+
} catch (_) {}
|
|
794
578
|
}
|
|
795
579
|
|
|
796
580
|
/**
|
|
@@ -823,7 +607,7 @@ export default class Auth0Client {
|
|
|
823
607
|
* to obtain a new token.
|
|
824
608
|
*
|
|
825
609
|
* A new token will be obtained either by opening an iframe or a
|
|
826
|
-
* refresh token (if `useRefreshTokens` is `true`)
|
|
610
|
+
* refresh token (if `useRefreshTokens` is `true`).
|
|
827
611
|
|
|
828
612
|
* If iframes are used, opens an iframe with the `/authorize` URL
|
|
829
613
|
* using the parameters provided as arguments. Random and secure `state`
|
|
@@ -832,7 +616,9 @@ export default class Auth0Client {
|
|
|
832
616
|
*
|
|
833
617
|
* If refresh tokens are used, the token endpoint is called directly with the
|
|
834
618
|
* 'refresh_token' grant. If no refresh token is available to make this call,
|
|
835
|
-
* the SDK
|
|
619
|
+
* the SDK will only fall back to using an iframe to the '/authorize' URL if
|
|
620
|
+
* the `useRefreshTokensFallback` setting has been set to `true`. By default this
|
|
621
|
+
* setting is `false`.
|
|
836
622
|
*
|
|
837
623
|
* This method may use a web worker to perform the token call if the in-memory
|
|
838
624
|
* cache is used.
|
|
@@ -848,35 +634,34 @@ export default class Auth0Client {
|
|
|
848
634
|
public async getTokenSilently(
|
|
849
635
|
options: GetTokenSilentlyOptions = {}
|
|
850
636
|
): Promise<string | GetTokenSilentlyVerboseResponse> {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
ignoreCache: false,
|
|
637
|
+
options = {
|
|
638
|
+
cacheMode: 'on',
|
|
854
639
|
...options,
|
|
855
|
-
|
|
640
|
+
authorizationParams: {
|
|
641
|
+
...this.options.authorizationParams,
|
|
642
|
+
...options.authorizationParams,
|
|
643
|
+
scope: getUniqueScopes(this.scope, options.authorizationParams?.scope)
|
|
644
|
+
}
|
|
856
645
|
};
|
|
857
646
|
|
|
858
647
|
return singlePromise(
|
|
859
|
-
() =>
|
|
860
|
-
|
|
861
|
-
ignoreCache,
|
|
862
|
-
...getTokenOptions
|
|
863
|
-
}),
|
|
864
|
-
`${this.options.client_id}::${getTokenOptions.audience}::${getTokenOptions.scope}`
|
|
648
|
+
() => this._getTokenSilently(options),
|
|
649
|
+
`${this.options.clientId}::${options.authorizationParams.audience}::${options.authorizationParams.scope}`
|
|
865
650
|
);
|
|
866
651
|
}
|
|
867
652
|
|
|
868
653
|
private async _getTokenSilently(
|
|
869
|
-
options: GetTokenSilentlyOptions
|
|
654
|
+
options: GetTokenSilentlyOptions
|
|
870
655
|
): Promise<string | GetTokenSilentlyVerboseResponse> {
|
|
871
|
-
const {
|
|
656
|
+
const { cacheMode, ...getTokenOptions } = options;
|
|
872
657
|
|
|
873
658
|
// Check the cache before acquiring the lock to avoid the latency of
|
|
874
659
|
// `lock.acquireLock` when the cache is populated.
|
|
875
|
-
if (
|
|
660
|
+
if (cacheMode !== 'off') {
|
|
876
661
|
const entry = await this._getEntryFromCache({
|
|
877
|
-
scope: getTokenOptions.scope,
|
|
878
|
-
audience: getTokenOptions.audience || 'default',
|
|
879
|
-
|
|
662
|
+
scope: getTokenOptions.authorizationParams.scope,
|
|
663
|
+
audience: getTokenOptions.authorizationParams.audience || 'default',
|
|
664
|
+
clientId: this.options.clientId,
|
|
880
665
|
getDetailedEntry: options.detailedResponse
|
|
881
666
|
});
|
|
882
667
|
|
|
@@ -885,6 +670,10 @@ export default class Auth0Client {
|
|
|
885
670
|
}
|
|
886
671
|
}
|
|
887
672
|
|
|
673
|
+
if (cacheMode === 'cache-only') {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
|
|
888
677
|
if (
|
|
889
678
|
await retryPromise(
|
|
890
679
|
() => lock.acquireLock(GET_TOKEN_SILENTLY_LOCK_KEY, 5000),
|
|
@@ -892,13 +681,16 @@ export default class Auth0Client {
|
|
|
892
681
|
)
|
|
893
682
|
) {
|
|
894
683
|
try {
|
|
684
|
+
window.addEventListener('pagehide', this._releaseLockOnPageHide);
|
|
685
|
+
|
|
895
686
|
// Check the cache a second time, because it may have been populated
|
|
896
687
|
// by a previous call while this call was waiting to acquire the lock.
|
|
897
|
-
if (
|
|
688
|
+
if (cacheMode !== 'off') {
|
|
898
689
|
const entry = await this._getEntryFromCache({
|
|
899
|
-
scope: getTokenOptions.scope,
|
|
900
|
-
audience:
|
|
901
|
-
|
|
690
|
+
scope: getTokenOptions.authorizationParams.scope,
|
|
691
|
+
audience:
|
|
692
|
+
getTokenOptions.authorizationParams.audience || 'default',
|
|
693
|
+
clientId: this.options.clientId,
|
|
902
694
|
getDetailedEntry: options.detailedResponse
|
|
903
695
|
});
|
|
904
696
|
|
|
@@ -911,16 +703,6 @@ export default class Auth0Client {
|
|
|
911
703
|
? await this._getTokenUsingRefreshToken(getTokenOptions)
|
|
912
704
|
: await this._getTokenFromIFrame(getTokenOptions);
|
|
913
705
|
|
|
914
|
-
await this.cacheManager.set({
|
|
915
|
-
client_id: this.options.client_id,
|
|
916
|
-
...authResult
|
|
917
|
-
});
|
|
918
|
-
|
|
919
|
-
this.cookieStorage.save(this.isAuthenticatedCookieName, true, {
|
|
920
|
-
daysUntilExpire: this.sessionCheckExpiryDays,
|
|
921
|
-
cookieDomain: this.options.cookieDomain
|
|
922
|
-
});
|
|
923
|
-
|
|
924
706
|
if (options.detailedResponse) {
|
|
925
707
|
const { id_token, access_token, oauthTokenScope, expires_in } =
|
|
926
708
|
authResult;
|
|
@@ -936,6 +718,7 @@ export default class Auth0Client {
|
|
|
936
718
|
return authResult.access_token;
|
|
937
719
|
} finally {
|
|
938
720
|
await lock.releaseLock(GET_TOKEN_SILENTLY_LOCK_KEY);
|
|
721
|
+
window.removeEventListener('pagehide', this._releaseLockOnPageHide);
|
|
939
722
|
}
|
|
940
723
|
} else {
|
|
941
724
|
throw new TimeoutError();
|
|
@@ -958,13 +741,14 @@ export default class Auth0Client {
|
|
|
958
741
|
options: GetTokenWithPopupOptions = {},
|
|
959
742
|
config: PopupConfigOptions = {}
|
|
960
743
|
) {
|
|
961
|
-
options
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
744
|
+
options = {
|
|
745
|
+
...options,
|
|
746
|
+
authorizationParams: {
|
|
747
|
+
...this.options.authorizationParams,
|
|
748
|
+
...options.authorizationParams,
|
|
749
|
+
scope: getUniqueScopes(this.scope, options.authorizationParams?.scope)
|
|
750
|
+
}
|
|
751
|
+
};
|
|
968
752
|
|
|
969
753
|
config = {
|
|
970
754
|
...DEFAULT_POPUP_CONFIG_OPTIONS,
|
|
@@ -975,9 +759,9 @@ export default class Auth0Client {
|
|
|
975
759
|
|
|
976
760
|
const cache = await this.cacheManager.get(
|
|
977
761
|
new CacheKey({
|
|
978
|
-
scope: options.scope,
|
|
979
|
-
audience: options.audience || 'default',
|
|
980
|
-
|
|
762
|
+
scope: options.authorizationParams.scope,
|
|
763
|
+
audience: options.authorizationParams.audience || 'default',
|
|
764
|
+
clientId: this.options.clientId
|
|
981
765
|
})
|
|
982
766
|
);
|
|
983
767
|
|
|
@@ -1006,88 +790,67 @@ export default class Auth0Client {
|
|
|
1006
790
|
* Builds a URL to the logout endpoint using the parameters provided as arguments.
|
|
1007
791
|
* @param options
|
|
1008
792
|
*/
|
|
1009
|
-
|
|
1010
|
-
if (options.
|
|
1011
|
-
options.
|
|
793
|
+
private _buildLogoutUrl(options: LogoutUrlOptions): string {
|
|
794
|
+
if (options.clientId !== null) {
|
|
795
|
+
options.clientId = options.clientId || this.options.clientId;
|
|
1012
796
|
} else {
|
|
1013
|
-
delete options.
|
|
797
|
+
delete options.clientId;
|
|
1014
798
|
}
|
|
1015
799
|
|
|
1016
|
-
const { federated, ...logoutOptions } = options;
|
|
800
|
+
const { federated, ...logoutOptions } = options.logoutParams || {};
|
|
1017
801
|
const federatedQuery = federated ? `&federated` : '';
|
|
1018
|
-
const url = this._url(
|
|
802
|
+
const url = this._url(
|
|
803
|
+
`/v2/logout?${createQueryParams({
|
|
804
|
+
clientId: options.clientId,
|
|
805
|
+
...logoutOptions
|
|
806
|
+
})}`
|
|
807
|
+
);
|
|
1019
808
|
|
|
1020
809
|
return url + federatedQuery;
|
|
1021
810
|
}
|
|
1022
811
|
|
|
1023
812
|
/**
|
|
1024
813
|
* ```js
|
|
1025
|
-
* auth0.logout();
|
|
814
|
+
* await auth0.logout(options);
|
|
1026
815
|
* ```
|
|
1027
816
|
*
|
|
1028
817
|
* Clears the application session and performs a redirect to `/v2/logout`, using
|
|
1029
|
-
* the parameters provided as arguments, to clear the Auth0 session.
|
|
1030
|
-
*
|
|
1031
|
-
* **Note:** If you are using a custom cache, and specifying `localOnly: true`, and you want to perform actions or read state from the SDK immediately after logout, you should `await` the result of calling `logout`.
|
|
818
|
+
* the parameters provided as arguments, to clear the Auth0 session.
|
|
1032
819
|
*
|
|
1033
820
|
* If the `federated` option is specified it also clears the Identity Provider session.
|
|
1034
|
-
* If the `localOnly` option is specified, it only clears the application session.
|
|
1035
|
-
* It is invalid to set both the `federated` and `localOnly` options to `true`,
|
|
1036
|
-
* and an error will be thrown if you do.
|
|
1037
821
|
* [Read more about how Logout works at Auth0](https://auth0.com/docs/logout).
|
|
1038
822
|
*
|
|
1039
823
|
* @param options
|
|
1040
824
|
*/
|
|
1041
|
-
public logout(options: LogoutOptions = {}): Promise<void>
|
|
1042
|
-
const {
|
|
1043
|
-
|
|
1044
|
-
if (localOnly && logoutOptions.federated) {
|
|
1045
|
-
throw new Error(
|
|
1046
|
-
'It is invalid to set both the `federated` and `localOnly` options to `true`'
|
|
1047
|
-
);
|
|
1048
|
-
}
|
|
825
|
+
public async logout(options: LogoutOptions = {}): Promise<void> {
|
|
826
|
+
const { onRedirect, ...logoutOptions } = options;
|
|
1049
827
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
828
|
+
await this.cacheManager.clear();
|
|
829
|
+
|
|
830
|
+
this.cookieStorage.remove(this.orgHintCookieName, {
|
|
831
|
+
cookieDomain: this.options.cookieDomain
|
|
832
|
+
});
|
|
833
|
+
this.cookieStorage.remove(this.isAuthenticatedCookieName, {
|
|
834
|
+
cookieDomain: this.options.cookieDomain
|
|
835
|
+
});
|
|
836
|
+
this.userCache.remove(CACHE_KEY_ID_TOKEN_SUFFIX);
|
|
1059
837
|
|
|
1060
|
-
|
|
1061
|
-
};
|
|
838
|
+
const url = this._buildLogoutUrl(logoutOptions);
|
|
1062
839
|
|
|
1063
|
-
if (
|
|
1064
|
-
|
|
840
|
+
if (onRedirect) {
|
|
841
|
+
await onRedirect(url);
|
|
1065
842
|
} else {
|
|
1066
|
-
|
|
1067
|
-
postCacheClear();
|
|
843
|
+
window.location.assign(url);
|
|
1068
844
|
}
|
|
1069
845
|
}
|
|
1070
846
|
|
|
1071
847
|
private async _getTokenFromIFrame(
|
|
1072
848
|
options: GetTokenSilentlyOptions
|
|
1073
849
|
): Promise<GetTokenSilentlyResult> {
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
const code_challenge = bufferToBase64UrlEncoded(code_challengeBuffer);
|
|
1079
|
-
|
|
1080
|
-
const { detailedResponse, ...withoutClientOptions } = options;
|
|
1081
|
-
|
|
1082
|
-
const params = this._getParams(
|
|
1083
|
-
withoutClientOptions,
|
|
1084
|
-
stateIn,
|
|
1085
|
-
nonceIn,
|
|
1086
|
-
code_challenge,
|
|
1087
|
-
options.redirect_uri ||
|
|
1088
|
-
this.options.redirect_uri ||
|
|
1089
|
-
window.location.origin
|
|
1090
|
-
);
|
|
850
|
+
const params: AuthorizationParams = {
|
|
851
|
+
...options.authorizationParams,
|
|
852
|
+
prompt: 'none'
|
|
853
|
+
};
|
|
1091
854
|
|
|
1092
855
|
const orgIdHint = this.cookieStorage.get<string>(this.orgHintCookieName);
|
|
1093
856
|
|
|
@@ -1095,11 +858,19 @@ export default class Auth0Client {
|
|
|
1095
858
|
params.organization = orgIdHint;
|
|
1096
859
|
}
|
|
1097
860
|
|
|
1098
|
-
const
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
861
|
+
const {
|
|
862
|
+
url,
|
|
863
|
+
state: stateIn,
|
|
864
|
+
nonce: nonceIn,
|
|
865
|
+
code_verifier,
|
|
866
|
+
redirect_uri,
|
|
867
|
+
scope,
|
|
868
|
+
audience
|
|
869
|
+
} = await this._prepareAuthorizeUrl(
|
|
870
|
+
params,
|
|
871
|
+
{ response_mode: 'web_message' },
|
|
872
|
+
window.location.origin
|
|
873
|
+
);
|
|
1103
874
|
|
|
1104
875
|
try {
|
|
1105
876
|
// When a browser is running in a Cross-Origin Isolated context, using iframes is not possible.
|
|
@@ -1121,53 +892,30 @@ export default class Auth0Client {
|
|
|
1121
892
|
throw new Error('Invalid state');
|
|
1122
893
|
}
|
|
1123
894
|
|
|
1124
|
-
const
|
|
1125
|
-
scope,
|
|
1126
|
-
audience,
|
|
1127
|
-
redirect_uri,
|
|
1128
|
-
ignoreCache,
|
|
1129
|
-
timeoutInSeconds,
|
|
1130
|
-
detailedResponse,
|
|
1131
|
-
...customOptions
|
|
1132
|
-
} = options;
|
|
1133
|
-
|
|
1134
|
-
const tokenResult = await oauthToken(
|
|
895
|
+
const tokenResult = await this._requestToken(
|
|
1135
896
|
{
|
|
1136
|
-
...
|
|
1137
|
-
...customOptions,
|
|
1138
|
-
scope,
|
|
1139
|
-
audience,
|
|
1140
|
-
baseUrl: this.domainUrl,
|
|
1141
|
-
client_id: this.options.client_id,
|
|
897
|
+
...options.authorizationParams,
|
|
1142
898
|
code_verifier,
|
|
1143
899
|
code: codeResult.code,
|
|
1144
900
|
grant_type: 'authorization_code',
|
|
1145
|
-
redirect_uri
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
);
|
|
1152
|
-
|
|
1153
|
-
const decodedToken = await this._verifyIdToken(
|
|
1154
|
-
tokenResult.id_token,
|
|
1155
|
-
nonceIn
|
|
901
|
+
redirect_uri,
|
|
902
|
+
timeout: options.authorizationParams.timeout || this.httpTimeoutMs
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
nonceIn
|
|
906
|
+
}
|
|
1156
907
|
);
|
|
1157
908
|
|
|
1158
|
-
this._processOrgIdHint(decodedToken.claims.org_id);
|
|
1159
|
-
|
|
1160
909
|
return {
|
|
1161
910
|
...tokenResult,
|
|
1162
|
-
|
|
1163
|
-
scope: params.scope,
|
|
911
|
+
scope: scope,
|
|
1164
912
|
oauthTokenScope: tokenResult.scope,
|
|
1165
|
-
audience:
|
|
913
|
+
audience: audience
|
|
1166
914
|
};
|
|
1167
915
|
} catch (e) {
|
|
1168
916
|
if (e.error === 'login_required') {
|
|
1169
917
|
this.logout({
|
|
1170
|
-
|
|
918
|
+
onRedirect: async () => {}
|
|
1171
919
|
});
|
|
1172
920
|
}
|
|
1173
921
|
throw e;
|
|
@@ -1177,128 +925,148 @@ export default class Auth0Client {
|
|
|
1177
925
|
private async _getTokenUsingRefreshToken(
|
|
1178
926
|
options: GetTokenSilentlyOptions
|
|
1179
927
|
): Promise<GetTokenSilentlyResult> {
|
|
1180
|
-
options
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
928
|
+
options = {
|
|
929
|
+
...options,
|
|
930
|
+
authorizationParams: {
|
|
931
|
+
...options.authorizationParams,
|
|
932
|
+
scope: getUniqueScopes(this.scope, options.authorizationParams.scope)
|
|
933
|
+
}
|
|
934
|
+
};
|
|
1185
935
|
|
|
1186
936
|
const cache = await this.cacheManager.get(
|
|
1187
937
|
new CacheKey({
|
|
1188
|
-
scope: options.scope,
|
|
1189
|
-
audience: options.audience || 'default',
|
|
1190
|
-
|
|
938
|
+
scope: options.authorizationParams.scope,
|
|
939
|
+
audience: options.authorizationParams.audience || 'default',
|
|
940
|
+
clientId: this.options.clientId
|
|
1191
941
|
})
|
|
1192
942
|
);
|
|
1193
943
|
|
|
1194
944
|
// If you don't have a refresh token in memory
|
|
1195
945
|
// and you don't have a refresh token in web worker memory
|
|
1196
|
-
//
|
|
946
|
+
// and useRefreshTokensFallback was explicitly enabled
|
|
947
|
+
// fallback to an iframe
|
|
1197
948
|
if ((!cache || !cache.refresh_token) && !this.worker) {
|
|
1198
|
-
if (this.useRefreshTokensFallback) {
|
|
949
|
+
if (this.options.useRefreshTokensFallback) {
|
|
1199
950
|
return await this._getTokenFromIFrame(options);
|
|
1200
951
|
}
|
|
1201
952
|
|
|
1202
953
|
throw new MissingRefreshTokenError(
|
|
1203
|
-
options.audience || 'default',
|
|
1204
|
-
options.scope
|
|
954
|
+
options.authorizationParams.audience || 'default',
|
|
955
|
+
options.authorizationParams.scope
|
|
1205
956
|
);
|
|
1206
957
|
}
|
|
1207
958
|
|
|
1208
959
|
const redirect_uri =
|
|
1209
|
-
options.redirect_uri ||
|
|
1210
|
-
this.options.redirect_uri ||
|
|
960
|
+
options.authorizationParams.redirect_uri ||
|
|
961
|
+
this.options.authorizationParams.redirect_uri ||
|
|
1211
962
|
window.location.origin;
|
|
1212
963
|
|
|
1213
|
-
let tokenResult: TokenEndpointResponse;
|
|
1214
|
-
|
|
1215
|
-
const {
|
|
1216
|
-
scope,
|
|
1217
|
-
audience,
|
|
1218
|
-
ignoreCache,
|
|
1219
|
-
timeoutInSeconds,
|
|
1220
|
-
detailedResponse,
|
|
1221
|
-
...customOptions
|
|
1222
|
-
} = options;
|
|
1223
|
-
|
|
1224
964
|
const timeout =
|
|
1225
965
|
typeof options.timeoutInSeconds === 'number'
|
|
1226
966
|
? options.timeoutInSeconds * 1000
|
|
1227
967
|
: null;
|
|
1228
968
|
|
|
1229
969
|
try {
|
|
1230
|
-
tokenResult = await
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
timeout: this.httpTimeoutMs
|
|
1245
|
-
} as RefreshTokenOptions,
|
|
1246
|
-
this.worker
|
|
1247
|
-
);
|
|
970
|
+
const tokenResult = await this._requestToken({
|
|
971
|
+
...options.authorizationParams,
|
|
972
|
+
grant_type: 'refresh_token',
|
|
973
|
+
refresh_token: cache && cache.refresh_token,
|
|
974
|
+
redirect_uri,
|
|
975
|
+
...(timeout && { timeout })
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
return {
|
|
979
|
+
...tokenResult,
|
|
980
|
+
scope: options.authorizationParams.scope,
|
|
981
|
+
oauthTokenScope: tokenResult.scope,
|
|
982
|
+
audience: options.authorizationParams.audience || 'default'
|
|
983
|
+
};
|
|
1248
984
|
} catch (e) {
|
|
1249
985
|
if (
|
|
1250
986
|
// The web worker didn't have a refresh token in memory so
|
|
1251
987
|
// fallback to an iframe.
|
|
1252
988
|
(e.message.indexOf(MISSING_REFRESH_TOKEN_ERROR_MESSAGE) > -1 ||
|
|
1253
|
-
// A refresh token was found, but is it no longer valid
|
|
1254
|
-
// Fallback to an iframe.
|
|
989
|
+
// A refresh token was found, but is it no longer valid
|
|
990
|
+
// and useRefreshTokensFallback is explicitly enabled. Fallback to an iframe.
|
|
1255
991
|
(e.message &&
|
|
1256
992
|
e.message.indexOf(INVALID_REFRESH_TOKEN_ERROR_MESSAGE) > -1)) &&
|
|
1257
|
-
this.useRefreshTokensFallback
|
|
993
|
+
this.options.useRefreshTokensFallback
|
|
1258
994
|
) {
|
|
1259
995
|
return await this._getTokenFromIFrame(options);
|
|
1260
996
|
}
|
|
1261
997
|
|
|
1262
998
|
throw e;
|
|
1263
999
|
}
|
|
1000
|
+
}
|
|
1264
1001
|
|
|
1265
|
-
|
|
1002
|
+
private async _saveEntryInCache(entry: CacheEntry) {
|
|
1003
|
+
const { id_token, decodedToken, ...entryWithoutIdToken } = entry;
|
|
1266
1004
|
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
decodedToken
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1005
|
+
this.userCache.set(CACHE_KEY_ID_TOKEN_SUFFIX, {
|
|
1006
|
+
id_token,
|
|
1007
|
+
decodedToken
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
await this.cacheManager.setIdToken(
|
|
1011
|
+
this.options.clientId,
|
|
1012
|
+
entry.id_token,
|
|
1013
|
+
entry.decodedToken
|
|
1014
|
+
);
|
|
1015
|
+
|
|
1016
|
+
await this.cacheManager.set(entryWithoutIdToken);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
private async _getIdTokenFromCache() {
|
|
1020
|
+
const audience = this.options.authorizationParams.audience || 'default';
|
|
1021
|
+
|
|
1022
|
+
const cache = await this.cacheManager.getIdToken(
|
|
1023
|
+
new CacheKey({
|
|
1024
|
+
clientId: this.options.clientId,
|
|
1025
|
+
audience,
|
|
1026
|
+
scope: this.scope
|
|
1027
|
+
})
|
|
1028
|
+
);
|
|
1029
|
+
|
|
1030
|
+
const currentCache = this.userCache.get<IdTokenEntry>(
|
|
1031
|
+
CACHE_KEY_ID_TOKEN_SUFFIX
|
|
1032
|
+
) as IdTokenEntry;
|
|
1033
|
+
|
|
1034
|
+
// If the id_token in the cache matches the value we previously cached in memory return the in-memory
|
|
1035
|
+
// value so that object comparison will work
|
|
1036
|
+
if (cache && cache.id_token === currentCache?.id_token) {
|
|
1037
|
+
return currentCache;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
this.userCache.set(CACHE_KEY_ID_TOKEN_SUFFIX, cache);
|
|
1041
|
+
return cache;
|
|
1274
1042
|
}
|
|
1275
1043
|
|
|
1276
1044
|
private async _getEntryFromCache({
|
|
1277
1045
|
scope,
|
|
1278
1046
|
audience,
|
|
1279
|
-
|
|
1047
|
+
clientId,
|
|
1280
1048
|
getDetailedEntry = false
|
|
1281
1049
|
}: {
|
|
1282
1050
|
scope: string;
|
|
1283
1051
|
audience: string;
|
|
1284
|
-
|
|
1052
|
+
clientId: string;
|
|
1285
1053
|
getDetailedEntry?: boolean;
|
|
1286
1054
|
}) {
|
|
1287
1055
|
const entry = await this.cacheManager.get(
|
|
1288
1056
|
new CacheKey({
|
|
1289
1057
|
scope,
|
|
1290
1058
|
audience,
|
|
1291
|
-
|
|
1059
|
+
clientId
|
|
1292
1060
|
}),
|
|
1293
1061
|
60 // get a new token if within 60 seconds of expiring
|
|
1294
1062
|
);
|
|
1295
1063
|
|
|
1296
1064
|
if (entry && entry.access_token) {
|
|
1297
1065
|
if (getDetailedEntry) {
|
|
1298
|
-
const {
|
|
1299
|
-
|
|
1066
|
+
const { access_token, oauthTokenScope, expires_in } = entry;
|
|
1067
|
+
const cache = await this._getIdTokenFromCache();
|
|
1300
1068
|
return {
|
|
1301
|
-
id_token,
|
|
1069
|
+
id_token: cache?.id_token,
|
|
1302
1070
|
access_token,
|
|
1303
1071
|
...(oauthTokenScope ? { scope: oauthTokenScope } : null),
|
|
1304
1072
|
expires_in
|
|
@@ -1308,4 +1076,81 @@ export default class Auth0Client {
|
|
|
1308
1076
|
return entry.access_token;
|
|
1309
1077
|
}
|
|
1310
1078
|
}
|
|
1079
|
+
|
|
1080
|
+
/**
|
|
1081
|
+
* Releases any lock acquired by the current page that's not released yet
|
|
1082
|
+
*
|
|
1083
|
+
* Get's called on the `pagehide` event.
|
|
1084
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
|
|
1085
|
+
*/
|
|
1086
|
+
private _releaseLockOnPageHide = async () => {
|
|
1087
|
+
await lock.releaseLock(GET_TOKEN_SILENTLY_LOCK_KEY);
|
|
1088
|
+
|
|
1089
|
+
window.removeEventListener('pagehide', this._releaseLockOnPageHide);
|
|
1090
|
+
};
|
|
1091
|
+
|
|
1092
|
+
private async _requestToken(
|
|
1093
|
+
options: PKCERequestTokenOptions | RefreshTokenRequestTokenOptions,
|
|
1094
|
+
additionalParameters?: RequestTokenAdditionalParameters
|
|
1095
|
+
) {
|
|
1096
|
+
const { nonceIn, organizationId } = additionalParameters || {};
|
|
1097
|
+
const authResult = await oauthToken(
|
|
1098
|
+
{
|
|
1099
|
+
baseUrl: this.domainUrl,
|
|
1100
|
+
client_id: this.options.clientId,
|
|
1101
|
+
auth0Client: this.options.auth0Client,
|
|
1102
|
+
useFormData: this.options.useFormData,
|
|
1103
|
+
timeout: this.httpTimeoutMs,
|
|
1104
|
+
...options
|
|
1105
|
+
},
|
|
1106
|
+
this.worker
|
|
1107
|
+
);
|
|
1108
|
+
|
|
1109
|
+
const decodedToken = await this._verifyIdToken(
|
|
1110
|
+
authResult.id_token,
|
|
1111
|
+
nonceIn,
|
|
1112
|
+
organizationId
|
|
1113
|
+
);
|
|
1114
|
+
|
|
1115
|
+
await this._saveEntryInCache({
|
|
1116
|
+
...authResult,
|
|
1117
|
+
decodedToken,
|
|
1118
|
+
scope: options.scope,
|
|
1119
|
+
audience: options.audience || 'default',
|
|
1120
|
+
...(authResult.scope ? { oauthTokenScope: authResult.scope } : null),
|
|
1121
|
+
client_id: this.options.clientId
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
this.cookieStorage.save(this.isAuthenticatedCookieName, true, {
|
|
1125
|
+
daysUntilExpire: this.sessionCheckExpiryDays,
|
|
1126
|
+
cookieDomain: this.options.cookieDomain
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
this._processOrgIdHint(decodedToken.claims.org_id);
|
|
1130
|
+
|
|
1131
|
+
return { ...authResult, decodedToken };
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
interface BaseRequestTokenOptions {
|
|
1136
|
+
audience?: string;
|
|
1137
|
+
scope?: string;
|
|
1138
|
+
timeout?: number;
|
|
1139
|
+
redirect_uri?: string;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
interface PKCERequestTokenOptions extends BaseRequestTokenOptions {
|
|
1143
|
+
code: string;
|
|
1144
|
+
grant_type: 'authorization_code';
|
|
1145
|
+
code_verifier: string;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
interface RefreshTokenRequestTokenOptions extends BaseRequestTokenOptions {
|
|
1149
|
+
grant_type: 'refresh_token';
|
|
1150
|
+
refresh_token: string;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
interface RequestTokenAdditionalParameters {
|
|
1154
|
+
nonceIn?: string;
|
|
1155
|
+
organizationId?: string;
|
|
1311
1156
|
}
|