@auth0/auth0-spa-js 2.11.2 → 2.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth0-spa-js.development.js +56 -47
- 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 +58 -47
- package/dist/lib/auth0-spa-js.cjs.js.map +1 -1
- package/dist/typings/Auth0Client.utils.d.ts +12 -0
- package/dist/typings/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/Auth0Client.ts +89 -70
- package/src/Auth0Client.utils.ts +15 -0
- package/src/cache/cache-manager.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -4,10 +4,22 @@ import { Auth0ClientOptions, AuthorizationParams, AuthorizeOptions, ClientAuthor
|
|
|
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 GET_TOKEN_FROM_IFRAME_LOCK_KEY = "auth0.lock.getTokenFromIFrame";
|
|
7
11
|
/**
|
|
8
12
|
* @ignore
|
|
9
13
|
*/
|
|
10
14
|
export declare const buildGetTokenSilentlyLockKey: (clientId: string, audience: string) => string;
|
|
15
|
+
/**
|
|
16
|
+
* @ignore
|
|
17
|
+
* Builds a global lock key for iframe-based authentication flows.
|
|
18
|
+
* This ensures only one iframe authorization request runs at a time per client,
|
|
19
|
+
* preventing "Invalid state" errors from concurrent iframe requests overwriting
|
|
20
|
+
* each other's state in the Auth0 session.
|
|
21
|
+
*/
|
|
22
|
+
export declare const buildIframeLockKey: (clientId: string) => string;
|
|
11
23
|
/**
|
|
12
24
|
* @ignore
|
|
13
25
|
*/
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "2.11.
|
|
1
|
+
declare const _default: "2.11.3";
|
|
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.11.
|
|
6
|
+
"version": "2.11.3",
|
|
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",
|
package/src/Auth0Client.ts
CHANGED
|
@@ -98,6 +98,7 @@ import {
|
|
|
98
98
|
cacheFactory,
|
|
99
99
|
getAuthorizeParams,
|
|
100
100
|
buildGetTokenSilentlyLockKey,
|
|
101
|
+
buildIframeLockKey,
|
|
101
102
|
OLD_IS_AUTHENTICATED_COOKIE_NAME,
|
|
102
103
|
patchOpenUrlWithOnRedirect,
|
|
103
104
|
getScopeToRequest,
|
|
@@ -1023,87 +1024,105 @@ export class Auth0Client {
|
|
|
1023
1024
|
authorizationParams: AuthorizationParams & { scope: string };
|
|
1024
1025
|
}
|
|
1025
1026
|
): Promise<GetTokenSilentlyResult> {
|
|
1026
|
-
const
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1027
|
+
const iframeLockKey = buildIframeLockKey(this.options.clientId);
|
|
1028
|
+
|
|
1029
|
+
// Acquire global iframe lock to serialize iframe authorization flows.
|
|
1030
|
+
// This is necessary because the SDK does not support multiple simultaneous transactions.
|
|
1031
|
+
// Since https://github.com/auth0/auth0-spa-js/pull/1408, when calling
|
|
1032
|
+
// `getTokenSilently()`, the global locking will lock per `audience` instead of locking
|
|
1033
|
+
// only per `client_id`.
|
|
1034
|
+
// This means that calls for different audiences would happen in parallel, which does
|
|
1035
|
+
// not work when using silent authentication (prompt=none) from within the SDK, as that
|
|
1036
|
+
// relies on the same transaction context as a top-level `loginWithRedirect`.
|
|
1037
|
+
// To resolve that, we add a second-level locking that locks only the iframe calls in
|
|
1038
|
+
// the same way as was done before https://github.com/auth0/auth0-spa-js/pull/1408.
|
|
1039
|
+
if (await retryPromise(() => lock.acquireLock(iframeLockKey, 5000), 10)) {
|
|
1040
|
+
try {
|
|
1041
|
+
const params: AuthorizationParams & { scope: string } = {
|
|
1042
|
+
...options.authorizationParams,
|
|
1043
|
+
prompt: 'none'
|
|
1044
|
+
};
|
|
1032
1045
|
|
|
1033
|
-
|
|
1034
|
-
params.organization = orgHint;
|
|
1035
|
-
}
|
|
1046
|
+
const orgHint = this.cookieStorage.get<string>(this.orgHintCookieName);
|
|
1036
1047
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
nonce: nonceIn,
|
|
1041
|
-
code_verifier,
|
|
1042
|
-
redirect_uri,
|
|
1043
|
-
scope,
|
|
1044
|
-
audience
|
|
1045
|
-
} = await this._prepareAuthorizeUrl(
|
|
1046
|
-
params,
|
|
1047
|
-
{ response_mode: 'web_message' },
|
|
1048
|
-
window.location.origin
|
|
1049
|
-
);
|
|
1048
|
+
if (orgHint && !params.organization) {
|
|
1049
|
+
params.organization = orgHint;
|
|
1050
|
+
}
|
|
1050
1051
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1052
|
+
const {
|
|
1053
|
+
url,
|
|
1054
|
+
state: stateIn,
|
|
1055
|
+
nonce: nonceIn,
|
|
1056
|
+
code_verifier,
|
|
1057
|
+
redirect_uri,
|
|
1058
|
+
scope,
|
|
1059
|
+
audience
|
|
1060
|
+
} = await this._prepareAuthorizeUrl(
|
|
1061
|
+
params,
|
|
1062
|
+
{ response_mode: 'web_message' },
|
|
1063
|
+
window.location.origin
|
|
1059
1064
|
);
|
|
1060
|
-
}
|
|
1061
1065
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1066
|
+
// When a browser is running in a Cross-Origin Isolated context, using iframes is not possible.
|
|
1067
|
+
// It doesn't throw an error but times out instead, so we should exit early and inform the user about the reason.
|
|
1068
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/crossOriginIsolated
|
|
1069
|
+
if ((window as any).crossOriginIsolated) {
|
|
1070
|
+
throw new GenericError(
|
|
1071
|
+
'login_required',
|
|
1072
|
+
'The application is running in a Cross-Origin Isolated context, silently retrieving a token without refresh token is not possible.'
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1064
1075
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
try {
|
|
1068
|
-
eventOrigin = new URL(this.domainUrl).origin;
|
|
1069
|
-
} catch {
|
|
1070
|
-
eventOrigin = this.domainUrl;
|
|
1071
|
-
}
|
|
1076
|
+
const authorizeTimeout =
|
|
1077
|
+
options.timeoutInSeconds || this.options.authorizeTimeoutInSeconds;
|
|
1072
1078
|
|
|
1073
|
-
|
|
1079
|
+
// Extract origin from domainUrl, fallback to domainUrl if URL parsing fails
|
|
1080
|
+
let eventOrigin: string;
|
|
1081
|
+
try {
|
|
1082
|
+
eventOrigin = new URL(this.domainUrl).origin;
|
|
1083
|
+
} catch {
|
|
1084
|
+
eventOrigin = this.domainUrl;
|
|
1085
|
+
}
|
|
1074
1086
|
|
|
1075
|
-
|
|
1076
|
-
throw new GenericError('state_mismatch', 'Invalid state');
|
|
1077
|
-
}
|
|
1087
|
+
const codeResult = await runIframe(url, eventOrigin, authorizeTimeout);
|
|
1078
1088
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
...options.authorizationParams,
|
|
1082
|
-
code_verifier,
|
|
1083
|
-
code: codeResult.code as string,
|
|
1084
|
-
grant_type: 'authorization_code',
|
|
1085
|
-
redirect_uri,
|
|
1086
|
-
timeout: options.authorizationParams.timeout || this.httpTimeoutMs
|
|
1087
|
-
},
|
|
1088
|
-
{
|
|
1089
|
-
nonceIn,
|
|
1090
|
-
organization: params.organization
|
|
1089
|
+
if (stateIn !== codeResult.state) {
|
|
1090
|
+
throw new GenericError('state_mismatch', 'Invalid state');
|
|
1091
1091
|
}
|
|
1092
|
-
);
|
|
1093
1092
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1093
|
+
const tokenResult = await this._requestToken(
|
|
1094
|
+
{
|
|
1095
|
+
...options.authorizationParams,
|
|
1096
|
+
code_verifier,
|
|
1097
|
+
code: codeResult.code as string,
|
|
1098
|
+
grant_type: 'authorization_code',
|
|
1099
|
+
redirect_uri,
|
|
1100
|
+
timeout: options.authorizationParams.timeout || this.httpTimeoutMs
|
|
1101
|
+
},
|
|
1102
|
+
{
|
|
1103
|
+
nonceIn,
|
|
1104
|
+
organization: params.organization
|
|
1105
|
+
}
|
|
1106
|
+
);
|
|
1107
|
+
|
|
1108
|
+
return {
|
|
1109
|
+
...tokenResult,
|
|
1110
|
+
scope: scope,
|
|
1111
|
+
oauthTokenScope: tokenResult.scope,
|
|
1112
|
+
audience: audience
|
|
1113
|
+
};
|
|
1114
|
+
} catch (e) {
|
|
1115
|
+
if (e.error === 'login_required') {
|
|
1116
|
+
this.logout({
|
|
1117
|
+
openUrl: false
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
throw e;
|
|
1121
|
+
} finally {
|
|
1122
|
+
await lock.releaseLock(iframeLockKey);
|
|
1105
1123
|
}
|
|
1106
|
-
|
|
1124
|
+
} else {
|
|
1125
|
+
throw new TimeoutError();
|
|
1107
1126
|
}
|
|
1108
1127
|
}
|
|
1109
1128
|
|
|
@@ -1169,7 +1188,7 @@ export class Auth0Client {
|
|
|
1169
1188
|
|
|
1170
1189
|
// If is refreshed with MRRT, we update all entries that have the old
|
|
1171
1190
|
// refresh_token with the new one if the server responded with one
|
|
1172
|
-
if (tokenResult.refresh_token &&
|
|
1191
|
+
if (tokenResult.refresh_token && cache?.refresh_token) {
|
|
1173
1192
|
await this.cacheManager.updateEntry(
|
|
1174
1193
|
cache.refresh_token,
|
|
1175
1194
|
tokenResult.refresh_token
|
package/src/Auth0Client.utils.ts
CHANGED
|
@@ -13,6 +13,11 @@ import { scopesToRequest } from './scope';
|
|
|
13
13
|
*/
|
|
14
14
|
export const GET_TOKEN_SILENTLY_LOCK_KEY = 'auth0.lock.getTokenSilently';
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @ignore
|
|
18
|
+
*/
|
|
19
|
+
export const GET_TOKEN_FROM_IFRAME_LOCK_KEY = 'auth0.lock.getTokenFromIFrame';
|
|
20
|
+
|
|
16
21
|
/**
|
|
17
22
|
* @ignore
|
|
18
23
|
*/
|
|
@@ -21,6 +26,16 @@ export const buildGetTokenSilentlyLockKey = (
|
|
|
21
26
|
audience: string
|
|
22
27
|
) => `${GET_TOKEN_SILENTLY_LOCK_KEY}.${clientId}.${audience}`;
|
|
23
28
|
|
|
29
|
+
/**
|
|
30
|
+
* @ignore
|
|
31
|
+
* Builds a global lock key for iframe-based authentication flows.
|
|
32
|
+
* This ensures only one iframe authorization request runs at a time per client,
|
|
33
|
+
* preventing "Invalid state" errors from concurrent iframe requests overwriting
|
|
34
|
+
* each other's state in the Auth0 session.
|
|
35
|
+
*/
|
|
36
|
+
export const buildIframeLockKey = (clientId: string) =>
|
|
37
|
+
`${GET_TOKEN_FROM_IFRAME_LOCK_KEY}.${clientId}`;
|
|
38
|
+
|
|
24
39
|
/**
|
|
25
40
|
* @ignore
|
|
26
41
|
*/
|
|
@@ -91,7 +91,7 @@ export class CacheManager {
|
|
|
91
91
|
// To refresh using MRRT we need to send a request to the server
|
|
92
92
|
// If cacheMode is 'cache-only', this will make us unable to call the server
|
|
93
93
|
// so it won't be needed to find a valid refresh token
|
|
94
|
-
if (!
|
|
94
|
+
if (!wrappedEntry && useMrrt && cacheMode !== 'cache-only') {
|
|
95
95
|
return this.getEntryWithRefreshToken(cacheKey, keys);
|
|
96
96
|
}
|
|
97
97
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default '2.11.
|
|
1
|
+
export default '2.11.3';
|