microsoft-identity-web: IDW10502: An MsalUiRequiredException

Microsoft.Identity.Web Library

Microsoft.Identity.Web

Microsoft.Identity.Web version

1.25.1

Web app

Sign-in users and call web APIs

Web API

Not Applicable

Token cache serialization

Distributed caches

Description

To me, it seems to be a bug that var res = await _api.CallWebApiForUserAsync(ServiceName, options => { options.RelativePath = "/api/getsomething"; //options.Scope = config.Scope //injected via config }, user: user); throws “IDW10502: An MsalUiRequiredException => inner: An error occured during token acquisition: No account or login hint was passed to the AcquireTokenSilent call” while at the same time this works: var accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new string[] { config.Scope }, user: user);

Reproduction steps

  1. Setup AddMicrosoftIdentityWebApp(b2cConfig) .EnableTokenAcquisitionToCallDownstreamApi(new string[] { config.Scope }) .AddDownstreamWebApi(“MyApi”, builder.Configuration.GetSection(“MyApi”)) .AddDistributedTokenCaches();//I do use redis
  2. Within Oidc’s OnTokenValidated event call -tokenAcquisition.GetAccessTokenForUserAsync //success -_api.CallWebApiForUserAsync both with “user: context.Principal”

Error message

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.35.1.0.MsalUiRequiredException: ErrorCode: user_null Microsoft.Identity.Client.MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.

Id Web logs

fail: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[17] Exception occurred while processing 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.35.1.0.MsalUiRequiredException: ErrorCode: user_null Microsoft.Identity.Client.MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call. at Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.ApiConfig.Executors.ClientApplicationBaseExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenSilentParameters silentParameters, CancellationToken cancellationToken) at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForWebAppWithAccountFromCacheAsync(IConfidentialClientApplication application, ClaimsPrincipal claimsPrincipal, IEnumerable1 scopes, String authority, MergedOptions mergedOptions, String userFlow, TokenAcquisitionOptions tokenAcquisitionOptions) at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable1 scopes, String authenticationScheme, String tenantId, String userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions) StatusCode: 0 ResponseBody:
Headers: — End of inner exception stack trace — at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable1 scopes, String authenticationScheme, String tenantId, String userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions) at Microsoft.Identity.Web.DownstreamWebApi.CallWebApiForUserAsync(String serviceName, String authenticationScheme, Action1 calledDownstreamWebApiOptionsOverride, ClaimsPrincipal user, StringContent content) at STPv2.Infrastructure.UrmApi.Me(ClaimsPrincipal user) in C:\Users\mlproe\source\repos\STPv2\STPv2\Infrastructure\UrmApi.cs:line 24 at STPv2.Program.UrmAuthentication(TokenValidatedContext ctx) in C:\Users\mlproe\source\repos\STPv2\STPv2\Program.cs:line 154 at STPv2.Program.<>c.<<ConfigureServices>b__2_7>d.MoveNext() in C:\Users\mlproe\source\repos\STPv2\STPv2\Program.cs:line 103 — End of stack trace from previous location — at Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilder.<>c__DisplayClass11_1.<<WebAppCallsWebApiImplementation>b__2>d.MoveNext() — End of stack trace from previous location — at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.RunTokenValidatedEventAsync(OpenIdConnectMessage authorizationResponse, OpenIdConnectMessage tokenEndpointResponse, ClaimsPrincipal user, AuthenticationProperties properties, JwtSecurityToken jwt, String nonce) at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandleRemoteAuthenticateAsync() Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler: Error: Exception occurred while processing 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.35.1.0.MsalUiRequiredException: ErrorCode: user_null Microsoft.Identity.Client.MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call. at Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken) at Microsoft.Identity.Client.ApiConfig.Executors.ClientApplicationBaseExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenSilentParameters silentParameters, CancellationToken cancellationToken) at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForWebAppWithAccountFromCacheAsync(IConfidentialClientApplication application, ClaimsPrincipal claimsPrincipal, IEnumerable1 scopes, String authority, MergedOptions mergedOptions, String userFlow, TokenAcquisitionOptions tokenAcquisitionOptions) at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable1 scopes, String authenticationScheme, String tenantId, String userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions) StatusCode: 0 ResponseBody:
Headers: — End of inner exception stack trace — at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable1 scopes, String authenticationScheme, String tenantId, String userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions) at Microsoft.Identity.Web.DownstreamWebApi.CallWebApiForUserAsync(String serviceName, String authenticationScheme, Action1 calledDownstreamWebApiOptionsOverride, ClaimsPrincipal user, StringContent content) at STPv2.Infrastructure.UrmApi.Me(ClaimsPrincipal user) in C:\Users\mlproe\source\repos\STPv2\STPv2\Infrastructure\UrmApi.cs:line 24 at STPv2.Program.UrmAuthentication(TokenValidatedContext ctx) in C:\Users\mlproe\source\repos\STPv2\STPv2\Program.cs:line 154 at STPv2.Program.<>c.<<ConfigureServices>b__2_7>d.MoveNext() in C:\Users\mlproe\source\repos\STPv2\STPv2\Program.cs:line 103 — End of stack trace from previous location — at Microsoft.Identity.Web.MicrosoftIdentityWebAppAuthenticationBuilder.<>c__DisplayClass11_1.<<WebAppCallsWebApiImplementation>b__2>d.MoveNext() — End of stack trace from previous location — at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.RunTokenValidatedEventAsync(OpenIdConnectMessage authorizationResponse, OpenIdConnectMessage tokenEndpointResponse, ClaimsPrincipal user, AuthenticationProperties properties, JwtSecurityToken jwt, String nonce) at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandleRemoteAuthenticateAsync()

Relevant code snippets

var res = await _api.CallWebApiForUserAsync(ServiceName, options =>
{
  options.RelativePath = "/api/getsomething";
  //options.Scope = config.Scope //injected via config
}, user: user);
//AND
var accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(new string[] { config.Scope }, user: user);

Regression


Expected behavior

_api.CallWebApiForUserAsync get the required access token just as tokenAcquisition.GetAccessTokenForUserAsync does

About this issue

Most upvoted comments

This discussion is going into a complete wrong direction. The initial question was “why CallWebApiForUserAsync is not able to get a token while GetAccessTokenForUserAsync does”. It’s still not clear why they behave differently.