Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Authorization Roles and Policies in Blazor WebAssembly with Identity?

I am using a Blazor WebAssembly 3.2-preview3 application scaffolded with Identity that has created the following 3 projects:

App.Client
App.Server
App.Shared

My IdentityUser implementation is called AppUser, and my ApiAuthorizationDbContext implementation is called AppDb

I have a role called Admin that has been assigned to administrators, and a Policy called RequireAdmin that checks for the Admin role. However, when I try to use an AuthorizeView in App.Client and view the page with an administrator, it does not show the link.

<AuthorizeView Policy="RequireAdmin">
    <Authorized>
        <a class="nav-link" href="admin">Admin</a>
    </Authorized>
</AuthorizeView>

How can I enable roles and policies in my Blazor WebAssembly application?

like image 470
Caleb Avatar asked Apr 11 '20 07:04

Caleb


People also ask

How do I allow anonymous users on a specific Blazor page?

In the case that we do want a specific page to be accessed by any users, not only authorized users, we will use the anonymous users category. To make a Blazor page accessible by all users, use [AllowAnonymous] with @attribute code in the _Imports.

How does Blazor authenticate the user?

Blazor uses the existing ASP.NET Core authentication mechanisms to establish the user's identity. The exact mechanism depends on how the Blazor app is hosted, Blazor WebAssembly or Blazor Server. In Blazor WebAssembly apps, authentication checks can be bypassed because all client-side code can be modified by users.

What is an example of role-based authorization in Blazor?

The following example is from the Blazor Server project template and uses ASP.NET Core Identity endpoints in the Identity area of the app to process Identity-related work. The AuthorizeView component supports role-based or policy-based authorization. For more information, see Role-based authorization in ASP.NET Core.

What is the default policy for authorizeview in Blazor?

If neither Roles nor Policy is specified, AuthorizeView uses the default policy. Blazor allows for authentication state to be determined asynchronously. The primary scenario for this approach is in Blazor WebAssembly apps that make a request to an external endpoint for authentication.

What are the Blazor WebAssembly authorization conventions?

Blazor WebAssembly apps run on the client. Authorization is only used to determine which UI options to show. Since client-side checks can be modified or bypassed by a user, a Blazor WebAssembly app can't enforce authorization access rules. Razor Pages authorization conventions don't apply to routable Razor components.


1 Answers

1. Create Roles and Policies in App.Shared

Add the Microsoft.AspNetCore.Authorization package to the App.Shared project

Add the following 2 classes in the App.Shared project to define Roles and Policies that will be used by the Client and Server.

Shared/RoleTypes.cs

namespace App.Shared
{
    public static class RoleTypes
    {
        public const string Admin = "Admin";
    }
}

Shared/PolicyTypes.cs

using Microsoft.AspNetCore.Authorization;

namespace App.Shared
{
    public static class PolicyTypes
    {
        public const string RequireAdmin = "RequireAdmin";

        public static AuthorizationOptions AddAppPolicies(this AuthorizationOptions options)
        {
            options.AddPolicy(RequireAdmin, policy =>
                policy.RequireRole(RoleTypes.Admin));
            return options;
        }
    }
}

2. Configure services in App.Server

Modify the Startup.cs file in the App.Server project to add your policies by calling the extension method that was just defined in App.Shared.

Also modify it to include the role claim in the openid scope.

Server/Startup.cs

using App.Shared;
...

namespace App.Server
{
    public class Startup
    {

        ...

        public void ConfigureServices(IServiceCollection services)
        {
            ...

            services.AddDefaultIdentity<AppUser>(options =>
                {
                    options.SignIn.RequireConfirmedAccount = true;
                })
                .AddRoles<IdentityRole>()
                .AddEntityFrameworkStores<AppDb>();

            services.AddIdentityServer()
                .AddApiAuthorization<AppUser, AppDb>(options =>
                {
                    // https://github.com/dotnet/AspNetCore.Docs/issues/17649
                    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");

            services.AddAuthentication()
                .AddIdentityServerJwt();

            services.AddAuthorization(options => options.AddAppPolicies());

            ...
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            ...

            app.UseAuthentication();
            app.UseAuthorization();
            app.UseIdentityServer();

            ...
        }
    }
}

3. Configure services in App.Client

Modify the Program.cs file in the App.Client project to add your policies by calling the extension method that was just defined in App.Shared. Also modify AddApiAuthorization to configure the role claim.

Client/Program.cs

using App.Client.Services;
using App.Shared;
...

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

            builder.Services.AddAuthorizationCore(options => options.AddAppPolicies());
            // 2 calls to AddApiAuthorization are necessary in 3.2-preview3
            // should be fixed in 3.2-preview4
            // https://github.com/dotnet/aspnetcore/issues/19854
            // https://github.com/dotnet/AspNetCore.Docs/issues/17649#issuecomment-612442543
            builder.Services.AddApiAuthorization();
            builder.Services.AddApiAuthorization(options =>
            {
                options.UserOptions.RoleClaim = "role";
            });

            ...
        }
    }
}
like image 60
Caleb Avatar answered Nov 15 '22 07:11

Caleb