Policies are most commonly registered at application startup in the Startup
classes ConfigureServices
method.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(config =>
{
config.AddPolicy("IsDeveloper", policy => policy.RequireClaim("IsDeveloper","true"));
});
}
the policy IsDeveloper
requires that a user have the claim IsDeveloper
with a value of true
.
Roles you can apply policies via the Authorize
attribute.
[Route("api/[controller]")]
[ApiController]
public class SystemController
{
[Authorize(Policy = “IsDeveloper”)]
public IActionResult LoadDebugInfo()
{
// ...
}
}
Blazors directives and components also work with policies.
@page "/debug"
@attribute [Authorize(Policy = "IsDeveloper")]
< AuthorizeView Policy="IsDeveloper">
< p>You can only see this if you satisfy the IsDeveloper policy.< /p>
< /AuthorizeView>
Easier Management
With role-based auth, if we had a couple of roles which were allowed access to protected resources - let’s say admin
and moderator
. We would need to go to every area they were permitted access and add an Authorize
attribute.
[Authorize(Roles = "admin,moderator")]
This doesn’t seem too bad initially, but what if a new requirement comes in and a third role, superuser
, needs the same access? We now need to go round every area and update all of the roles. With policy-based auth we can avoid this.
We can define a policy in a single place and then apply it once to all the resources which require it. Then when extra roles need to be added, we can just update the policy from the central point without the need to update the individual resources.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(config =>
{
config.AddPolicy("IsAdmin", policy => policy.RequireRole("admin", "moderator", "superuser"));
});
}
[Authorize(Policy = "IsAdmin")]
Creating shared policies
We need to install the Microsoft.AspNetCore.Authorization
package from NuGet in order to do this.
After that create a new class called Policies
with the following code.
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();
}
}
Here we’re using the AuthorizationPolicyBuilder
to define each policy, both require the user to be authenticated then be in either the Admin
role or User
role, depending on the policy.
Configuring the server
Rregistering the policies in ConfigureServices
in the Startup
class. Add the following code under the existing call to AddAuthentication
.
services.AddAuthorization(config =>
{
config.AddPolicy(Policies.IsAdmin, Policies.IsAdminPolicy());
config.AddPolicy(Policies.IsUser, Policies.IsUserPolicy());
});
registering each policy and using the constants we defined in the Policies
class to declare their names, which saves using magic strings.
If we move over to the SampleDataController
we can update the Authorize
attribute to use the new IsAdmin
policy instead of the old role.
[Authorize(Policy = Policies.IsAdmin)]
[Route("api/[controller]")]
public class SampleDataController : Controller
Again, we can use our name constant to avoid the magic strings.
Configuring the client
Our server is now using the new policies we defined, all that’s left to do is to swap over our Blazor client to use them as well.
As with the server we’ll start by registering the policies in ConfigureServices
in the Startup
class. We already have a call to AddAuthorizationCore
so we just need to update it.
services.AddAuthorizationCore(config =>
{
config.AddPolicy(Policies.IsAdmin, Policies.IsAdminPolicy());
config.AddPolicy(Policies.IsUser, Policies.IsUserPolicy());
});
In Index.razor
, update the AuthorizeView
component to use policies - still avoiding the magic strings.
< 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
s Authorize
attribute.
@attribute [Authorize(Policy = Policies.IsAdmin)]
Refer here