Role Based Authorization in ASP.NET Core Identity

We already discussed about different types of Authorization in ASP.NET Core. Here we'll discuss more about Role Based Authorization and how to implement it using ASP.NET Core Identity.

What is Role Based Authorization (RBAC)

A role is a collection of permissions provided to a user or system based on the responsibility of a user.

Instead of providing permisssion to the each user/system for each endpoint or resource, we can create a group of permission based on the requirement of a group and assign that group to the user/system, so they can perform task as per the authroity.

Role Based Authorization control allow the user/system to access based on the permission group they are assigned.
For e.g. If a user is assigned to Administrator role, then the user can access administritive content based on the permissions assigned to that role.
It helps us grouping the permisions to a user defined names. If a user's role is changed, it is easy to change the role instead of updating all the permission for a user. Thus it will allow centralized management.

In ASP.NET Core Identity, we already have AspNetRoles and AspNetUserRoles tables, which will be used to store roles and assign user to roles respectively.

ASP.NET Core Identity automatically add roles into claims upon login from AspNetUserRoles table, Which can be direcly accessed via current user's HttpContext's ClaimPrincipal.

Implement RBAC in ASP.NET Web API

To authorize an endpoint to a specific role, we first need to add Role to the AspNetRoles tables and assign the Role to user/system who can only perform the action. The user role assignment will be stored in AspNetUserRoles table.

For this blog we'll add a RoleController and Authorized this controller to Admin role. That means only Admin Roles can access this controller methods to perform any task.

As in our DB there is no role present, so any user can't be able to access that contoller method. We've to add Amin Role and assign the role to the user. 
We can chieve this by adding manually to the DB or seeding a Admin user with Admin role when the application is first started. In this case we don't have to add manually write to the DB. So let's start.

Seed initial data

We'll add a Admin user and a Administrator role and assign that role to admin user.  Also, we'll create a non admin user which will not have any role assigned. 
This class will create the required data, whenever an application is run for the first time. So, that admin user can enter into the system and do admin task.

Here is the SeedAdminUserAndData.cs class which contains the logic of creating an admin and nonadmin user and role and assign the role to admin user.
using Microsoft.AspNetCore.Identity;

namespace EmployeeManagement.Services
{
    public class SeedInitialData
    {
        public static async Task SeedAdminUserAndData(IServiceProvider serviceProvider)
        {
            var userManager = serviceProvider.GetService<UserManager<IdentityUser>>();
            var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();

            var adminUser = await userManager!.FindByNameAsync("admin@mail.com");
            if (adminUser == null)
            {
                adminUser = new IdentityUser
                {
                    UserName = "admin@mail.com",
                    Email = "admin@mail.com",
                    EmailConfirmed = true
                };
                await userManager.CreateAsync(adminUser, "Admin@123456");
            }

            if (adminUser == null)
            {
                throw new Exception("Please provide a strong password.");
            }

            var nonAdminUser = await userManager!.FindByNameAsync("nonadmin@gmail.com");
            if (nonAdminUser == null)
            {
                nonAdminUser = new IdentityUser
                {
                    UserName = "nonadmin@mail.com",
                    Email = "nonadmin@mail.com",
                    EmailConfirmed = true
                };
                await userManager.CreateAsync(nonAdminUser, "NonAdmin@1234");
            }

            if (roleManager == null)
            {
                throw new Exception("roleManager is null or not initialized.");
            }

            if (!await roleManager.RoleExistsAsync("Administrator"))
            {
                await roleManager.CreateAsync(new IdentityRole("Administrator"));
            }

            await userManager.AddToRoleAsync(adminUser, "Administrator");
        }
    }
}
Call the above class from program.cs after builder.build() to seed the data automaically, when the app starts for first time.
var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    await SeedInitialData.SeedAdminUserAndData(services);
}

Register RoleManager Services

ASP.NET Core Identity provides built in services to manage roles. We've to register the service in program.cs.

builder.Services.AddIdentityApiEndpoints<IdentityUser>()
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<IdentityDBContext>();
Here we've registered the role manager services using .AddRoles<IdentityRole>(). This will allow us to access rolmanager intance in SeedInitialData.cs class.

Apply RBAC to Controller

Here we'll add a RoleController which will be used in future to manage roles for our EmployeeManagement App. Here, we'll Authorize the contoller to Administrator roles only.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace EmployeeManagement.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    [Authorize(Roles = "Administrator")]
    public class RolesController(RoleManager<IdentityRole> roleManager) : ControllerBase
    {
        [HttpGet]
        public async Task<IActionResult> GetAll()
        {
            return Ok(await roleManager.Roles.ToListAsync());
        }
    }
}

Run the Application

Let's run the Application. When the application will start, it will execute SeedInitialData.SeedAdminUserAndData(services) which will seed the users and roles mentioned in the class.

Let's login with non admin user and execute the GET /Roles endpoint.


Now, login with admin user which has Administrato role and excute the same endpoint.


Hooray! We're able to authorize our endpoint using RBAC.

Conclusion

 In this article we learnt how to authorize our endpoint using RBAC with ASP.NET Core Identity. When we've small number of roles and our role definitions are not chaning frequently,then this approach is best suitable. Everytime we've change is role definitions, we've to redeploy the app to reflect the changes.Thus, systems where roles definitions are changing frequently will need to redeploy the the changes everytime, which is not suitable for this scenario. In the future articles, we'll also get to know about the other authorization types and also will come up solution with scenarios where roles are changing frequently.

Thank you!


Comments