Home > Mobile >  Blazor WASM - User.Identity?.IsAuthenticated == true but unable to get access token without user int
Blazor WASM - User.Identity?.IsAuthenticated == true but unable to get access token without user int

Time:01-24

I have a dotnet 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 is called by AccountClaimsPrincipalFactory

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

Result of calling 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.

ClaimsPrincipal implementation of IsAuthenticated

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?

CodePudding user response:

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

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

enter image description here

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

enter image description here

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

enter image description here

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;
                });
  • Related