How to work with Roles in ASP.NET Core Identity

How to work with Roles in ASP.NET Core Identity

In ASP.NET Core Identity you can create Roles that contain a set of permissions for doing a set of activities in the application. For example an organization can have 4 roles which are:

  • 1. Admin – For doing administration works like assigning work to employees.
  • 2. Manager – For looking after the clients need and completing projects on time.
  • 3. Network – For keeping the internet of the organization, up and running, in a secured manner.
  • 4. Security – For guarding the premises of the organization.
In my previous tutorial I have explained How to do Authentication of Users in ASP.NET Core Identity. Now I will extend it with Identity Roles so make sure you read that tutorial also.

In ASP.NET Core Identity you can create any number of Roles and assign Identity users to these roles.

RoleManager<T> class

For accessing and managing roles you need the help of RoleManager<T> class. ‘T’ is the class that represents roles in the Identity Database.

The RoleManager class is used to manage the Roles in Identity, and has the some important functions and properties which are defined in the table given below.

Name Description
CreateAsync(role) Creates a new role
DeleteAsync(role) Deletes the specified role
FindByIdAsync(id) Find a role by its id
FindByNameAsync(name) Find a role by its name
RoleExistsAsync(name) Used to check if a role with the specified name exists or not
UpdateAsync(name) Updates a role
Roles This property returns all the roles in the Identity

To represent roles you will need the help of IdentityRole class. The important properties of this class are:

  • 1. Id – gives a unique identifier of the role.
  • 2. Name – defines the name of the role.
  • 3. Users – It returns a collection of IdentityUserRole objects that represents members of a role.

Create and Delete Roles in Identity

I will now create the functionality to Create and Delete Roles in ASP.NET Core Identity. So create a new Controller called RoleController.cs and add the following code to it:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace Identity.Controllers
{
    public class RoleController : Controller
    {
        private RoleManager<IdentityRole> roleManager;
        public RoleController(RoleManager<IdentityRole> roleMgr)
        {
            roleManager = roleMgr;
        }

        public ViewResult Index() => View(roleManager.Roles);

        private void Errors(IdentityResult result)
        {
            foreach (IdentityError error in result.Errors)
                ModelState.AddModelError("", error.Description);
        }
    }
}

In the Role Controller class, I have added a dependency of RoleManager class to the Constructor in order to to get the roles in a variable called roleManager. This code is:

private RoleManager<IdentityRole> roleManager;
public RoleController(RoleManager<IdentityRole> roleMgr)
{
    roleManager = roleMgr;
}
Get all Roles in Identity

The Index Action method of the Role Controller class returns all the Identity Roles as model to the default view like:

public ViewResult Index() => View(roleManager.Roles);

Create the Index View inside the Views ➤ Role folder. Add the following code to it:

@model IEnumerable<IdentityRole>
 
<h1 class="bg-info text-white">All Roles</h1>
<a asp-action="Create" class="btn btn-secondary">Create a Role</a>
 
<table class="table table-sm table-bordered table-bordered">
    <tr><th>ID</th><th>Name</th><th>Users</th><th>Update</th><th>Delete</th></tr>
    @foreach (var role in Model)
    {
        <tr>
            <td>@role.Id</td>
            <td>@role.Name</td>
            <td i-role="@role.Id"></td>
            <td><a class="btn btn-sm btn-primary" asp-action="Update" asp-route-id="@role.Id">Update</a></td>
            <td>
                <form asp-action="Delete" asp-route-id="@role.Id" method="post">
                    <button type="submit" class="btn btn-sm btn-danger">
                        Delete
                    </button>
                </form>
            </td>
        </tr>
    }
</table>

This View takes a model of type IEnumerable<IdentityRole>. I will loop through all the roles using the foreach method and then show them inside a table.

Notice the i-role attribute on the 3rd td element:

<td i-role="@role.Id"></td>

This attribute will call a Custom Tag Helper which will modify this td element to display a list of all the Users who are members of each Role.

So create a folder on the root of the project and call it TagHelper. To this folder add a Custom Tag Helper and call it RoleUsersTH.cs. The code of this RoleUserTH class is given below:

