Skip to content

Commit d43d034

Browse files
fix: add two-tier locking to prevent iframe race conditions
- Add global iframe lock to serialize iframe authorization requests - Keep per-audience locking for refresh token flows (preserves MRRT performance) - Fixes 'Invalid state' errors when multiple audiences requested in parallel - Resolves regression introduced in v2.8.0 by PR #1408 Two-tier locking strategy: - Outer lock (per-audience): Prevents duplicate calls for same audience - Inner lock (global iframe): Prevents Auth0 session state conflicts Refresh token flows bypass the inner lock and remain fully parallel. Iframe flows acquire both locks to ensure correctness.
1 parent 506894f commit d43d034

File tree

2 files changed

+46
-21
lines changed

2 files changed

+46
-21
lines changed

src/Auth0Client.ts

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ import {
9898
cacheFactory,
9999
getAuthorizeParams,
100100
buildGetTokenSilentlyLockKey,
101+
buildIframeLockKey,
101102
OLD_IS_AUTHENTICATED_COOKIE_NAME,
102103
patchOpenUrlWithOnRedirect,
103104
getScopeToRequest,
@@ -1023,32 +1024,44 @@ export class Auth0Client {
10231024
authorizationParams: AuthorizationParams & { scope: string };
10241025
}
10251026
): Promise<GetTokenSilentlyResult> {
1026-
const params: AuthorizationParams & { scope: string } = {
1027-
...options.authorizationParams,
1028-
prompt: 'none'
1029-
};
1027+
const iframeLockKey = buildIframeLockKey(this.options.clientId);
10301028

1031-
const orgHint = this.cookieStorage.get<string>(this.orgHintCookieName);
1029+
// Acquire global iframe lock to serialize iframe authorization flows
1030+
const lockAcquired = await retryPromise(
1031+
() => lock.acquireLock(iframeLockKey, 5000),
1032+
10
1033+
);
10321034

1033-
if (orgHint && !params.organization) {
1034-
params.organization = orgHint;
1035+
if (!lockAcquired) {
1036+
throw new TimeoutError();
10351037
}
10361038

1037-
const {
1038-
url,
1039-
state: stateIn,
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-
);
1050-
10511039
try {
1040+
const params: AuthorizationParams & { scope: string } = {
1041+
...options.authorizationParams,
1042+
prompt: 'none'
1043+
};
1044+
1045+
const orgHint = this.cookieStorage.get<string>(this.orgHintCookieName);
1046+
1047+
if (orgHint && !params.organization) {
1048+
params.organization = orgHint;
1049+
}
1050+
1051+
const {
1052+
url,
1053+
state: stateIn,
1054+
nonce: nonceIn,
1055+
code_verifier,
1056+
redirect_uri,
1057+
scope,
1058+
audience
1059+
} = await this._prepareAuthorizeUrl(
1060+
params,
1061+
{ response_mode: 'web_message' },
1062+
window.location.origin
1063+
);
1064+
10521065
// When a browser is running in a Cross-Origin Isolated context, using iframes is not possible.
10531066
// It doesn't throw an error but times out instead, so we should exit early and inform the user about the reason.
10541067
// https://developer.mozilla.org/en-US/docs/Web/API/crossOriginIsolated
@@ -1104,6 +1117,8 @@ export class Auth0Client {
11041117
});
11051118
}
11061119
throw e;
1120+
} finally {
1121+
await lock.releaseLock(iframeLockKey);
11071122
}
11081123
}
11091124

src/Auth0Client.utils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ export const buildGetTokenSilentlyLockKey = (
2121
audience: string
2222
) => `${GET_TOKEN_SILENTLY_LOCK_KEY}.${clientId}.${audience}`;
2323

24+
/**
25+
* @ignore
26+
* Builds a global lock key for iframe-based authentication flows.
27+
* This ensures only one iframe authorization request runs at a time per client,
28+
* preventing "Invalid state" errors from concurrent iframe requests overwriting
29+
* each other's state in the Auth0 session.
30+
*/
31+
export const buildIframeLockKey = (clientId: string) =>
32+
`auth0.lock.iframe.${clientId}`;
33+
2434
/**
2535
* @ignore
2636
*/

0 commit comments

Comments
 (0)