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?
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.
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.
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.
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.
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. 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";
});
...
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With