Closed DOMZE closed 1 month ago
I am getting the same behavior.
Are you not in this use case : https://github.com/AzureAD/microsoft-identity-web/wiki/Managing-incremental-consent-and-conditional-access ?
@vincentlemordanttechso no, as I mentioned, doing the OBO works using the REST endpoint, when taking the access token returned. The problem seems to be within the library (unless I'm told otherwise), as the user doesn't seem to be saved in the cache.
seems like I get it working. I continue investigating and will publish my sample app once done.
Thanks @VladislavAntonyuk
Unfortunately, this is not in a Blazor app. As per my description, this seems to be a cache problem. As the user is able to get an access token, but that access token doesn't seem to be stored in the cache and thus cannot be retrieved.
@jmprieur any idea?
@bgavrilMS can you please take a look at this cache issue? Thanks.
@jennyf19 - we can have a look, but why is Id.Web calling AcquireTokenSilent
in a web api (OBO) scenario? Can we get some help on this?
I just updated to the latest release 3.1.0 and it is no longer giving me problems.
@Norrch2 unfortunately, 3.1.0 still gives the same problem.
I don't think this is related to in-memory caching.
@DOMZE
I cannot find the code shared here:
string accessToken;
// ITokensAcquisition is scoped, so we need to create a new scope here as the handler is a singleton
using (var scope = _serviceProvider.CreateScope())
{
var a = scope.ServiceProvider.GetRequiredService<ITokenAcquisition>();
var x = await a.GetAuthenticationResultForUserAsync(_apiOptions.Scopes, user: context.User);
accessToken = x.AccessToken;
}
but the token that was validated by the web API is not known by TokenAcquisition in this scope, because TokenAcquisition is, by default, as scoped service, so you are creating a new instance of token acquisition (the one above does not have the context of the request), which you don't want to do. You can either not create the scope above, or use TokenAcquisition as a singleton by calling services.AddTokenAcquistion(true)
after the .EnableTokenAcquisitionToCallDownstreamApi()
.
one issue is here you should be using Instance
and TenantId
, not Authority
.... (Authority
is used only for CIAM applications). There was a bug fixed in the latest around this, which is probably why it now works for @Norrch2
Also, we would advise that you just do .AddMicrosoftIdentityWebApi(Configuration, "EntraId")
, instead both delegates.
Authorization should happen from the claims, not the body, as you have set up here because using the claims is secure because the token is validated.
Closing, but feel free to respond here if you have questions.
@jennyf19 thank you for taking the time to reply and giving this a look.
The code shown is in the web app and not web api, in the PermissionActionAuthorizationHandler
class.
The question that still remains in my head, why does this work in one environment where the token (access token) in the web app can be exchanged using OBO and in the other environment it cannot? Why is the logs not giving me the same results? The data in the in-memory cache (which is a singleton instance) seems different and this is where I am trying to understand. Is this due to the lifetime of the ITokenAcquision
as you mentioned because of the request information? This is where I'm puzzled because I get 2 different results of the logs/info from the cache in 2 different environments.
using the IConfiguration
to populate is great and easier indeed, the delegates was just to show what options there is to configure and a discussion around this was going to occur.
@DOMZE what are the differences in the environments?
related to:
An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.
Is it the same user that sign's in? Did the user consent, did you send the challenge back to the user to let them consent?
@henrik-me it is the same user. Taking the access token and calling the endpoint for the OBO flow with the requested scope in Entra is not giving any consent error and goes through with the OBO returning an access token for the requested API/scopes. So I don't think this is an Entra problem as I went straight to the source to verify if really consent was necessary. So the differences I'm talking about are:
Demo.App.Authorization.PermissionActionAuthorizationHandler: Warning: Evaluating authorization requirement for permission >= read
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z] ConfidentialClientApplication 35451336 created
Microsoft.Identity.Web.TokenAcquisition: Information: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z] [Internal cache] Total number of cache partitions found while getting refresh tokens: 1
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z - 903da1ed-fea8-42fe-960c-4b20d3ee8359] [GetAccounts] Found 1 RTs and 1 accounts in MSAL cache.
Microsoft.Identity.Web.TokenAcquisition: Information: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z - 903da1ed-fea8-42fe-960c-4b20d3ee8359] [Region discovery] Not using a regional authority.
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z - 903da1ed-fea8-42fe-960c-4b20d3ee8359] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? True.
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z - 903da1ed-fea8-42fe-960c-4b20d3ee8359] [GetAccounts] Found 1 RTs and 1 accounts in MSAL cache after environment filtering.
Microsoft.Identity.Web.TokenAcquisition: Information: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z - 903da1ed-fea8-42fe-960c-4b20d3ee8359] [Region discovery] Not using a regional authority.
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z - 903da1ed-fea8-42fe-960c-4b20d3ee8359] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? True.
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z - 903da1ed-fea8-42fe-960c-4b20d3ee8359] Filtered by home account id. Remaining accounts 1
Microsoft.Identity.Web.TokenAcquisition: Information: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z] Found 1 cache accounts and 0 broker accounts
Microsoft.Identity.Web.TokenAcquisition: Information: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.7 Microsoft Windows 10.0.22631 [2024-07-26 14:43:49Z] Returning 1 accounts
versus
Demo.App.Authorization.PermissionActionAuthorizationHandler: Warning: Evaluating authorization requirement for permission >= read
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.3 Microsoft Windows 10.0.19045 [2024-08-06 14:29:12Z] ConfidentialClientApplication 14303791 created
Microsoft.Identity.Web.TokenAcquisition: Information: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.3 Microsoft Windows 10.0.19045 [2024-08-06 14:29:12Z] [Internal cache] Total number of cache partitions found while getting refresh tokens: 1
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.3 Microsoft Windows 10.0.19045 [2024-08-06 14:29:12Z - 221d6e7d-91dd-4728-a5f5-c5a9af74c536] [GetAccounts] Found 0 RTs and 0 accounts in MSAL cache.
Microsoft.Identity.Web.TokenAcquisition: Information: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.3 Microsoft Windows 10.0.19045 [2024-08-06 14:29:12Z - 221d6e7d-91dd-4728-a5f5-c5a9af74c536] [Region discovery] Not using a regional authority.
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.3 Microsoft Windows 10.0.19045 [2024-08-06 14:29:12Z - 221d6e7d-91dd-4728-a5f5-c5a9af74c536] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? True.
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.3 Microsoft Windows 10.0.19045 [2024-08-06 14:29:12Z - 221d6e7d-91dd-4728-a5f5-c5a9af74c536] [GetAccounts] Found 0 RTs and 0 accounts in MSAL cache after environment filtering.
Microsoft.Identity.Web.TokenAcquisition: Debug: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.3 Microsoft Windows 10.0.19045 [2024-08-06 14:29:12Z - 221d6e7d-91dd-4728-a5f5-c5a9af74c536] Filtered by home account id. Remaining accounts 0
Microsoft.Identity.Web.TokenAcquisition: Information: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.3 Microsoft Windows 10.0.19045 [2024-08-06 14:29:12Z] Found 0 cache accounts and 0 broker accounts
Microsoft.Identity.Web.TokenAcquisition: Information: False MSAL 4.61.3.0 MSAL.NetCore .NET 8.0.3 Microsoft Windows 10.0.19045 [2024-08-06 14:29:12Z] Returning 0 accounts
What's concerning to me is the Returning 1 accounts
vs Returning 0 accounts
what is the difference in the environments?
Ok, so I had a look at the code for the web site. And I managed to repro like this:
await _tokenAcquisition.GetAuthenticationResultForUserAsync(_apiOptions.Scopes, user: context.User);
This is expected. The key is point 2 - I restarted the service which led to the deletion of the memory cache. ASP.NET core maintains cookies and remembers the user, but their access token is gone. The correct solution is to redirect the user back to the auth page.
You could use some persisted cache or you could use session cache, see https://learn.microsoft.com/en-us/entra/msal/dotnet/how-to/token-cache-serialization?tabs=aspnetcore
But you must handle this exception, because it can also occur for other reasons, such as the tenant admin adding an MFA policy.
@bgavrilMS
The problem occurs on the first try (the webserver is never restarted), i.e:
Same occurs in Incognito mode:
You mentioned using session cache, but as per @jennyf19's recommendation, ITokenAcquisitionIMsalTokenCacheProvider
is scoped lifetime based and ITokenAcquisition
is registered as a singleton as it's being using in a Singleton context (PermissionActionAuthorizationHandler
). If I create a new scope, ITokenAcquisition
won't be aware of the request context thus, not possible to use AddSessionTokenCaches
.
There's something that is failing in the MSAL cache when storing the tokens (post sign-in), thus the difference (Returning 1 accounts
vs Returning 0 accounts
)
Do you have the [AuthorizeForScopes]
attribute on your controller or controller action, @DOMZE ?
See https://github.com/AzureAD/microsoft-identity-web/wiki/Managing-incremental-consent-and-conditional-access
I have connected with the customer and we have made some progress.
Here's what's happening:
X
_tokenAcquisition(scope: Y, user: context.User)
is called and this fails with "user_null" errorNote that at step 4 we made sure that a token is in the cache from step 3. I manually checked that an account exists in MSAL's cache with that exact ID.
The CX tried this with their regular setup, which is ADFSv4 federated to AAD. And it is failing with user_null
. However, upon trying with pure AAD
accounts, this worked!
Next steps:
GetAccount
as currently I cannot figure out why the cache miss occurs.Hi @DOMZE - I updated the sample and should be able to get a few more details out. YOu should:
Then send the logs over.
@jmprieur @jennyf19 - I couldn't configure Id.Web to log MSAL pii messages. What am I doing wrong?
https://github.com/dstamand-msft/demo-authnauthz/blob/main/src/Demo.App/appsettings.json
Did you look at https://github.com/AzureAD/microsoft-identity-web/wiki/Logging ?
I did. But all examples show how to set it via config. But this sample uses code, not config, and I couldn't figure out how to set it.
That said, even in WebAppCallsGraph devapp I can see that the PII flag is always false once it reaches MSAL.
Events need to be chained, Bogdan, otherwise you lose all benefit of using IdWeb. See https://github.com/AzureAD/microsoft-identity-web/wiki/customization
Ok, I have found the problem. The IdToken
already has a claim called uid
. This conflicts with Id.Web, because Id.Web tries to inject a claim named uid
in the ClaimsPrincipal.
A solution for Id.Web is to use names like idweb_uid
and idweb_utid
(similar to how ESTS prefixes its special claims). And the modify the logic to do:
idweb_uid
/ idweb-utid
uid
/ utid
Thoughts @jmprieur ?
@DOMZE - to workaround this, could the CX configure Entra so that it doesn't add uid
claim ? I am not familiar with it, so I don't even know if it's possible.
@DOMZE - while the team thinks about how to fix this, can you see on your end if custom claim named uid
is set? Can it be removed somehow?
@bgavrilMS I did ask. Awaiting reply and see if we can test without that uid
claim
@bgavrilMS confirmed, removing the uid
claim fixed the problem.
Ok, so there are ~2~3 possible fixes:
uid
or uuid
claims already. Smth like "The id token has an optional claim named uid. Microsoft.Identity.Web uses this claim internally. Pls rename the optional claim`closing as won't fix.
Microsoft.Identity.Web version and scenario
3.0.1 Web app calls web api.
Problem description (updated by @bgavrilMS for clarity)
In a typical web app calls web api scenario, Identity.Web always throws
MicrosoftIdentityWebChallengeUserException
with error codeuser_null
when callingTokenAcqusition
API. For some application registrations, but not for all.The issue only occurs when the ID Token is customized to have an additional claim named
uid
, via the app portal. For example:Root cause: Microsoft.Identity.Web tries to inject its own
uid
claim into the id token / ClaimsPrincipal (which it gets from client_info). This conflicts with the user'suid
claim and the user's claim wins. This breaks Microsoft.Identity.Web's ability to reference a user from MSAL's cache, causing theuser_null
error.Original Description
I'm having trouble with another user with the OBO flow using Microsoft.Identity.Web (MIW). The code works on my machine and in my environment (Azure). However, the exact same code does not work the other users' machine & environment. The log on the other users' environment confirms that the tokens are being saved in the cache (In Memory Cache).
I can confirm that the
ClaimsPrincipal
is properly populated in both environment (as shown when signing in).App Registrations have been verified on both end (mine and the other users). The token can be exchanged with the proper requested scope for the requested application using REST calls, thus not a problem with App Registrations. The admin consent has been granted and I am not using the ./default scope but rather requesting a specific scope i.e
api://<app_name>/scope_name
Enabling the logs for the library, I realize that in my environment the account is saved AND is retrieved when doing the OBO call using
ITokenAcquisition.GetAuthenticationResultForUserAsync
. In the other users' environment, this gives the error:The logs says that the account in the cache is not found (0 account in cache) and thus why this fails. See logs below
Reproduction steps
PermissionActionAuthorizationHandler
fileError message
Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent. ---> MSAL.NetCore.4.61.3.0.MsalUiRequiredException: ErrorCode: user_null Microsoft.Identity.Client.MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.
Id Web logs
Own environment:
Other users environment:
Relevant code snippets
Regression
No response
Expected behavior
Expected the access token to be in the cache for the other users