using Identity.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Identity.TagHelpers
{
    [HtmlTargetElement("td", Attributes = "i-role")]
    public class RoleUsersTH : TagHelper
    {
        private UserManager<AppUser> userManager;
        private RoleManager<IdentityRole> roleManager;

        public RoleUsersTH(UserManager<AppUser> usermgr, RoleManager<IdentityRole> rolemgr)
        {
            userManager = usermgr;
            roleManager = rolemgr;
        }

        [HtmlAttributeName("i-role")]
        public string Role { get; set; }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            List<string> names = new List<string>();
            IdentityRole role = await roleManager.FindByIdAsync(Role);
            if (role != null)
            {
                foreach (var user in userManager.Users)
                {
                    if (user != null && await userManager.IsInRoleAsync(user, role.Name))
                        names.Add(user.UserName);
                }
            }
            output.Content.SetContent(names.Count == 0 ? "No Users" : string.Join(", ", names));
        }
    }
}

This Custom Tag Helper operates on the td elements having an i-role attribute. This attribute is used to receive the id of the role that is being processed. The RoleManager and UserManager objects fetches a list of all the users that resides in a given role.

I need to update my View Imports file (_ViewImports.cshtml) so that:

  • 1. I can refer to the Microsoft.AspNetCore.Identity namespace in the views.
  • 2. I can use my custom tag helper in the Views.

The updated code of the _ViewImports.cshtml file is given below:

@using Identity.Models
@using Microsoft.AspNetCore.Identity
@addTagHelper Identity.TagHelpers.*, Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Creating a New Identity Role

A New Identity Role is created by the Create Action method. In the below code of the Role Controller. I have highlighted the create action method code which you need to add.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace Identity.Controllers
{
    public class RoleController : Controller
    {
        private RoleManager<IdentityRole> roleManager;
        public RoleController(RoleManager<IdentityRole> roleMgr)
        {
            roleManager = roleMgr;
        }

        public ViewResult Index() => View(roleManager.Roles);

        public IActionResult Create() => View();

        [HttpPost]
        public async Task<IActionResult> Create([Required]string name)
        {
            if (ModelState.IsValid)
            {
                IdentityResult result = await roleManager.CreateAsync(new IdentityRole(name));
                if (result.Succeeded)
                    return RedirectToAction("Index");
                else
                    Errors(result);
            }
            return View(name);
        }
    }
}

The Create Action takes the Role name as string in it’s parameter and uses the CreateAsync() method to create the Identity Role.

You will also need to add the Create View inside the Views ➤ Role folder having the code as given below.

@model string
 
<h1 class="bg-info text-white">Create a Role</h1>
<a asp-action="Index" class="btn btn-secondary">Back</a>
<div asp-validation-summary="All" class="text-danger"></div>
 
<form asp-action="Create" method="post">
    <div class="form-group">
        <label for="name">Name:</label>
        <input name="name" class="form-control" />
    </div>
    <button type="submit" class="btn btn-primary">Create</button>
</form>
Deleting a Role

The Delete Role feature is provided by the Delete Action method which takes the Role id in it’s parameter, and uses the DeleteAsync() method to delete the role. The Code of this action method is given below:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;

namespace Identity.Controllers
{
    public class RoleController : Controller
    {
        // other actions
        
        [HttpPost]
        public async Task<IActionResult> Delete(string id)
        {
            IdentityRole role = await roleManager.FindByIdAsync(id);
            if (role != null)
            {
                IdentityResult result = await roleManager.DeleteAsync(role);
                if (result.Succeeded)
                    return RedirectToAction("Index");
                else
                    Errors(result);
            }
            else
                ModelState.AddModelError("", "No role found");
            return View("Index", roleManager.Roles);
        }
    }
}
Testing the Create & Delete Functionality

Run your project and go to the create role URL – https://localhost:44395/Role/Create. Once you create a role you will be redirected the Index View which will show all the Roles in the Identity Database.

The create role view is shown below:

create role identity

The Index View will have a delete button through which an Identity Role can be deleted from the database. Check the below image:

Cross Origin Resource Sharing (CORS) is a browser restriction applied to JavaScript & jQuery. I have written an article to deal with them, check it here – How to Enable Cross-Origin Requests (CORS) in ASP.NET Core
all roles identity

Adding and Removing Users from Identity Roles

Now I will create a functionality to Add or Remove Users from Identity Roles. For implementing this functionality add 2 classes called RoleEdit.cs and RoleModification.cs inside the Models folder. The codes of these classes are given below.

The RoleEdit class code:

using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;

namespace Identity.Models
{
    public class RoleEdit
    {
        public IdentityRole Role { get; set; }
        public IEnumerable<AppUser> Members { get; set; }
        public IEnumerable<AppUser> NonMembers { get; set; }
    }
}

The RoleEdit class is used to represent the Role and the details of the Users who are in the role or not in the role.

