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

Rabe, Jens 35 Reputation points
2023-10-25T21:14:05.82+00:00

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 ran on the server, isAdmin is true when my logged-in user is 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?

Blazor
Blazor
A free and open-source web framework that enables developers to create web apps using C# and HTML being developed by Microsoft.
1,647 questions
0 comments No comments
{count} vote

3 answers

Sort by: Most helpful
  1. Rabe, Jens 35 Reputation points
    2023-10-26T07:15:42.04+00:00

    I am using the exemplary authentication which is generated when creating a new Blazor Web App. I solved the problem in the meantime myself.

    I had to augment UserInfo in the Client project:

    public class UserInfo {
        public required string UserName { get; set; } // <= added
        public required string UserId { get; set; }
        public required string Email { get; set; }
        public string? Role { get; set; } // <= added
    }
    

    And PersistingRevalidatingAuthenticationStateProvider in the server-side project:

            if(principal.Identity?.IsAuthenticated == true) {
                var userId = principal.FindFirst(_options.ClaimsIdentity.UserIdClaimType)?.Value;
                var email = principal.FindFirst(_options.ClaimsIdentity.EmailClaimType)?.Value;
                // The next two ones are added
                var name = principal.FindFirst(_options.ClaimsIdentity.UserNameClaimType)?.Value;
                var role = principal.FindFirst(_options.ClaimsIdentity.RoleClaimType)?.Value;
    
                if(userId != null && email != null) {
                    _state.PersistAsJson(nameof(UserInfo), new UserInfo {
                        UserId = userId,
                        Email = email,
                        // New below
                        UserName = name ?? email,
                        Role = role
                    });
                }
            }
    

    And PersistentAuthenticationStateProvider on the client:

        public override Task<AuthenticationState> GetAuthenticationStateAsync() {
            if(!persistentState.TryTakeFromJson<UserInfo>(nameof(UserInfo), out var userInfo) || userInfo is null) {
                return _unauthenticatedTask;
            }
            IEnumerable<Claim> EnumerateClaims() {
                yield return new(ClaimTypes.NameIdentifier, userInfo.UserId);
                yield return new(ClaimTypes.Name, userInfo.UserName); // added
                yield return new(ClaimTypes.Email, userInfo.Email);
                // added
                if(userInfo.Role is string role) {
                    yield return new(ClaimTypes.Role, role);
                }
            }
    
            return Task.FromResult(
                new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(EnumerateClaims(),
                    authenticationType: nameof(PersistentAuthenticationStateProvider)))));
        }
    

    This allows me to get the real user name and role.

    2 people found this answer helpful.

  2. Bruce (SqlWork.com) 69,501 Reputation points
    2023-10-25T22:17:24.85+00:00

    what authentication did you use on the server and what are you using for the WASM. In the server version how did you load the role claims?

    when using server blazor, the authentication cookie has the roles. Usually they are added via GetRoles() when the identity is created, then stored in the cookie.

    when using Blazor WASM, the blazor app requests a JwtToken, The server issuing the token would need to add the claim roles to the token before returning.

    either change the authentication that return the JwtToken to include the roles, or the WASM will need to do an api call to get the roles, and apply to the User via the custom authentication state provider that support adding the roles.


  3. Dunce 41 Reputation points
    2025-01-15T10:13:21.9466667+00:00

    Easy fix to ensure all claims are passed to WASM! (.net 8 and 9)

    By default when using Identity with .net8 and .net9 not all claims are serialised and passed to WebAssembly - For example the user Id is not persisted -it's incomplete as you point out.

    I struggled with this for a long time but it turns out there is a REALLY easy way to ensure that ALL claims are serialised and passed to WASM. In the server program.cs, modify the "AddAuthenticationStateSerialization" to include all claims as follows;

    builder.Services.AddRazorComponents()
    
    .AddInteractiveServerComponents()
    
    .AddInteractiveWebAssemblyComponents()
    
    .AddAuthenticationStateSerialization(options => options.SerializeAllClaims = true);
    

    reference: https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-9.0&tabs=visual-studio

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.