Original: https: //chrissainty.com/securing-your-blazor-apps-configuring-policy-based-authorization-with-blazor/
In the previous article , I showed how to add role-based to Blazor WebAssembly (Blazor client) application authorization. In this article, I'll show you how to use Blazor configure policy-based authorization.
Policy-based authorization
ASP.NET Core allows a more flexible way to create an authorization rule policy-based authorization. Policy authorization concept consists of three components:
- Policy - Policy have one or more requirements.
- Requirement - Strategies for assessing the parameters of the current user principal data collection.
- Handler - processing program used to determine whether the current user has access to subject the requested resource.
Strategy is usually when the application starts Startup
the class ConfigureService
registration process.
services.AddAuthorization(config => { config.AddPolicy("IsDeveloper", policy => policy.RequireClaim("IsDeveloper", "true")); });
In the above example, the policy IsDeveloper
requires the user needs to have IsDeveloper
a statement, and a value true
.
Like the role authorization, you can use the same Authorize
attributes apply to policy authorization.
[Route("api/[controller]")] [ApiController] public class SystemController { [Authorize(Policy = “IsDeveloper”)] public IActionResult LoadDebugInfo() { // ... } }
Blazors instructions and components also can use the same strategy.
@page "/debug" @attribute [Authorize(Policy = "IsDeveloper")]
<AuthorizeView Policy="IsDeveloper"> <p>You can only see this if you satisfy the IsDeveloper policy.</p> </AuthorizeView>
Easier to manage
The biggest advantage of policy-based authorization is to improve authorization management applications. Use role-based authorization, if we have two roles are allowed to access protected resources - such as admin
and moderator
. We need to add a resource each is allowed access to the Authorize
property.
[Authorize(Roles = "admin,moderator")]
It does not look bad at the beginning, but if a new need arises, third role superuser
, need the same access rights, how to do it? Now we need to update all of the roles in each access resource. Through policy-based verification, we can avoid this situation.
We can define a policy in one, and then apply it to all the resources it needs. When the need to add additional roles, we only need to update this policy, without the need to update each resource.
public void ConfigureServices(IServiceCollection services) { services.AddAuthorization(config => { config.AddPolicy("IsAdmin", policy => policy.RequireRole("admin", "moderator", "superuser")); }); }
[Authorize(Policy = "IsAdmin")]
Create custom requirements
Policy authorization is very flexible, you can create role-based requirements, statements, and even create custom requirements. Let's look at how to create custom requirements.
Normally, when you have a complex logic, we use a custom needs. As mentioned above, we need a demand and a top handler to use policy authorization.
Let's create a check the user's e-mail address is whether demand for the company domain. We need to create an authorization demand class, this class needs to implement IAuthorizationRequirement
the interface, it's just an empty marker interface.
public class CompanyDomainRequirement : IAuthorizationRequirement { public string CompanyDomain { get; } public CompanyDomainRequirement(string companyDomain) { CompanyDomain = companyDomain; } }
Next, we need to create a class that inherits from the demand for our AuthorizationHandler
handler, T
is to deal with the demand.
public class CompanyDomainHandler : AuthorizationHandler<CompanyDomainRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CompanyDomainRequirement requirement) { if (!context.User.HasClaim(c => c.Type == ClaimTypes.Email)) { return Task.CompletedTask; } var emailAddress = context.User.FindFirst(c => c.Type == ClaimTypes.Email).Value; if (emailAddress.EndsWith(requirement.CompanyDomain)) { return context.Succeed(requirement); } return Task.CompletedTask; } }
In the above code, we check for e-mail statement. If so, we check whether it ends in the domain specified as required, and if so, success is returned, otherwise it fails.
We just want our demands a strategy associated with it, and CompanyDomainHandler
registered with the dependency injection container.
public void ConfigureServices(IServiceCollection services) { services.AddAuthorization(config => { config.AddPolicy("IsCompanyUser", policy => policy.Requirements.Add(new CompanyDomainRequirement("newco.com"))); }); services.AddSingleton<IAuthorizationHandler, CompanyDomainHandler>(); }
For more detailed information about the custom requirements, we recommend checking out the official documentation .
Blazor use policy
Now that we understand what the strategy is, let's see how to use them in your application.
In Blazor application on an article we will switch to a policy-based authorization. As part of this effort, we will see another advantage of policy-based authorization, that can define policies in a shared project and reference them in the server and the client.
Create a Sharing Policy
In share the project before you create a policy, we need to start NuGet install Microsoft.AspNetCore.Authorization
this package.
After installation, use the following code to create a named Policies
class.
public static class Policies { public const string IsAdmin = "IsAdmin"; public const string IsUser = "IsUser"; public static AuthorizationPolicy IsAdminPolicy() { return new AuthorizationPolicyBuilder().RequireAuthenticatedUser() .RequireRole("Admin") .Build(); } public static AuthorizationPolicy IsUserPolicy() { return new AuthorizationPolicyBuilder().RequireAuthenticatedUser() .RequireRole("User") .Build(); } }
We first define the two constants IsAdmin
and IsUser
. We will use them when enrollment policy. Next is the strategy itself, IsAdminPolicy
and IsUserPolicy
. Here I use AuthorizationPolicyBuilder
to define each policy, the two policies require users to authenticate, and then depending on the policy, users may be Admin
roles and User
roles.
Configure the server
Now that we have defined the policy, we need to let the server to use them. First, Startup
class ConfigureServices
enrollment policy method, AddAuthentication
after adding the following code.
services.AddAuthorization(config => {
config.AddPolicy(Policies.IsAdmin, Policies.IsAdminPolicy());
config.AddPolicy(Policies.IsUser, Policies.IsUserPolicy());
});
The code is very easy to understand, we use Policies
the constants defined in the class to declare their names and register each policy, avoid using magic string.
In WeatherForecastController
you can useIsAdmin策略代旧的角色。
[ApiController] [Route("[controller]")] [Authorize(Policy = Policies.IsAdmin)] public class WeatherForecastController : ControllerBase
Similarly, we can use the name to avoid the constant magic string.
Configuring the Client
The server can now use our new strategy defined, the next step is to use them in Blazor client.
And the service side, we are in the Startup
class ConfigureServices
enrollment policy approach. We have previously called AddAuthorizationCore
, so only need to update it.
services.AddAuthorizationCore(config => {
config.AddPolicy(Policies.IsAdmin, Policies.IsAdminPolicy());
config.AddPolicy(Policies.IsUser, Policies.IsUserPolicy());
});
In Index.razor
use policy update AuthorizeView
components - as to avoid the use of magic string.
<AuthorizeView Policy="@Policies.IsUser"> <p>You can only see this if you satisfy the IsUser policy.</p> </AuthorizeView> <AuthorizeView Policy="@Policies.IsAdmin"> <p>You can only see this if you satisfy the IsAdmin policy.</p> </AuthorizeView>
Finally, update FetchData.razor
the Authorize
property.
@attribute [Authorize(Policy = Policies.IsAdmin)]
That's it! Our application is now transferred to policy-based authorization. We now have a more flexible licensing system, you can use roles, statements, policy or custom combination of any of the above.
About server Blazor
I did not specifically discuss the server Blazor, the reason is very simple, can be done without problems on our transfer to the server Blazor.
to sum up
In this article, we discuss the ASP.NET Core and Blazor policy-based authorization. We also understand some of the advantages of using role-based authorization with respect to the policy-based authorization and we will migrate applications from a role-based authentication to verify policy-based.
Finally Code ( GitHub )