The RoleModification class code:

using System.ComponentModel.DataAnnotations;

namespace Identity.Models
{
    public class RoleModification
    {
        [Required]
        public string RoleName { get; set; }

        public string RoleId { get; set; }

        public string[] AddIds { get; set; }

        public string[] DeleteIds { get; set; }
    }
}

The RoleModification class code will help in doing the changes to a role.

I will now use these 2 classes to add or remove users from a role. For this I will add an Update action to the Role Controller.

The updated code of the RoleController.cs containing the Update Action is highlighted and given below:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Identity.Models;

namespace Identity.Controllers
{
    public class RoleController : Controller
    {
        private RoleManager<IdentityRole> roleManager;
        private UserManager<AppUser> userManager;

        public RoleController(RoleManager<IdentityRole> roleMgr, UserManager<AppUser> userMrg)
        {
            roleManager = roleMgr;
            userManager = userMrg;
        }

        // other methods

        public async Task<IActionResult> Update(string id)
        {
            IdentityRole role = await roleManager.FindByIdAsync(id);
            List<AppUser> members = new List<AppUser>();
            List<AppUser> nonMembers = new List<AppUser>();
            foreach (AppUser user in userManager.Users)
            {
                var list = await userManager.IsInRoleAsync(user, role.Name) ? members : nonMembers;
                list.Add(user);
            }
            return View(new RoleEdit
            {
                Role = role,
                Members = members,
                NonMembers = nonMembers
            });
        }

        [HttpPost]
        public async Task<IActionResult> Update(RoleModification model)
        {
            IdentityResult result;
            if (ModelState.IsValid)
            {
                foreach (string userId in model.AddIds ?? new string[] { })
                {
                    AppUser user = await userManager.FindByIdAsync(userId);
                    if (user != null)
                    {
                        result = await userManager.AddToRoleAsync(user, model.RoleName);
                        if (!result.Succeeded)
                            Errors(result);
                    }
                }
                foreach (string userId in model.DeleteIds ?? new string[] { })
                {
                    AppUser user = await userManager.FindByIdAsync(userId);
                    if (user != null)
                    {
                        result = await userManager.RemoveFromRoleAsync(user, model.RoleName);
                        if (!result.Succeeded)
                            Errors(result);
                    }
                }
            }

            if (ModelState.IsValid)
                return RedirectToAction(nameof(Index));
            else
                return await Update(model.RoleId);
        }
    }
}

Notice I have added a dependency of UserManager class on the constructor.

JWT token system provides highest quality security to your APIs. Want to read it more – How to secure APIs with JWT in ASP.NET Core 3.1 [with source codes]

The HTTP GET version of the Update Action method is used to fetch members and non-members of a selected Identity Role. While the HTTP POST version of the Update Action method is used for adding or removing users from an Identity Role.

I have used the following members of the UserManager class to play with the roles:

Name Description
AddToRoleAsync(AppUser user, string name) Adds a user to a role
RemoveFromRoleAsync(AppUser user, string name) Removes a user from a role
GetRolesAsync(AppUser user) Gives the names of the roles in which the user is a member
IsInRoleAsync(AppUser user, string name) Returns true is the user is a member of a specified role, else returns false

Next, add an Update View inside the Views ➤ Role folder with the following code:

@model RoleEdit
 
<h1 class="bg-info text-white">Update Role</h1>
<a asp-action="Index" class="btn btn-secondary">Back</a>
<div asp-validation-summary="All" class="text-danger"></div>
 
<form method="post">
    <input type="hidden" name="roleName" value="@Model.Role.Name" />
    <input type="hidden" name="roleId" value="@Model.Role.Id" />
     
    <h2 class="bg-info p-1 text-white">Add To @Model.Role.Name</h2>
    <table class="table table-bordered table-sm">
        @if (Model.NonMembers.Count() == 0)
        {
            <tr><td colspan="2">All Users Are Members</td></tr>
        }
        else
        {
            @foreach (AppUser user in Model.NonMembers)
            {
                <tr>
                    <td>@user.UserName</td>
                    <td>
                        <input type="checkbox" name="AddIds" value="@user.Id">
                    </td>
                </tr>
            }
        }
    </table>
 
    <h2 class="bg-info p-1 text-white">Remove From @Model.Role.Name</h2>
    <table class="table table-bordered table-sm">
        @if (Model.Members.Count() == 0)
        {
            <tr><td colspan="2">No Users Are Members</td></tr>
        }
        else
        {
            @foreach (AppUser user in Model.Members)
            {
                <tr>
                    <td>@user.UserName</td>
                    <td>
                        <input type="checkbox" name="DeleteIds" value="@user.Id">
                    </td>
                </tr>
            }
        }
    </table>
    <button type="submit" class="btn btn-primary">Save</button>
