blazor.net-8.0

AuthenticationStateProvider propagates incomplete identity to WebAssembly with Blazor in .NET 8


I have a component using auto render mode:

@attribute [RenderModeInteractiveAuto]

I inject the AuthenticationStateProvider:

@inject Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider _auth

Now, in my code, if I do something like this:

var state = await _auth.GetAuthenticationStateAsync();
var user = state.User;
var isAdmin = user.IsInRole("admin");

When this is run on the server, isAdmin is true for logged-in users in the admin role. However, on WASM, this is false. Plus, on the server side, the name claim correctly contains the user name whereas in WASM it contains the e-mail address.

Is this still a bug, or do I have to tweak some settings to get the correct identity propagated?


Solution

  • Authentication in Blazor 8 WASM is performed by PersistingServerAuthenticationStateProvider on the server persisting the Identity using the UserInfo class to the wasm client via its PersistentAuthenticationStateProvider

    I achieved this and forced everyone to be an "Administrator" for demonstration/testing purposes as follows:

    Updated UserInfo to include Roles

    public class UserInfo
    {
        public required string UserId { get; set; }
        public required string Email { get; set; }
        public required UserRole[]? Roles { get; set; }
    }
    
    public class UserRole
    {
        public required string Name { get; set; }
    }
    
    

    On the server project update PersistingServerAuthenticationStateProvider.cs

    Just below these two lines

    var userId = principal.FindFirst(_options.ClaimsIdentity.UserIdClaimType)?.Value;
    var email = principal.FindFirst(_options.ClaimsIdentity.EmailClaimType)?.Value;
    
    

    I added this to persist the role claims

    UserRole[] userRoles =
        principal.FindAll(_options.ClaimsIdentity.RoleClaimType)
        .Select(a=> new UserRole { Name = a.Value })
        .Append(new UserRole { Name = "Administrator" }) // <== remove this
        .ToArray();
    

    Then in the client to create the ClaimsIdentity update PersistentAuthenticationStateProvider.cs

    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        if (!persistentState.TryTakeFromJson<UserInfo>(nameof(UserInfo), out var userInfo) || userInfo is null)
        {
            return _unauthenticatedTask;
        }
    
        List<Claim> claims = [
            new Claim(ClaimTypes.NameIdentifier, userInfo.UserId),
            new Claim(ClaimTypes.Name, userInfo.Email), // <== Name Claim Issue
            new Claim(ClaimTypes.Email, userInfo.Email)];
    
        foreach (var role in userInfo?.Roles ?? [])
        {
            claims.Add(new Claim(ClaimTypes.Role, role.Name));
        }
    
        return Task.FromResult(
            new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims.ToArray(),
                authenticationType: nameof(PersistentAuthenticationStateProvider)))));
    }
    
    
    @page "/auth"
    
    @using Microsoft.AspNetCore.Authorization
    
    @attribute [Authorize]
    @attribute [RenderModeInteractiveWebAssembly]
    
    <PageTitle>Auth</PageTitle>
    
    <h1>You are authenticated</h1>
    
    <AuthorizeView>
        Hello @context.User.Identity?.Name!
    </AuthorizeView>
    <AuthorizeView Roles="Administrator">
        [Administrator]
    </AuthorizeView>