diff --git a/.changeset/fix-refresh-token-loop.md b/.changeset/fix-refresh-token-loop.md new file mode 100644 index 000000000..4b39e25ab --- /dev/null +++ b/.changeset/fix-refresh-token-loop.md @@ -0,0 +1,6 @@ +--- +'@asgardeo/browser': patch +'@asgardeo/react': patch +--- + +Fix infinite loop of refresh grant requests when the refresh token is expired or revoked diff --git a/packages/browser/src/__legacy__/helpers/authentication-helper.ts b/packages/browser/src/__legacy__/helpers/authentication-helper.ts index 995f80799..c2592383b 100644 --- a/packages/browser/src/__legacy__/helpers/authentication-helper.ts +++ b/packages/browser/src/__legacy__/helpers/authentication-helper.ts @@ -65,6 +65,7 @@ export class AuthenticationHelper; protected _instanceId: number; protected _isTokenRefreshing: boolean; + protected _hasRefreshFailed: boolean; public constructor(authClient: AsgardeoAuthClient, spaHelper: SPAHelper) { this._authenticationClient = authClient; @@ -72,6 +73,11 @@ export class AuthenticationHelper { + this._hasRefreshFailed = false; // Disable this temporarily /* if (config.storage === Storage.BrowserMemory) { SPAUtils.setSignOutURL(await _authenticationClient.getSignOutUrl()); @@ -717,6 +725,13 @@ export class AuthenticationHelper if (timeUntilRefresh <= 0) { if (this._isTokenRefreshLoading) return; + if (authenticationHelper.hasRefreshFailed()) return; this._isTokenRefreshLoading = true; try { diff --git a/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx b/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx index fe0f132d5..761b99ac2 100644 --- a/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx +++ b/packages/react/src/contexts/Asgardeo/AsgardeoProvider.tsx @@ -388,6 +388,23 @@ const AsgardeoProvider: FC> = ({ }; }, [asgardeo]); + // When the refresh token is expired or revoked, the browser SDK posts a + // 'refresh-access-token-error' message. Sync the React context so components + // stop calling auth methods and the user is redirected to sign in again. + useEffect(() => { + const handleRefreshTokenError = (event: MessageEvent): void => { + if (event?.data?.type === 'refresh-access-token-error') { + setIsSignedInSync(false); + } + }; + + window.addEventListener('message', handleRefreshTokenError); + + return (): void => { + window.removeEventListener('message', handleRefreshTokenError); + }; + }, []); + useEffect(() => { (async (): Promise => { try {