Use Roles to Authorize Users
User identities in .NET Core may belong to one or more defined roles. These roles can then be used to restrict access to various parts of the system. For example, if your system makes use of an administrator role, you can use that role as a means of identifying those users who have administrator-level access privileges. Other users might have lesser privileges in roles such as developer or user.
“OK, that’s all well and good, but where do these roles come from? Is there some sort of predefined list?”
No, there’s no list. The roles come from you. They might be prebuilt into the system during development, or they might be added later via the user interface. What you need to know is how to add roles using C# code.
Just as there is a UserManager for managing users, there is a RoleManager for managing roles, and you access it the same way: through dependency injection. Inject an instance of the RoleManager through the constructor of a controller class, and you have access to everything you need to create and manage roles.
The following code sample demonstrates how you can check for the existence of an administrator role and, if it does not exist, create it:
bool roleExists = await _roleManager.RoleExistsAsync("Administrator");
if (!roleExists)
{
var role = new IdentityRole();
role.Name = " Administrator ";
await _roleManager.CreateAsync(role);
}
Then, to assign a user to the role, pass an instance of the user’s identity data class to the UserManager’s AddToRoleAsync
method:
var result = await _userManager.AddToRoleAsync(user, "Administrator");
Finally, to restrict access to an area of code – this can be done to an entire class or a single method – use the Authorize
attribute, which you learned to use in the MVC class to grant only authenticated users access to either a class or method.
To extend the Authorize
attribute to roles, use the following syntax:
[Authorize(Roles = "Administrator")]
public class AdminController : Controller
{
}
You can also combine roles. In this example, specifying two roles in a single Authorize
attribute states that a user must be a member of one of these two roles to access the class:
[Authorize(Roles = "Administrator, Developer")]
public class DevController : Controller
{
}
Alternatively, the same roles specified in two separate Authorize
attributes means that a user must be a member of both roles to gain access:
[Authorize(Roles = "Administrator")]
[Authorize(Roles = "Developer")]
public class AdminDevController : Controller
{
}
Use Claims and Policies to Authorize Users
Claims are similar to roles, in that they can be used to restrict or limit user access, but they do so on a more granular level. Where a role might be equatable to a position or level of prestige, a claim may be more like an identifying trait.
The following might all be considered real-world examples of claims-based authorization:
You must be 48 inches or taller to ride this roller coaster.
You must be 21 or older to enter this club.
Tuesday night is Ladies’ Night! Ladies drink free!
Senior discount at the movies.
These claims are based on the value of a personal attribute or property, such as age, height, or gender. If you think of a web application in these terms, only users who have claims that match the criteria are permitted access to those areas of the application.
Claims are policy-based. As the developer, it’s your responsibility to build and register policies that express the requirements for your claims.
Claims and their policies are registered in the ConfigureServices
method of the Startup.cs file in your .NET Core project. The following example registers a claim for the Ladies’ Night example above:
services.AddAuthorization(options =>
{
options.AddPolicy("LadiesNight", policy => policy.RequireClaim("Gender",
"Female"));
});
If the claim value isn’t a single value or requires some transformation or comparison, such as a minimum age requirement, you need to use a handler to fulfill the policy rather than a single attribute value.
The following example shows how a handler is used to validate the minimum height requirement in the roller coaster example:
services.AddAuthorization(options =>
{
options.AddPolicy("MinimumHeight48", policy => policy.Requirements.Add(
new MinimumHeight(48)));
});
Note that the requirement we are adding to the policy is an object whose constructor is receiving the stated minimum value. This is where the claim and policy require a bit more work.
First, you need to create the MinimumHeight
class:
using Microsoft.AspNetCore.Authorization;
public class MinimumHeight : IAuthorizationRequirement
{
public int MinHeight { get; }
public MinimumHeight(int minHeight)
{
MinHeight = minHeight;
}
}
Next, build the handler class for the minimum height requirement:
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;
public class MinimumHeightHandler : AuthorizationHandler<MinimumHeight>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext
context, MinimumHeight requirement)
{
var height = context.User.FindFirst(c => c.Type == ClaimTypes.Height).Value);
if (height >= requirement.MinHeight)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Lastly, now that your handler is written, register it in the ConfigureServices
method. Add one line of code below the policy line:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthorization(options =>
{
options.AddPolicy("MinimumHeight48", policy => policy.Requirements.Add(
new MinimumHeight(48)));
});
services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
}
Use the New Policy
Now, use the policy for your claim to either restrict or grant access to an area of code. It works much like the earlier examples regarding roles. To limit access to an entire class based on a minimum height requirement, use the following syntax:
[Authorize(Policy = "MinimumHeight48")]
public class RideCoasterController : Controller
{
public IActionResult Index() => View();
}
Let’s Recap!
In this chapter, you learned how to control access to application pages and data using role and policy-based security methods. Specifically, you learned how to:
Use the
Authorize
attribute in conjunction with role names to authorize access based on user membership in a particular role.Use claims to create policies based on those claims.
Use the
Authorize
attribute in conjunction with policies to provide a more granular level of access control.
Now it’s time to assess your learning from Part 2 of this course by completing the Part 2 Quiz!