Skip to content

MSAL.js with NAA does not refresh access token after claims challenge CAE #6534

@Osaibi-RivaEngine

Description

@Osaibi-RivaEngine

Provide required information needed to triage your issue

Your Environment

  • Platform [PC desktop, Mac, iOS, Office on the web]: New Outlook on windows desktop
  • Host [Excel, Word, PowerPoint, etc.]: Outlook
  • Microsoft Outlook Version: 1.2026.210.300
  • Client Version: 20260213001.06
  • WebView2 Version: 145.0.3800.65 (Stable)
  • Operating System: Windows
  • Browser (if using Office on the web):

Expected behavior

When acquireTokenSilent or acquireTokenPopup is called with a claims parameter (containing a decoded CAE claims challenge from a Graph API 401 response) in the Nested App Authentication (NAA) flow via createNestablePublicClientApplication:

  1. A new token should be issued that satisfies the claims challenge.
  2. The new token should be cached, replacing the previous stale token.
  3. Subsequent calls to acquireTokenSilent (without claims) should return the new, valid token.

Current behavior

In the NAA flow, acquireTokenSilent or acquireTokenPopup with the claims parameter does return a valid token that satisfies the claims challenge - the immediate Graph call succeeds (HTTP 200). However, MSAL does not update its cache with this new token. On subsequent calls:

  • acquireTokenSilent without claims returns the old cached token (the one that was revoked/insufficient), not the new valid token that was just acquired.
  • Graph rejects this old token again with HTTP 401 and the same claims challenge.
  • This forces the application to re-acquire with claims on every single API call, defeating the purpose of token caching entirely.

Steps to reproduce

  1. Create an Office Add-in using NAA (createNestablePublicClientApplication) with clientCapabilities: ["CP1"] and supportsNestedAppAuth: true.
  2. Register the Graph app, SPA redirect URI brk-multihub://..., and authorized Office client app IDs.
  3. Sign in via the add-in and successfully call Microsoft Graph (e.g., GET /v1.0/me) -> confirm HTTP 200.
  4. Trigger a CAE event by revoking the user's sessions in Entra ID.
  5. Call Microsoft Graph again from the add-in -> Graph returns HTTP 401 with WWW-Authenticate: Bearer ... claims="eyJhY2..." - Sometimes I have to wait for up to 15 mins for CAE to trigger.
  6. Parse and base64-decode the claims value from the header.
  7. Call acquireTokenSilent({ scopes: ["User.Read"], account, claims: decodedClaims }) -> MSAL returns a new valid token. Call Graph with it -> HTTP 200 (success).
  8. On the next call, use acquireTokenSilent({ scopes: ["User.Read"], account }) without claims -> MSAL returns the old cached token, not the valid token from step 7.
  9. Call Graph with this old token -> HTTP 401 again with the same claims challenge.
  10. The application is now forced to pass claims on every single token request to get a valid token, because MSAL never updates its cache.

Link to live example(s)

  1. Reproduction repository: https://github.com/Osaibi-RivaEngine/NAA_CAE clone and follow the setup steps below.
  2. Setup: You must create your own Graph app registration and update the client ID in the project before running:
    • Open src/auth/authConfig.ts and replace the CLIENT_ID value with your own Client ID.
    • Open manifest.json and update webApplicationInfo.id and webApplicationInfo.resource with your client ID.
    • Ensure your app registration has a SPA redirect URI brk-multihub://... and the required authorized Office client app IDs.

Provide additional details

  1. @azure/msal-browser version: 4.28.2
  2. The claims parameter is a decoded JSON string (from the base64 value in the WWW-Authenticate header), e.g.:
    {"access_token":{"nbf":{"essential":true, "value":"1771868574"}}}

Attempted mitigations - none worked

1. forceRefresh: true
We tried to set request.forceRefresh = true when a claims challenge is present, expecting MSAL to bypass the cache:

if (storedClaims) {
  request.forceRefresh = true;
}
const result = await pca.acquireTokenSilent(request);

acquireTokenSilent calls still return the old stale token from cache.

2. setActiveAccount after acquiring the valid token
After each successful acquireTokenSilent or acquireTokenPopup call with claims, we explicitly call pca.setActiveAccount(result.account) to ensure the new token is associated with the active account:

const result = await pca.acquireTokenSilent(request);
if (result.account) {
  pca.setActiveAccount(result.account);
}

This has no effect on the caching issue. The next acquireTokenSilent without claims still returns the old cached token.

Context

We are building an Outlook Desktop add-in that calls Microsoft Graph and must comply with organisation's Conditional Access policies (session revocation, location-based access, MFA step-up). We opted in to CAE via clientCapabilities: ["CP1"] and implemented full claims-challenge handling as documented. However because the NAA does not cache the new valid token after a claims challenge, every subsequent API call requires two round-trips - one that fails with a 401 and triggers the claims flow, and a second that passes the claims to get a fresh token. This does not break our application - it remains functional - but it doubles the number of network requests needed for every Graph call after a CAE event, degrading performance and user experience unnecessarily.

This is from the sample app provided in live example:

Image

Useful logs

  • Console errors - acquireTokenSilent with claims returns a valid new token (confirmed by successful Graph call). However, a subsequent acquireTokenSilent without claims returns a different JWT, the old cached token with earlier nbf/iat timestamps. Comparing the two JWTs confirms the cache was not updated.

Metadata

Metadata

Assignees

Labels

Area: authenticationIssue related to authenticationResolution: cannot reproIssue cannot be reproduced using the information provided

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions