Skip to content

Silent token refresh fails for federated CIAM users – wrong authority used for refresh #2871

@jsjohann

Description

@jsjohann

Copilot Usage Confirmation

  • I have tried using GitHub Copilot to resolve this issue.

MSAL Version

2.8.1

Description

When using MSALCIAMAuthority with federated identity providers (e.g. users from another Microsoft Entra tenant signing in via external IdP), silent token acquisition fails after the first interactive sign-in. The library sends the refresh token to the wrong authority URL, causing AADSTS500210 (domain/tenant mismatch).

In the logs, MSAL shows:

Request authority cache look up for https://< TENANT>.ciamlogin.com/<CIAM_TENANT_ID>, using https://< TENANT>.ciamlogin.com/<IDP_TENANT_ID> instead."

Root cause: MSAL/MSID uses the wrong authority for the refresh request. The tokens show that the id_token’s tid is correct (CIAM tenant <CIAM_TENANT_ID>), but the idp claim points to the federated IdP (sts.windows.net/<IDP_TENANT_ID>). The cached authority/realm appears to be derived from the IdP (<IDP_TENANT_ID>) instead of the request authority or token tid, causing the refresh to be sent to the wrong endpoint.

Error Details

  • Error code: AADSTS500210
  • Error message: Domain name does not match with the tenant identifier. Trace ID: <TRACE_ID> Correlation ID: <CORRELATION_ID> Timestamp: …

The refresh token is issued by CIAM and must be sent to the CIAM token endpoint. The library sends it to an authority that uses the IdP tenant (<IDP_TENANT_ID>) instead of the CIAM tenant (<CIAM_TENANT_ID>), which the server rejects.

MSAL Logs

Federated (failing) – key log lines:

(Default cache) Saving token ... for userID ... with environment <TENANT>.ciamlogin.com, realm <IDP_TENANT_ID>, clientID <CLIENT_ID>

Request authority cache look up for https://<TENANT>.ciamlogin.com/<CIAM_TENANT_ID>, using https://<TENANT>.ciamlogin.com/<IDP_TENANT_ID> instead

(Default accessor) Finding token with ... authority https://<TENANT>.ciamlogin.com/<IDP_TENANT_ID>

Trying to acquire access token using App Refresh Token for clientId https://<TENANT>.ciamlogin.com/<IDP_TENANT_ID> ...

Creating Error with description: AADSTS500210: Domain name does not match with the tenant identifier Trace ID: <TRACE_ID> Correlation ID: <CORRELATION_ID> Timestamp: 2026-02-12 08:31:22Z

Silent flow finished. Result (null), error: -51115 error domain: MSIDErrorDomain

CIAM native (working) – key log lines:

(Default cache) Saving token ... for userID ... with environment <TENANT>.ciamlogin.com, realm <CIAM_TENANT_ID>, clientID <CLIENT_ID>

Request authority cache look up for https://<TENANT>.ciamlogin.com/<CIAM_TENANT_ID>, using https://<TENANT>.ciamlogin.com/<CIAM_TENANT_ID> instead

(Default accessor) Finding token with ... authority https://<TENANT>.ciamlogin.com/<CIAM_TENANT_ID>

Trying to acquire access token using App Refresh Token for clientId https://<TENANT>.ciamlogin.com/<CIAM_TENANT_ID> ...

Silent flow finished. Result (not-null), error: 0 error domain: (null)

Log comparison – federated (failing) vs CIAM native (working):

Step Federated (failing) CIAM native (working)
Interactive – saving tokens realm <IDP_TENANT_ID> realm <CIAM_TENANT_ID>
Request authority cache look up using .../<IDP_TENANT_ID> instead using .../<CIAM_TENANT_ID> instead (no swap)
Refresh token request Finding token ... authority https://.../<IDP_TENANT_ID> Finding token ... authority https://.../<CIAM_TENANT_ID>
Trying to acquire using App Refresh Token https://.../<IDP_TENANT_ID> → AADSTS500210 https://.../<CIAM_TENANT_ID> → success

Credential cache entries show realm set to the federated IdP tenant (<IDP_TENANT_ID>) instead of the CIAM tenant (<CIAM_TENANT_ID>), even though the id_token’s tid claim is correct (see tokens below).

Note: Even when the app explicitly passes authority on MSALSilentTokenParameters (CIAM tenant), MSAL internally overrides it and uses the account's realm (IdP tenant for federated) for the refresh request.

ID Token Federated (failing case)

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "[REDACTED]"
}.{
  "aud": "<CLIENT_ID>",
  "iss": "https://<CIAM_TENANT_ID>.ciamlogin.com/<CIAM_TENANT_ID>/v2.0",
  "iat": 1770884710,
  "nbf": 1770884710,
  "exp": 1770888610,
  "idp": "https://sts.windows.net/<IDP_TENANT_ID>/",
  "name": "[REDACTED]",
  "oid": "<OID_FEDERATED>",
  "preferred_username": "<REDACTED>",
  "rh": "[REDACTED]",
  "sid": "[REDACTED]",
  "sub": "[REDACTED]",
  "tid": "<CIAM_TENANT_ID>",
  "uti": "[REDACTED]",
  "ver": "2.0"
}.[Signature]

Note: The id_token has tid: <CIAM_TENANT_ID> (CIAM tenant, correct) and idp: https://sts.windows.net/<IDP_TENANT_ID>/ (IdP). Despite correct tid, silent refresh fails with AADSTS500210, suggesting the wrong authority comes from somewhere other than tid (e.g. idp or account metadata).

