简体   繁体   中英

Blazor WASM - User.Identity?.IsAuthenticated == true but unable to get access token without user intervention

I have a do.net 7 Blazor WASM app, using Azure AD B2C (via AddMsalAuthentication in Program.cs ).

The homepage of the app allows anonymous access, and features a call-to-action to login if the user is not authenticated.

In the layout used by the homepage, I have a dropdown that is populated via an API call. This will attempt to make the API call if User.Identity?.IsAuthenticated == true . The API call uses an HTTP client using BaseAddressAuthorizationMessageHandler (which in turn inherits AuthorizationMessageHandler ) which is responsible for silently obtaining an access token before making the call.

If obtaining an access token fails, AccessTokenNotAvailableException is thrown and I call Redirect() on the exception, which redirects to the B2C login screen. This is not the behaviour I want as users are redirected without responding to the call-to-action to log in.

Delving into the library code, this seems to be where Blazor WASM creates the ClaimsIdentity . It calls a JS method in MSAL.js, AuthenticationService.getUser :

AuthenticationService.getUser 由 AccountClaimsPrincipalFactory 调用

If I call the AuthenticationService.getUser method in my browser, I can see an object returned:

调用 AuthenticationService.getUser 的结果

I note the exp claim for the user is a few days ago - in my mind this has expired.

I also notice ClaimsIdentity.IsAuthenticated returns true when the AuthenticationType is non-null. In my case, the AuthenticationType is a Guid matching my B2C app's ClientId .

IsAuthenticated 的 ClaimsPrincipal 实现

So my question is: what should I be calling, other than User.Identity?.IsAuthenticated == true to determine whether the user is authenticated, and an access token can be provisioned without the user having to re-authenticate? Should I be using Blazor custom policies?

After some more investigation, my conclusion is this behaviour is by design.

The default policy out-of-the box simply adds RequireAuthenticatedUser :

在此处输入图像描述

This doesn't necessarily do what you'd expect - it adds DenyAnonymousAuthorizationRequirement :

在此处输入图像描述

Which in turn simply checks that the Identity is authenticated (which as I noted before just checks for a non-null authentication type):

在此处输入图像描述

My solution was to use a custom policy - in services.AddAuthorizationCore - to check the exp claim:

new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .RequireClaim("email")
                .AddAuthenticationSchemes("bearer")
                .RequireAssertion(ctx =>
                {
                    var exp = ctx.User.Claims.SingleOrDefault(c => c.Type.Equals("exp" , StringComparison.OrdinalIgnoreCase));

                    if (exp is null)
                        return false;

                    var datetime = DateTimeOffset.FromUnixTimeSeconds(long.Parse(exp.Value));
                    return datetime.UtcDateTime > Clock.Instance.UtcNow;
                });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM