Skip to content

Title: MsalAuthenticationTemplate leaves users permanently stuck when silent iframe times out (timed_out error not treated as interaction required) #8434

@akulyakhtin

Description

@akulyakhtin

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

  1. Set up MsalAuthenticationTemplate with interactionType={InteractionType.Redirect}
  2. Log in successfully
  3. Wait until both the refresh token and AAD session cookie expire
  4. Reload the page

Expected: interactive redirect login is triggered
Actual: timed_out error is stored, user is permanently stuck

Root cause trace

Step 1useMsalAuthentication 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)

  • Sent
  • Pending

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

  1. log in
  2. wait until both the refresh token and AAD session cookie expire
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug-unconfirmedA reported bug that needs to be investigated and confirmedmsal-angularRelated to @azure/msal-angular packagemsal-browserRelated to msal-browser packagepublic-clientIssues regarding PublicClientApplicationsquestionCustomer is asking for a clarification, use case or information.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions