How to authorize user’s role in client side of blazor wasm?

Environments

  • Asp.Net Core 5.0
  • Blazor WebAssembly App (Asp.Net Core Hosting)
  • Asp.Net Core Identity (with Identity Server 4)

Problem

I want to use Role-based authorization between Server side and Client side.

I can login correctly and UserManager.IsInRoleAsync(user, "admin") returns True in the Server side.

But neither @attribute [Authorize(Roles = "admin")] nor <AuthorizeView Roles="admin"> doesn’t work in the Client side. Also User.Identity.IsInRole("admin") returns False in the Client side.

How can I get the user’s role in the Client side?

Codes

Server.csproj

// Startup.ConfigureServices()

services.AddDefaultIdentity<ApplicationUser>(options =>
{
    options.SignIn.RequireConfirmedAccount = true;

    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 2;
    options.Password.RequireNonAlphanumeric = false;

    options.User.RequireUniqueEmail = true;
})
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();

services.AddAuthentication()
    .AddIdentityServerJwt();
// Startup.Configure()

app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
// RolesController.Get()

var userid = HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
var currentUser = await userManager.FindByIdAsync(userid);
return await userManager.IsInRoleAsync(currentUser, "admin"); // Returns True

Client.csproj

// Program.Main()

builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("WebAppIdentity.ServerAPI"));

builder.Services.AddApiAuthorization();
// Test.razor

<AuthorizeView Roles="admin">
    <Authorizing>
        Authorizing...
    </Authorizing>
    <NotAuthorized>
        You are not an admin. // Always here
    </NotAuthorized>
    <Authorized>
        Hello, admin!
    </Authorized>
</AuthorizeView>

<button @onclick="ShowInfo">Show Info</button>
<p>@infoString</p>

@code
{
    [CascadingParameter]
    private Task<AuthenticationState> stateTask { get; set; }
    private string infoString { get; set; }

    private async void ShowInfo()
    {
        var user = (await stateTask).User;

        infoString = $"Is admin: {user.IsInRole("admin")}"; // Always False
    }
}

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

There are currently two accepted ways of handling this.

The first

#1 Configure Identity to use roles by calling AddRoles

services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

#2 Configure identity server to put the role claim into the id token and the access token and prevent the default mapping for roles in the JwtSecurityTokenHandler.

services.AddIdentityServer()
    .AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
        options.IdentityResources["openid"].UserClaims.Add("role");
        options.ApiResources.Single().UserClaims.Add("role");
    });

// Need to do this as it maps "role" to ClaimTypes.Role and causes issues
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");

#3 On your blazor application use [Authorize(Roles = “admin”)] or any other role your app defines.

#4 On your protected resource APIs use [Authorize(Roles = “admin”)] or any other role your app defines.

The second

#1 Add Class to configure options.UserOptions.RoleClaim on the Client

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Extensions.Options;

namespace App.Client.Services
{
    public class ApiAuthorizationOptionsConfiguration
        : IPostConfigureOptions<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>>
    {
        public void Configure(RemoteAuthenticationOptions<ApiAuthorizationProviderOptions> options)
        {
            options.UserOptions.RoleClaim ??= "role";
        }

        public void PostConfigure(string name, RemoteAuthenticationOptions<ApiAuthorizationProviderOptions> options)
        {
            if (string.Equals(name, Options.DefaultName))
            {
                Configure(options);
            }
        }
    }
}

#2 Modify the Program.cs file to call ApiAuthorizationOptionsConfiguration and configure the role claim.

using App.Client.Services;
...

namespace App.Client
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            ...

            builder.Services.AddAuthorizationCore();
            builder.Services.AddApiAuthorization();
            builder.Services.TryAddEnumerable(
                ServiceDescriptor.Singleton<
                    IPostConfigureOptions<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>>,
                    ApiAuthorizationOptionsConfiguration>());

            ...
        }
    }
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x