Core Library
MSAL.js (@azure/msal-browser)
Core Library Version
5.4.0
Wrapper Library
MSAL Angular (@azure/msal-angular)
Wrapper Library Version
5.0.6
Public or Confidential Client?
Public
Description
MsalAuthenticationTemplate leaves users permanently stuck when silent iframe times out (timed_out error not treated as interaction required)
Library version
@azure/msal-react: 5.0.6
@azure/msal-browser: 5.4.0
Description
When a user's session fully expires (both access token and refresh token), acquireTokenSilent falls back to a silent iframe. If the AAD session cookie has also expired, the iframe receives no response and throws a BrowserAuthError with code timed_out.
MsalAuthenticationTemplate never recovers from this — it stores the error permanently and never triggers an interactive login. The user is stuck on every subsequent page load until they manually clear browser storage and cookies.
Steps to reproduce
- Set up
MsalAuthenticationTemplate with interactionType={InteractionType.Redirect}
- Log in successfully
- Wait until both the refresh token and AAD session cookie expire
- Reload the page
Expected: interactive redirect login is triggered
Actual: timed_out error is stored, user is permanently stuck
Root cause trace
Step 1 — useMsalAuthentication calls acquireTokenSilent when an account exists (useMsalAuthentication.ts:211):
return instance
.acquireTokenSilent(tokenRequest)
Step 2 — When the refresh token is expired, StandardController falls back to silent iframe (StandardController.ts:2073):
"Refresh token expired/invalid or CacheLookupPolicy is set to Skip,
attempting acquire token by iframe."
followed by acquireTokenBySilentIframe (line 2077).
Step 3 — The iframe (<iframe class="msalSilentIframe">) waits for a BroadcastChannel response that never arrives because the AAD session cookie is also expired → throws BrowserAuthError with code timed_out.
Step 4 — Back in useMsalAuthentication, the catch block only handles InteractionRequiredAuthError (useMsalAuthentication.ts:219):
.catch(async (e: AuthError) => {
if (e instanceof InteractionRequiredAuthError) {
return login(fallbackInteractionType, tokenRequest); // ← redirect triggered
}
// timed_out (BrowserAuthError) falls here — no fallback, user is stuck
if (mounted.current) {
setResponse([null, e]);
}
throw e;
});
timed_out is a BrowserAuthError, not InteractionRequiredAuthError, so the fallback is never reached.
Suggested fix
import { BrowserAuthError, BrowserAuthErrorCodes } from "@azure/msal-browser";
.catch(async (e: AuthError) => {
if (
e instanceof InteractionRequiredAuthError ||
(e instanceof BrowserAuthError && e.errorCode === BrowserAuthErrorCodes.timedOut)
) {
return login(fallbackInteractionType, tokenRequest);
}
if (mounted.current) {
setResponse([null, e]);
}
throw e;
});
Root cause analysis and issue description prepared with the assistance of Claude Code (Anthropic).
Workaround
Pass an errorComponent to MsalAuthenticationTemplate that detects timed_out and calls instance.loginRedirect() manually:
import { BrowserAuthError } from "@azure/msal-browser";
import { useMsal } from "@azure/msal-react";
import { useEffect } from "react";
function AuthErrorHandler({ error }) {
const { instance } = useMsal();
useEffect(() => {
if (error instanceof BrowserAuthError && error.errorCode === "timed_out") {
instance.loginRedirect({ scopes: ["User.Read"] });
}
}, [error, instance]);
return null;
}
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
authenticationRequest={{ scopes: ["User.Read"] }}
errorComponent={AuthErrorHandler}
>
<MyApp />
</MsalAuthenticationTemplate>
Error Message
"Token acquisition in iframe failed due to timeout"
And the user gets permanently stuck until they clear the cookies.
MSAL Logs
No response
Network Trace (Preferrably Fiddler)
MSAL Configuration
const msalConfig = {
auth: {
clientId: "<your-client-id>",
authority: "https://login.microsoftonline.com/<your-tenant-id>",
redirectUri: "<your-redirect-uri>",
},
cache: {
cacheLocation: "localStorage",
},
system: {
iframeBridgeTimeout: 10000, // default
},
};
Relevant Code Snippets
## Code to reproduce
// index.tsx
const msalInstance = new PublicClientApplication(msalConfig);
msalInstance.initialize().then(() => {
msalInstance.handleRedirectPromise().then((redirectResult) => {
if (redirectResult && redirectResult.account) {
msalInstance.setActiveAccount(redirectResult.account);
}
if (!msalInstance.getActiveAccount() && msalInstance.getAllAccounts().length > 0) {
msalInstance.setActiveAccount(msalInstance.getAllAccounts()[0]);
}
msalInstance.addEventCallback((event) => {
if (event.eventType === EventType.LOGIN_SUCCESS && event.payload.account) {
msalInstance.setActiveAccount(event.payload.account);
}
});
root.render(
<MsalProvider instance={msalInstance}>
<App />
</MsalProvider>
);
});
});
// App.tsx
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
authenticationRequest={{ scopes: ["User.Read"] }}
>
<MyApp />
</MsalAuthenticationTemplate>
Reproduction Steps
- log in
- wait until both the refresh token and AAD session cookie expire
- then reload the page.
Observe the timeout error and the error persists until user clears the cookies
Expected Behavior
User shall not be stuck with the error. Clearing the cookies is not something that can be required from users in this scenario.
Identity Provider
Entra ID (formerly Azure AD) / MSA
Browsers Affected (Select all that apply)
Chrome, Other
Regression
No response
Core Library
MSAL.js (@azure/msal-browser)
Core Library Version
5.4.0
Wrapper Library
MSAL Angular (@azure/msal-angular)
Wrapper Library Version
5.0.6
Public or Confidential Client?
Public
Description
MsalAuthenticationTemplateleaves users permanently stuck when silent iframe times out (timed_outerror not treated as interaction required)Library version
@azure/msal-react:5.0.6@azure/msal-browser:5.4.0Description
When a user's session fully expires (both access token and refresh token),
acquireTokenSilentfalls back to a silent iframe. If the AAD session cookie has also expired, the iframe receives no response and throws aBrowserAuthErrorwith codetimed_out.MsalAuthenticationTemplatenever recovers from this — it stores the error permanently and never triggers an interactive login. The user is stuck on every subsequent page load until they manually clear browser storage and cookies.Steps to reproduce
MsalAuthenticationTemplatewithinteractionType={InteractionType.Redirect}Expected: interactive redirect login is triggered
Actual:
timed_outerror is stored, user is permanently stuckRoot cause trace
Step 1 —
useMsalAuthenticationcallsacquireTokenSilentwhen an account exists (useMsalAuthentication.ts:211):Step 2 — When the refresh token is expired,
StandardControllerfalls back to silent iframe (StandardController.ts:2073):followed by
acquireTokenBySilentIframe(line 2077).Step 3 — The iframe (
<iframe class="msalSilentIframe">) waits for aBroadcastChannelresponse that never arrives because the AAD session cookie is also expired → throwsBrowserAuthErrorwith codetimed_out.Step 4 — Back in
useMsalAuthentication, the catch block only handlesInteractionRequiredAuthError(useMsalAuthentication.ts:219):timed_outis aBrowserAuthError, notInteractionRequiredAuthError, so the fallback is never reached.Suggested fix
Root cause analysis and issue description prepared with the assistance of Claude Code (Anthropic).
Workaround
Pass an
errorComponenttoMsalAuthenticationTemplatethat detectstimed_outand callsinstance.loginRedirect()manually:Error Message
"Token acquisition in iframe failed due to timeout"
And the user gets permanently stuck until they clear the cookies.
MSAL Logs
No response
Network Trace (Preferrably Fiddler)
MSAL Configuration
Relevant Code Snippets
Reproduction Steps
Observe the timeout error and the error persists until user clears the cookies
Expected Behavior
User shall not be stuck with the error. Clearing the cookies is not something that can be required from users in this scenario.
Identity Provider
Entra ID (formerly Azure AD) / MSA
Browsers Affected (Select all that apply)
Chrome, Other
Regression
No response