Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] Blazor server does not handle the MsalUiRequiredException #360

Closed
jennyf19 opened this issue Jul 23, 2020 · 10 comments
Closed

[Bug] Blazor server does not handle the MsalUiRequiredException #360

jennyf19 opened this issue Jul 23, 2020 · 10 comments
Labels
Blazor bug Something isn't working fixed web app

Comments

@jennyf19
Copy link
Collaborator

When calling Blazor server for the acquire token silent (or OBO) part, or when there is a need for more scopes or conditional access, Blazor does not handle context.Result = new ChallengeResult(properties); in AuthorizeForScopesAttribute, like in MVC or Razor.

@jennyf19
Copy link
Collaborator Author

@gwgrubbs @schmid37
Do you mind trying this branch with the following code changes in the CallWebApi.razor page:
First you'll need to inject the following services and a using statement:

@using Microsoft.Identity.Web
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject NavigationManager NavigationManager

and then in OnInitializedAsync() add the following code:

protected override async Task OnInitializedAsync()
{
        try
        {
            apiResult = await downstreamAPI.CallWebApiAsync();
        }
        catch (MicrosoftIdentityWebChallengeUserException ex)
        {
            await BlazorHelper.ChallengeUserAsync(
                ex,
                AuthenticationStateProvider,
                NavigationManager,
                GetType().Name).ConfigureAwait(false);
        }
}

Here's an example.

We might take this as a "quick fix", with a better long term solution. Let us know if this works for you. Thanks.

cc: @jmprieur

@dansmitt
Copy link

@jennyf19 What I did:

protected override async Task OnInitializedAsync()
{
    try
    {
        retVal = await AadService.GetAsync();
    }
    catch (MicrosoftIdentityWebChallengeUserException ex)
    {
        await BlazorHelper.ChallengeUserAsync(
            ex,
            AuthenticationStateProvider,
            NavigationManager,
            GetType().Name).ConfigureAwait(false);
    }
}

Exception:

blazor.server.js:19 [2020-07-24T12:48:31.731Z] Error: System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Identity.Web.HttpContextExtensions.GetTokenUsedToCallWebAPI(HttpContext httpContext) in C:\src\Microsoft.Identity.Web\HttpContextExtensions.cs:line 29
at Microsoft.Identity.Web.TokenAcquisition.GetAccessTokenForUserAsync(IEnumerable`1 scopes, String tenant, String userFlow,

@jennyf19
Copy link
Collaborator Author

thanks @schmid37 @gwgrubbs
if you have time, mind trying this branch and doing the following in the CallWebApi page:

@inject MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler;
...
@code {
    private string apiResult;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            apiResult = await downstreamAPI.CallWebApiAsync();
        }
        catch (Exception ex)
        {
            ConsentHandler.HandleException(ex);
        }
    }

And then in Startup.cs:

services.AddServerSideBlazor()
             .AddMicrosoftIdentityConsentHandler();

Appreciate your patience as we sort this out.

@dansmitt
Copy link

dansmitt commented Jul 24, 2020

@jennyf19

First login works.
Restart server, calling the function, exception is:

Error: System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Identity.Web.MicrosoftIdentityConsentAndConditionalAccessHandler.HandleException(Exception ex) in C:\src\Microsoft.Identity.Web\MicrosoftIdentityCircuitHandler.cs:line 92

--> Delete cookies, and it works again.

@dansmitt
Copy link

@jennyf19 so what I want to say, after a server restart, it never works.

@gwgrubbs
Copy link

gwgrubbs commented Jul 24, 2020

@jennyf19

Case where a cookie exists, but the token does not exist in cache and an attempt to acquire a new token (running locally).

in _Host.cshtml:

@model _HostAuthPageModel

method in _HostAuthPageModel.cs:

public async Task<IActionResult> OnGet()
{
	if (!User.Identity.IsAuthenticated)
		return Challenge();

	try
	{
		AccessToken = await tokenAcquisition.GetAccessTokenForUserAsync(apiOptions.Value.Scopes);
	}
	catch (MicrosoftIdentityWebChallengeUserException ex)
	{
		//can't get a token from the token store, MUST assume a sign-out path as requests to API will NOT be authenticated
		logger.LogError(ex, ex.Message);
		consentHandler.HandleException(ex);
	}

	return Page();
}

So, at initial page load of the application the OnGet() method is executed, the user is authenticated (due to the cookie), then attempting to acquire an access token throws an MicrosoftIdentityWebChallengeUserException exception (as expected). When the consentHandler.HandleException(ex) executes, the following exception occurs:

System.ArgumentNullException: Value cannot be null. (Parameter 'claimsPrincipal')
   at Microsoft.Identity.Web.ClaimsPrincipalExtensions.GetDisplayName(ClaimsPrincipal claimsPrincipal) in D:\src\microsoft-identity-web\src\Microsoft.Identity.Web\ClaimsPrincipalExtensions.cs:line 128
   at Microsoft.Identity.Web.ClaimsPrincipalExtensions.GetLoginHint(ClaimsPrincipal claimsPrincipal) in D:\src\microsoft-identity-web\src\Microsoft.Identity.Web\ClaimsPrincipalExtensions.cs:line 90
   at Microsoft.Identity.Web.IncrementalConsentAndConditionalAccessHelper.BuildAuthenticationProperties(String[] scopes, MsalUiRequiredException ex, ClaimsPrincipal user) in D:\src\microsoft-identity-web\src\Microsoft.Identity.Web\IncrementalConsentAndConditionalAccessHelper.cs:line 74
   at Microsoft.Identity.Web.MicrosoftIdentityConsentAndConditionalAccessHandler.HandleException(Exception exception) in D:\src\microsoft-identity-web\src\Microsoft.Identity.Web\MicrosoftIdentityCircuitHandler.cs:line 85
   at OBFUSCATED.Web.App.Pages._HostAuthPageModel.OnGet() in D:\src\OBFUSCATED\src\OBFUSCATED.Web.App\Pages\_HostAuthPageModel.cs:line 55
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.GenericTaskHandlerMethod.Convert[T](Object taskAsObject)
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory.GenericTaskHandlerMethod.Execute(Object receiver, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext context)
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

@jennyf19
Copy link
Collaborator Author

jennyf19 commented Jul 24, 2020

Thank you @gwgrubbs
I've just pushed up a fix for the null ref (hopefully), i think it got lost either in a PR that wasn't merged where this was fixed, or something else. Also, this has been merged into master.
If you're using a razor page, you can use the [AuthorizeForScopes] attribute to handle the challenge on the class describing the page.

@schmid37 I'm not able to reproduce this. Do you have a basic repro you can share w/exact steps? That will help determine a fix faster. Thank you. You can email it if you want too: [email protected]

@dansmitt
Copy link

@jennyf19 Thank you for the help. I emailed you!

@pmaytak
Copy link
Contributor

pmaytak commented Jul 25, 2020

@gwgrubbs @schmid37

This is included in Microsoft Identity Web 0.2.1-preview release.

@pmaytak pmaytak closed this as completed Jul 25, 2020
@jmprieur
Copy link
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Blazor bug Something isn't working fixed web app
Projects
None yet
Development

No branches or pull requests

5 participants