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 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 a 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 the "Admin" role, and "User2" belongs to the "HR" role.

Using AuthorizeFilter, we can control the access in our MVC/Web API application by specifying this attribute in the controller or action method. Role-based authorization checks whether the login user role has access to the page or not. Here, the developer embeds the roles with their code.

To demonstrate with an example, I have created 3 roles and 3 users and mapped users with roles. I have achieved this by using the following code.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)  
{  
    // Other configurations...
    
    app.UseMvc(routes =>  
    {  
        routes.MapRoute(  
            name: "default",  
            template: "{controller=Home}/{action=Index}/{id?}");  
    });  
  
    CreateRoles(serviceProvider).Wait();  
}  
  
private async Task CreateRoles(IServiceProvider serviceProvider)  
{  
    // Initializing custom roles   
    var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();  
    var UserManager = serviceProvider.GetRequiredService<UserManager<IdentityUser>>();  
    string[] roleNames = { "Admin", "User", "HR" };  
    IdentityResult roleResult;  
  
    foreach (var roleName in roleNames)  
    {  
        var roleExist = await RoleManager.RoleExistsAsync(roleName);  
        if (!roleExist)  
        {  
            // Create the roles and seed them to the database  
            roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));  
        }  
    }  
  
    IdentityUser user = await UserManager.FindByEmailAsync("[email protected]");  
  
    if (user == null)  
    {  
        user = new IdentityUser()  
        {  
            UserName = "[email protected]",  
            Email = "[email protected]",  
        };  
        await UserManager.CreateAsync(user, "Test@123");  
    }  
    await UserManager.AddToRoleAsync(user, "Admin");  
  
    IdentityUser user1 = await UserManager.FindByEmailAsync("[email protected]");  
  
    if (user1 == null)  
    {  
        user1 = new IdentityUser()  
        {  
            UserName = "[email protected]",  
            Email = "[email protected]",  
        };  
        await UserManager.CreateAsync(user1, "Test@123");  
    }  
    await UserManager.AddToRoleAsync(user1, "User");  
  
    IdentityUser user2 = await UserManager.FindByEmailAsync("[email protected]");  
  
    if (user2 == null)  
    {  
        user2 = new IdentityUser()  
        {  
            UserName = "[email protected]",  
            Email = "[email protected]",  
        };  
        await UserManager.CreateAsync(user2, "Test@123");  
    }  
    await UserManager.AddToRoleAsync(user2, "HR");  
}

Application

We can specify the roles that have access to the requested resource using the Roles property of the Authorize attribute. For example, the following code allows us to access the action method to users who are members of the "Admin" role.

[Authorize(Roles = "Admin")]
public IActionResult OnlyAdminAccess()
{
    ViewData["role"] = "Admin";
    return View("MyPage");
}

We can specify multiple roles as a comma-separated list. For example, in the following code snippet, the action method would be only accessible by users who are members of either "Admin" or "User".

[Authorize(Roles = "Admin,User")]  
public IActionResult MultipleAccess()  
{  
    ViewData["role"] = "Admin";  
    return View("MyPage");  
}

We can also apply multiple Authorize attributes and specify the role that has access.

[Authorize(Roles = "Admin")]  
[Authorize(Roles = "User")]  
public IActionResult MultipleAccess()  
{  
    ViewData["role"] = "Admin";  
    return View("MyPage");  
}

Policy-based role checks

We can also create a 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".

public void ConfigureServices(IServiceCollection services)  
{  
    // Other service configurations...
    
    services.AddAuthorization(options =>  
    {  
        options.AddPolicy("OnlyAdminAccess", policy => policy.RequireRole("Admin"));  
    });  
}

We can apply the policy to AuthorizeAttribute using the "Policy" property.

[Authorize(Policy = "OnlyAdminAccess")]  
public IActionResult PolicyExample()  
{  
    ViewData["role"] = "Admin";  
    return View("MyPage");  
}

Using this policy method, we can also do role-based authorization for Razor pages. For example, if I have the Razor page "Test1.cshtml" and this page can only be accessed by the user having an "Admin" role, the following code helps us to add authorization for this Razor page.

public void ConfigureServices(IServiceCollection services)  
{  
    // Other configurations...
    
    services.AddMvc().AddRazorPagesOptions(options =>  
    {  
        options.Conventions.AuthorizePage("/test1", "OnlyAdminAccess");  
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);  
  
    services.AddAuthorization(options =>  
    {  
        options.AddPolicy("OnlyAdminAccess", policy => policy.RequireRole("Admin"));  
    });  
}

You can view or download the source code from the GitHub link here.