Role Base Authorization In ASP.NET Core 2.1

Introduction
 
Authorization is a process that determines what a user is able to do. For example, an Admin user is allowed to install/remove a software from a computer and a non-Admin user can use the software from the computer. It is independent and orthogonal from authentication. However, authorization requires an authentication mechanism. For applications, the first step is always authentication and then authorization.
 
Identity is membership system that allows us to add login functionality to our application and identity may belong to one or more roles. For Example, "User1" belongs to "Admin" role and "User2" belongs to "HR" role.
 
Using AuthorizeFilter, we can control the access in our MVC/Web API application by specifying this attribute in controller or action method. Role based authorization checks whether login user role has access to the page or not. Here developer embeds the roles with their code.
 
To demostrate with an example, I have created 3 roles and 3 users and mapped user with roles. I have achieved this by using the following code.
  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)  
  2. {  
  3.     ....  
  4.     ....  
  5.     app.UseMvc(routes =>  
  6.     {  
  7.         routes.MapRoute(  
  8.             name: "default",  
  9.             template: "{controller=Home}/{action=Index}/{id?}");  
  10.     });  
  11.   
  12.     CreateRoles(serviceProvider).Wait();  
  13. }  
  14.   
  15. private async Task CreateRoles(IServiceProvider serviceProvider)  
  16. {  
  17.     //initializing custom roles   
  18.     var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();  
  19.     var UserManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>();  
  20.     string[] roleNames = { "Admin""User""HR" };  
  21.     IdentityResult roleResult;  
  22.   
  23.     foreach (var roleName in roleNames)  
  24.     {  
  25.         var roleExist = await RoleManager.RoleExistsAsync(roleName);  
  26.         if (!roleExist)  
  27.         {  
  28.             //create the roles and seed them to the database: Question 1  
  29.             roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));  
  30.         }  
  31.     }  
  32.   
  33.     IdentityUser user = await UserManager.FindByEmailAsync("[email protected]");  
  34.   
  35.     if (user == null)  
  36.     {  
  37.         user = new IdentityUser()  
  38.         {  
  39.             UserName = "[email protected]",  
  40.             Email = "[email protected]",  
  41.         };  
  42.         await UserManager.CreateAsync(user, "Test@123");  
  43.     }  
  44.     await UserManager.AddToRoleAsync(user, "Admin");  
  45.   
  46.   
  47.     IdentityUser user1 = await UserManager.FindByEmailAsync("[email protected]");  
  48.   
  49.     if (user1 == null)  
  50.     {  
  51.         user1 = new IdentityUser()  
  52.         {  
  53.             UserName = "[email protected]",  
  54.             Email = "[email protected]",  
  55.         };  
  56.         await UserManager.CreateAsync(user1, "Test@123");  
  57.     }  
  58.     await UserManager.AddToRoleAsync(user1, "User");  
  59.   
  60.     IdentityUser user2 = await UserManager.FindByEmailAsync("[email protected]");  
  61.   
  62.     if (user2 == null)  
  63.     {  
  64.         user2 = new IdentityUser()  
  65.         {  
  66.             UserName = "[email protected]",  
  67.             Email = "[email protected]",  
  68.         };  
  69.         await UserManager.CreateAsync(user2, "Test@123");  
  70.     }  
  71.     await UserManager.AddToRoleAsync(user2, "HR");  
  72.   
  73. }   
 
 
We can specify the roles that have access to the requested resource using the Roles property of Authorize attribute. For example, the following code allows us to access the action method to users who are member of "Admin" role.
  1. [Authorize(Roles = "Admin")]  
  2. public IActionResult OnlyAdminAccess()  
  3. {  
  4.     ViewData["role"] = "Admin";  
  5.     return View("MyPage");  
  6. }  
We can specify multiple roles as a comma separated list. For example, in the following code snippet, action method would be only accessible by users who are members of either "Admin" or "User".
  1. [Authorize(Roles = "Admin,User")]  
  2. public IActionResult MultipleAccess()  
  3. {  
  4.     ViewData["role"] = "Admin";  
  5.     return View("MyPage");  
  6. }  
We can also apply multiple Authorize attributes and specify the role that has access.
  1. [Authorize(Roles = "Admin")]  
  2. [Authorize(Roles = "User")]  
  3. public IActionResult MultipleAccess()  
  4. {  
  5.     ViewData["role"] = "Admin";  
  6.     return View("MyPage");  
  7. }  
Policy based role checks
 
We can also create new policy that expresses the role requirement. We can add & register policy using the authorization service configuration. In the following code snippet, I have created the policy that allows access only to users who are members of "Admin".
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3. ....  
  4. ....  
  5. services.AddAuthorization(options =>  
  6.    {  
  7.        options.AddPolicy("OnlyAdminAccess", policy => policy.RequireRole("Admin"));  
  8.    });  
  9. }  
We can apply the policy to AuthorizeAttribute using "Policy" property.
  1. [Authorize(Policy = "OnlyAdminAccess")]  
  2. public IActionResult PolicyExample()  
  3. {  
  4.     ViewData["role"] = "Admin";  
  5.     return View("MyPage");  
  6. }  
Using this policy method, we can also do role based authorization for Razor pages. For example, if I have Razor page "Test1.cshtml" and this page can only be accessed by the user having "Admin" role, the following code helps us to add authorization for this Razor page.
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     ...  
  4.     ...  
  5.     services.AddMvc().AddRazorPagesOptions(options =>  
  6.     {  
  7.         options.Conventions.AuthorizePage("/test1""OnlyAdminAccess");  
  8.   
  9.   
  10.     }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);  
  11.   
  12.     services.AddAuthorization(options =>  
  13.     {  
  14.         options.AddPolicy("OnlyAdminAccess", policy => policy.RequireRole("Admin"));  
  15.     });  
  16. }  
You can view or download the source code from the GitHub link here