Access Token Federated

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "[REDACTED]"
}.{
  "aud": "<CLIENT_ID>",
  "iss": "https://<CIAM_TENANT_ID>.ciamlogin.com/<CIAM_TENANT_ID>/v2.0",
  "iat": 1770884710,
  "nbf": 1770884710,
  "exp": 1770889829,
  "aio": "[REDACTED]",
  "azp": "<CLIENT_ID>",
  "azpacr": "0",
  "idp": "https://sts.windows.net/<IDP_TENANT_ID>/",
  "name": "[REDACTED]",
  "oid": "<OID_FEDERATED>",
  "preferred_username": "<REDACTED>",
  "rh": "[REDACTED]",
  "scp": "User.Read",
  "sid": "[REDACTED]",
  "sub": "[REDACTED]",
  "tid": "<CIAM_TENANT_ID>",
  "uti": "[REDACTED]",
  "ver": "2.0",
  "xms_ftd": "[REDACTED]"
}.[Signature]

ID Token CIAM

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "[REDACTED]"
}.{
  "aud": "<CLIENT_ID>",
  "iss": "https://<CIAM_TENANT_ID>.ciamlogin.com/<CIAM_TENANT_ID>/v2.0",
  "iat": 1770884900,
  "nbf": 1770884900,
  "exp": 1770888800,
  "name": "[REDACTED]",
  "oid": "<OID_CIAM>",
  "preferred_username": "<REDACTED>",
  "rh": "[REDACTED]",
  "sid": "[REDACTED]",
  "sub": "[REDACTED]",
  "tid": "<CIAM_TENANT_ID>",
  "uti": "[REDACTED]",
  "ver": "2.0"
}.[Signature]

Access Token CIAM

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "[REDACTED]"
}.{
  "aud": "<CLIENT_ID>",
  "iss": "https://<CIAM_TENANT_ID>.ciamlogin.com/<CIAM_TENANT_ID>/v2.0",
  "iat": 1770884900,
  "nbf": 1770884900,
  "exp": 1770890052,
  "aio": "[REDACTED]",
  "azp": "<CLIENT_ID>",
  "azpacr": "0",
  "name": "[REDACTED]",
  "oid": "<OID_CIAM>",
  "preferred_username": "<REDACTED>",
  "rh": "[REDACTED]",
  "scp": "User.Read",
  "sid": "[REDACTED]",
  "sub": "[REDACTED]",
  "tid": "<CIAM_TENANT_ID>",
  "uti": "[REDACTED]",
  "ver": "2.0",
  "xms_ftd": "[REDACTED]"
}.[Signature]

Reproduction Steps

  1. Configure a CIAM app with MSALCIAMAuthority (e.g. https://<TENANT>.ciamlogin.com/<CIAM_TENANT_ID>).
  2. Add an external identity provider (e.g. another Microsoft Entra tenant).
  3. Sign in interactively with a user from that federated IdP.
  4. Confirm both access token and refresh token are received.
  5. Wait for the access token to expire or trigger a silent refresh.
  6. Call acquireTokenSilent (or rely on automatic refresh).

Result: Silent refresh fails with AADSTS500210.

Expected Behavior

Silent token refresh should succeed for federated users. The refresh token is issued by CIAM, so the refresh request should always go to the request authority (CIAM tenant <CIAM_TENANT_ID>), not an authority derived from the idp claim (IdP tenant <IDP_TENANT_ID>).

Regression

No response

Screenshots & Screen Recordings

No response

Additional context

Technical details:

  • The id_token for federated users has tid: <CIAM_TENANT_ID> (CIAM tenant, correct) and idp: https://sts.windows.net/<IDP_TENANT_ID>/ (federated IdP).
  • Despite correct tid, the refresh request is sent to an authority using tenant <IDP_TENANT_ID> (IdP tenant), causing AADSTS500210.
  • Root cause: The account identifier is {uid}.{utid} from client_info in the token response. MSIDClientInfo parses uid and utid from the base64-decoded client_info JSON. MSIDCIAMOauth2Factory resultAuthorityWithConfiguration:tokenResponse: (IdentityCore) builds the cache authority using response.clientInfo.utid as rawTenant – for CIAM the library never uses id_token.tid for the authority. For federated users, the server returns utid = IdP tenant (<IDP_TENANT_ID>), so the cache authority and realm are wrong. MSALCIAMOauth2Provider issuerAuthorityWithAccount returns this cached authority via getAuthorityURL:homeAccountId:....
  • CIAM native vs federated: For CIAM-native users (no idp claim), client_info returns utid = CIAM tenant (<CIAM_TENANT_ID>), so silent refresh works. For federated users, utid = IdP tenant (<IDP_TENANT_ID>), so silent refresh fails.
  • Contrast with AAD: MSIDAADOauth2Factory uses response.idTokenObj.realm (maps to tid) for the authority; CIAM uses client_info.utid instead.
  • The refresh token is issued by CIAM and must be sent to the CIAM token endpoint.

Suggested fix:

For CIAM flows, always use the request authority (or the issuer from the token response) for caching and refresh. Do not derive the refresh authority from the idp claim or the account’s home tenant when it would point to a different authority than the one that issued the token. When tid in the id_token ≠ utid in client_info (federated case), use the request authority or id_token.tid for caching and refresh instead of client_info.utid.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions