AzureAD / microsoft-identity-web

Helps creating protected web apps and web APIs with Microsoft identity platform and Azure AD B2C
MIT License
684 stars 217 forks source link

"The cache contains multiple tokens" error can occur if "openid", "profile" or "offline_access" scopes are requested #2998

Open edahlberg opened 3 months ago

edahlberg commented 3 months ago

Microsoft.Identity.Web Library

Microsoft.Identity.Web.TokenAcquisition

Microsoft.Identity.Web version

3.1.0

Web app

Sign-in users and call web APIs

Web API

Protected web APIs call downstream web APIs

Token cache serialization

In-memory caches

Description

When requesting tokens for any of the scopes "openid", "profile" or "offline_access", it's possible to receive Microsoft.Identity.Client.MsalClientException: The cache contains multiple tokens satisfying the requirements. Try to clear token cache.

This happens because when getting tokens from cache, these three scopes are explicitly ignored because they're "required" scopes. Code can be seen in TokenAcquisition.cs. -> scopes.Except(_scopesRequestedByMsal). This means that a token with any scope will pass the requirements.

Reproduction steps

  1. With a configured application that can call downstream scopes, add a controller with the included code snippet below

  2. Replace the custom api scope with a real downstream api/scope the application can access.

  3. When called, the "var c" line will always throw Microsoft.Identity.Client.MsalClientException: The cache contains multiple tokens satisfying the requirements. Try to clear token cache.

Error message

Microsoft.Identity.Client.MsalClientException: 'The cache contains multiple tokens satisfying the requirements. Try to clear token cache. '

This exception was originally thrown at this call stack:
    Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(System.Threading.CancellationToken) in SilentRequest.cs
    Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync.AnonymousMethod__1() in RequestBase.cs
    Microsoft.Identity.Client.Utils.StopwatchService.MeasureCodeBlockAsync(System.Func<System.Threading.Tasks.Task>) in StopwatchService.cs
    Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(System.Threading.CancellationToken) in RequestBase.cs
    Microsoft.Identity.Client.ApiConfig.Executors.ClientApplicationBaseExecutor.ExecuteAsync(Microsoft.Identity.Client.ApiConfig.Parameters.AcquireTokenCommonParameters, Microsoft.Identity.Client.ApiConfig.Parameters.AcquireTokenSilentParameters, System.Threading.CancellationToken) in ClientApplicationBaseExecutor.cs

Id Web logs

No response

Relevant code snippets

Include this in your controller with the middle api replaced.

var tokenAcquisition = this.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
var a = await tokenAcquisition.GetAccessTokenForUserAsync(
    scopes: ["openid"],
    user: context.Principal);
var b = await tokenAcquisition.GetAccessTokenForUserAsync(
    scopes: ["api://4208e39b-8a71-4a72-9a4c-cbdfe57b9ebf/custom:scope"], // Replace this line
    user: context.Principal);
var c = await tokenAcquisition.GetAccessTokenForUserAsync(
    scopes: ["openid"],
    user: context.Principal);

Regression

No response

Expected behavior

Either the cache should return the right token, or requesting these "required" scopes should be disallowed.

bgavrilMS commented 2 months ago

TokenAcquisition always requests openid scope. What are you trying to achieve?

edahlberg commented 2 months ago

@bgavrilMS

This issue is split from https://github.com/AzureAD/microsoft-identity-web/issues/13 where people were using the "openid" scope as a workaround for that issue not being fixed. This is done in one of the official examples as well:

https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/blob/master/2-WebApp-graph-user/2-6-BFF-Proxy/CallGraphBFF/Utils/CustomCookieAuthenticationEvents.cs

In that case, if any other api scope is ever requested alongside "openid", it messes up the cache. So if you were to add a downstream API call to that example project, it would have the same issue.

I didn't report this because I'm having an issue per se, but because it's wrong behavior from the cache and you asked me to do so: https://github.com/AzureAD/microsoft-identity-web/issues/13#issuecomment-2307144273

jmprieur commented 1 month ago

@bgavrilMS what would you advise? change in MSAL?

bgavrilMS commented 1 month ago

The error is almost always indicative of a bug in MSAL. However, I do not understand if this is OBO scenario or auth_code.