</form>

The View has 2 HTML tables:

  • 1. For showing users who are non-members of the selected role.
  • 2. For showing users who are members of the selected role.

A checkbox is given against each user’s name, and through this checkbox users can be added or removed from the selected role.

Testing Update Role Feature

Make sure you have 3 Users created in Identity. If not, then go to the URL – https://localhost:44395/Admin/Create and create the following users:

1. Name – tom, Email[email protected], Password[email protected]
2. Name – alice, Email[email protected], Password[email protected]
3. Name – pintu, Email[email protected], Password[email protected]

See the below image:

create users in identity

To test the Update Role Feature, where you will add or remove users from a role, you should have 3 roles created from the URL – https://localhost:44395/Role/Create.

These roles are:

1. Manager
2. Developer
3. Admin

Notice the Users column shows No Users for each of the Roles, as shown in the image below:

create roles in identity

Now click the Update button against the role called Manager which will take you to the Update View where you will see your 3 users(tom, alice, pintu) under the Add To Manager heading, see below image:

update role

Next, check the tom and alice checkboxes and click the save button. This will add these 2 users to the Manager role, as shown by the given image:

users added to role

Next, click the Update button of the Manager’s role once more. You will now see user called pintu shown inside the heading called Add To Manager, and tom & alice shown inside another heading called Remove From Manager.

Check the below image:

members and non members of manager role

Click the checkbox for alice and then click the save button. This will remove alice from Manger’s role. Check the below image:

alice removed from manager role

Note that a user can be added to Multiple Roles, like tom can be added to all the Identity Roles.

Identity Roles for Authentication

Roles can be use with the [Authorize] attributes to specify that all Users of the selected Role can only access a give action method.

Let’s take an example. When I add the Roles property of the Authorize attribute like [Authorize(Roles = "Manager")] to the Index action of the Home Controller. Then it specifies that only Manager Role Users can access the Index Action method of the Home Controller. The code is given below:

public class HomeController : Controller
{
    [Authorize(Roles = "Manager")]
    public IActionResult Index()
    {
        return View((object)"Hello");
    }
}

Run your application and login with user’s tom credentials. Everything will work fine since user tom belongs to the Manager role.

Now log-in with user alice credentials. Since alice does not belongs to Manager’s role then you note that on opening the URL of the Index Action of the Home Controller, which is Home/Index, you will be redirected to access denied page whose URL is https://localhost:44395/Account/AccessDenied?ReturnUrl=%2F. Since you have not created the AccessDenied action on the Account controller therefore you will get HTTP ERROR 404 error.

So create the action called AccessDenied in the Account Controller as shown by the below code:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Identity.Models;
using System.Threading.Tasks;

namespace Identity.Controllers
{
    [Authorize]
    public class AccountController : Controller
    {
        //...

        public IActionResult AccessDenied()
        {
            return View();
        }
    }
}

Also adds the AccessDenied.cshtml view file inside the Views ➤ Account folder, and add the below shown code to it.

<h2 class="bg-danger mb-1 p-2 text-white">Access Denied</h2>
<a asp-action="Index" asp-controller="Home" class="btn btn-primary">OK</a>

Now re-run your application and go to the login URL – https://localhost:44395/Account/Login. Log in with user alice credentials:

1. Email – [email protected]
2. Password – [email protected]

After login you will be redirected to the Access Denied URL, as shown in the below image:

access denied url identity
The /Account/AccessDenied URL is the default URL set by Identity which you can change by setting the AccessDeniedPath configuration property in the ConfigureServices method of Startup Class.
services.ConfigureApplicationCookie(opts =>
{
    opts.AccessDeniedPath = "/Stop/Index";
});

Remember: If you change the roles for the user you are currently authenticated as, the changes won’t take effect until you log out and re-login.

You can download the full codes of this tutorial from the below link:

Download

Conclusion
This completes the tutorial on Roles in Identity and now you are in a position to upgrade your website to include Identity Roles and authentication of users based on these roles.

Share this article -

yogihosting

ABOUT THE AUTHOR

This article has been written by the Technical Staff of YogiHosting. Check out other articles on "ASP.NET Core, jQuery, EF Core, SEO, jQuery, HTML" and more.