Pro Code Guide

Developer’s Guide To Programming

HOME    CONTACT   ABOUT

ASP.NET Core Identity Roles based Authorization

Updated Mar 1, 2021 | 0 comments

This article will get you started with what are ASP.NET Core Identity roles and the need for roles, how to enable ASP.NET Core Identity roles in MVC Application, and how to implement role-based authorization.

Role-based authorization is for basic authorization where roles & their permissions can be decided at the start and there are no requirements for dynamic changing user permissions at runtime.

This is the third post in the series – ASP.NET Core Security. In my previous posts, I covered how to get started with ASP.NET Core Identity, understanding claims & implementing claims-based authorization. In this article, we will learn about roles and how to implement ASP.NET Core Identity roles-based authorization.

Before you start reading this I would suggest that first, you read my previous article about ASP.NET Core Identity – Getting Started

  1. ASP.NET Core Identity – Getting Started
  2. ASP.NET Core Identity Claims based Authorization
  3. ASP.NET Core Identity Identity Roles based Authorization
  4. Implement Cookie Authentication in ASP.NET Core

Introduction to ASP.NET Core Identity roles

Roles are a standard & common approach for implementing authorization in Applications. Identity can contain roles & roles, in turn, contain permissions for performing actions in the application. You can assign multiple roles to a user. When a user is created it can be linked to one or more roles.

E.g. in our Sample Blog App which we created as part of Identity Getting Started user [email protected] can be administrator & [email protected] can be assigned the role of a normal user. Based on permissions set for administrator & normal user both users will be allowed to perform different actions in the application.

Need for roles

Now if the user accesses any web application with only user-id & password (i.e. without role) then all users will have access to all features in the application. Also, it will be difficult to restrict access to users based on user id as new users will be added and each time it will require a change in access specific logic.

Instead, we can create roles like admin, normal user, power user, etc. and we can assign required access to these roles. Now when we create users we can add users to specific role groups based on the type of access required for that user.

So by doing this application access will be role-based and users can be assigned these roles based on the access required.

ASP.NET Core 5 Design Patterns: Thinking code using architectural principles, testing, design patterns, and C

Implement Roles using ASP.NET Core Identity Roles in Web Application

We will learn how to implement roles using ASP.NET Core Identity roles in Web application with Identity. For this, we will build upon the existing source code of the Sample Blog App in which we have so far added identity & implemented claim based authorization. Here is the link for the source code which will be used for demonstration of roles implementation & authorization.

After downloading the above project you will have to perform Add Migrations & update the database to the project, in Visual Studio, by running the below commands in the package manager console.

add-migration FirstMigration
update-database

After running the above commands run the project and you should see the below screen.

Roles Based Authorization

Let’s enable roles in our ASP.NET Core application i.e. in our sample blog app & then we will add UI to create new roles and enable role assigning during user creation i.e. during register user.

Enabling ASP.NET Core Identity Roles in MVC Application

To enable roles in the ASP.NET Core Application we need to modify Configure method in the Areas/Identity/IdentityHostingStartup.cs file as shown below

public void Configure(IWebHostBuilder builder)
{
    builder.ConfigureServices((context, services) => {
        services.AddDbContext<SampleAppContext>(options =>
            options.UseSqlServer(
                context.Configuration.GetConnectionString("SampleAppContextConnection")));

        services.AddIdentity<SampleAppUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddDefaultUI()
            .AddEntityFrameworkStores<SampleAppContext>()
            .AddDefaultTokenProviders();

        services.AddScoped<IUserClaimsPrincipalFactory<SampleAppUser>,
            ApplicationUserClaimsPrincipalFactory
            >();
    });
}

Also, we need to modify Areas/Identity/Data/ApplicationUserClaimsPrincipalFactory.cs so that roles can be supported by this factory

public class ApplicationUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<SampleAppUser, IdentityRole>
{
    public ApplicationUserClaimsPrincipalFactory(
        UserManager<SampleAppUser> userManager,
        RoleManager<IdentityRole> roleManager,
        IOptions<IdentityOptions> options
        ) : base(userManager, roleManager, options)
    {

    }

    protected override async Task<ClaimsIdentity> GenerateClaimsAsync(SampleAppUser user)
    {
        var identity = await base.GenerateClaimsAsync(user);

        identity.AddClaim(new Claim("FullName",
            user.FullName
            ));

        return identity;
    }
}

Let’s wire the UI to create new ASP.NET Core Identity Roles

Add role controller to view & create roles in our Sample Blog App i.e. Controllers/RoleController.cs

public class RoleController : Controller
{
    RoleManager<IdentityRole> roleManager;

    public RoleController(RoleManager<IdentityRole> roleManager)
    {
        this.roleManager = roleManager;
    }

    public IActionResult Index()
    {
        var roles = roleManager.Roles.ToList();
        return View(roles);
    }

    public IActionResult Create()
    {
        return View(new IdentityRole());
    }

    [HttpPost]
    public async Task<IActionResult> Create(IdentityRole role)
    {
        await roleManager.CreateAsync(role);
        return RedirectToAction("Index");
    }
}

Now let’s add views for the index & create actions i.e. Views/Role/Index.cshtml & Views/Role/Create.cshtml

@model IEnumerable<Microsoft.AspNetCore.Identity.IdentityRole>
@{
    ViewData["Title"] = "Index";
}

<h1>List of Roles</h1>
<a asp-action="Create">Create</a>
<table class="table table-striped table-bordered">
    <thead>
        <tr>
            <td>Id</td>
            <td>Name</td>
        </tr>
    </thead>
    <tbody>
        @foreach (var role in Model)
        {
            <tr>
                <td>@role.Id</td>
                <td>@role.Name</td>
            </tr>

        }
    </tbody>
</table>
@model Microsoft.AspNetCore.Identity.IdentityRole

@{
    ViewData["Title"] = "Create";
}
<h1>Create Role</h1>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>
<div>
    <a asp-action="Index">Back to List</a>
</div>
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");
    }
}

Now to display a link for Roles on the Main page add the following code to Views/Shared/_Layout.cshtml. Add following code under unordered HTML list for Links

<li class="nav-item">
    <a class="nav-link text-dark" asp-area="" asp-controller="Role" asp-action="Index">Roles</a>
</li>

Let’s run the code & create a couple of roles by navigating to Roles links

ASP.NET Core Identity roles
ASP.NET Core Identity roles

Enable role assignment on user creation i.e. Register

Let’s add a role to the page where we create a new user so that we are able to specify a role for the user when the user is being created. For this, we will have to change the register view to capture the role & saving logic on the register button to save the role for the user along with user creation.

Make below changes in Areas/Identity/Pages/Account/Register.cshtml file

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
    var roles = (List<IdentityRole>)ViewData["roles"];
}

<h1>@ViewData["Title"]</h1>

<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="@Model.ReturnUrl" method="post">
            <h4>Create a new account.</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Input.FullName"></label>
                <input asp-for="Input.FullName" class="form-control" />
                <span asp-validation-for="Input.FullName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Email"></label>
                <input asp-for="Input.Email" class="form-control" />
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Password"></label>
                <input asp-for="Input.Password" class="form-control" />
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ConfirmPassword"></label>
                <input asp-for="Input.ConfirmPassword" class="form-control" />
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.UserRole"></label>
                <select asp-for="Input.UserRole" class="form-control" asp-items='new SelectList(roles,"Id","Name")'>
                </select>
                <span asp-validation-for="Input.UserRole" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Register</button>
        </form>
    </div>
    <div class="col-md-6 col-md-offset-2">
        <section>
            <h4>Use another service to register.</h4>
            <hr />
            @{
                if ((Model.ExternalLogins?.Count ?? 0) == 0)
                {
                    <div>
                        <p>
                            There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
                            for details on setting up this ASP.NET application to support logging in via external services.
                        </p>
                    </div>
                }
                else
                {
                    <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                        <div>
                            <p>
                                @foreach (var provider in Model.ExternalLogins)
                                {
                                    <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
                                }
                            </p>
                        </div>
                    </form>
                }
            }
        </section>
    </div>
</div>
@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Make below changes in Areas/Identity/Pages/Account/Register.cshtml.cs file

[AllowAnonymous]
public class RegisterModel : PageModel
{
    private readonly SignInManager<SampleAppUser> _signInManager;
    private readonly UserManager<SampleAppUser> _userManager;
    private readonly ILogger<RegisterModel> _logger;
    private readonly IEmailSender _emailSender;
    private readonly RoleManager<IdentityRole> _roleManager;

    public RegisterModel(
        UserManager<SampleAppUser> userManager,
        SignInManager<SampleAppUser> signInManager,
        ILogger<RegisterModel> logger,
        IEmailSender emailSender,
        RoleManager<IdentityRole> roleManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _logger = logger;
        _emailSender = emailSender;
        _roleManager = roleManager;
    }

    [BindProperty]
    public InputModel Input { get; set; }

    public string ReturnUrl { get; set; }

    public IList<AuthenticationScheme> ExternalLogins { get; set; }

    public class InputModel
    {
        [Required]
        [StringLength(25, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 10)]
        [Display(Name = "Full Name")]
        public string FullName { get; set; }

        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; }

        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }

        [Required]
        [Display(Name = "User Role")]
        public string UserRole { get; set; }
    }

    public async Task OnGetAsync(string returnUrl = null)
    {
        ViewData["roles"] = _roleManager.Roles.ToList();
        ReturnUrl = returnUrl;
        ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
    }

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        returnUrl = returnUrl ?? Url.Content("~/");
        var role = _roleManager.FindByIdAsync(Input.UserRole).Result;
        ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
        if (ModelState.IsValid)
        {
            var user = new SampleAppUser { UserName = Input.Email, Email = Input.Email, FullName = Input.FullName };
            var result = await _userManager.CreateAsync(user, Input.Password);
            if (result.Succeeded)
            {
                _logger.LogInformation("User created a new account with password.");

                await _userManager.AddToRoleAsync(user, role.Name);

                var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
                var callbackUrl = Url.Page(
                    "/Account/ConfirmEmail",
                    pageHandler: null,
                    values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
                    protocol: Request.Scheme);

                await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                    $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

                if (_userManager.Options.SignIn.RequireConfirmedAccount)
                {
                    return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
                }
                else
                {
                    await _signInManager.SignInAsync(user, isPersistent: false);
                    return LocalRedirect(returnUrl);
                }
            }
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
        }

        ViewData["roles"] = _roleManager.Roles.ToList();
        // If we got this far, something failed, redisplay form
        return Page();
    }
}

Let’s run the code to register a new user with an admin role.

So far what we have done is enabled roles in the ASP.NET Core MVC application & then added code to be able to create new roles so that we are able to change register view & code to capture a role for the user during user creation on the register.

Now let’s use this role to implement authorization for the users based on this role which is assigned to the user during user creation.

Implement ASP.NET Core Identity Roles based authorization

Now let’s implement some access restrictions based on the role admin that we have assigned to the new user [email protected] while creating that user. What we saw was we were able to create new roles without login i.e. anyone can browse to application link and create roles.

We will add restrictions on role creation that users with an admin role will be able to create roles and all other users will be able to view roles.

As shown in the code below we will register a policy ‘rolecreation’ for role creation for an admin role in method ConfigureServices Method in a startup.cs file

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();

    services.AddAuthorization(options =>
    {
        options.AddPolicy("EmailID", policy =>
        policy.RequireClaim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "[email protected]"
        ));

        options.AddPolicy("rolecreation", policy =>
        policy.RequireRole("Admin")
        );
    });
}

Now we will apply this policy ‘rolecreation’ in role controller to actions for role creation to restrict role creation to admin roles only. Make below changes to Controllers/RoleController.cs

public class RoleController : Controller
{
    RoleManager<IdentityRole> roleManager;

    public RoleController(RoleManager<IdentityRole> roleManager)
    {
        this.roleManager = roleManager;
    }

    public IActionResult Index()
    {
        var roles = roleManager.Roles.ToList();
        return View(roles);
    }

    [Authorize(Policy = "rolecreation")]
    public IActionResult Create()
    {
        return View(new IdentityRole());
    }

    [HttpPost]
    [Authorize(Policy = "rolecreation")]
    public async Task<IActionResult> Create(IdentityRole role)
    {
        await roleManager.CreateAsync(role);
        return RedirectToAction("Index");
    }
}

After making the above changes let’s run the code to check ASP.NET Core Identity Roles based authorization. Now it will not be possible to create roles without login & also after login only users with an admin role will be able to create roles.

Summary

In this article, we learned how to create ASP.NET Core Identity roles, assign roles to users & use these identity roles to implement authorization for users based on roles assigned to them. Role-based authorization is a very commonly used implementation for restricting access to users. Users can be created and assigned roles based on access requirements for the user.

We made use of one of the important features of ASP.NET core that is to define authorization policies based on roles & we made use of this policy to control access to controller actions.

References: ASP.NET Core Identity Roles based Authorization

Download Source Code


https://github.com/procodeguide/Sample.Identity.Roles
0 forks.
0 stars.
0 open issues.
Recent commits:

Hope you found this article useful. Your support is appreciated!
Buy me a coffeeBuy me a coffee
Home 9 Programming 9 ASP.NET Core Identity Roles based Authorization

Set start URL in ASP.NET Core – Quick & Easy ways

This article will cover the ways to set start URL in ASP.NET Core 5 applications i.e. change the default URL (http://localhost:5000) in ASP.NET Core applications. When you create any new ASP.NET Core application whether its MVC App or Web API and run it then it will...

Hangfire in ASP.NET Core – Easy way to Schedule Background Jobs

This article covers detail about how to integrate Hangfire in ASP.NET Core applications which is an easy way to schedule background jobs in .NET Core and .NET based applications. Background jobs or tasks allow the programmers to execute code on a different thread but...

How to Send Emails in ASP.NET Core – Quick & Easy Guide

These days sending emails like user email id confirmation, OTP emails, acknowledgments, etc. is a very common requirement of any application. In this article, we will see a demo about how to send emails in ASP.NET Core in quick & easy steps. We will create an...

ML.NET – Machine Learning with .NET Core – Beginner’s Guide

This article will get you started with the fundamentals of Machine Learning and how to get started with Machine Learning with .NET Core i.e. ML.NET. We will even learn different concepts of Machine learning with a brief overview. Introduction to Machine Learning...

Implement Cookie Authentication in ASP.NET Core – Detailed Guide

This article will get you started with implementing cookie authentication in ASP.NET Core applications. Cookie authentication allows you to have your own login/register screens & custom logic for user-id/password validation without the need to use ASP.NET Core...

ASP.NET Core Identity Roles based Authorization

This article will get you started with what are ASP.NET Core Identity roles and the need for roles, how to enable ASP.NET Core Identity roles in MVC Application, and how to implement role-based authorization. Role-based authorization is for basic authorization where...

Dependency Injection in ASP.NET Core 3.1 – Beginner’s Guide

This article will cover in details dependency injection in ASP.NET Core 3.1. ASP.NET Core is designed from the ground up to support the dependency injection design pattern. Dependency injection in ASP.NET Core provides a technique to create applications that are...

Real-time Web Applications with SignalR in ASP.NET Core 3.1

In this article, we will learn about real-time web & how to build real-time web applications using SignalR in ASP.NET Core. As part of this article, we will work on an application that can send real-time messages/alerts to all connected users or private messages...

Code Profiling using MiniProfiler in ASP.NET Core 3.1

This article will get you started with how to enable miniprofiler in ASP.NET Core to measure the performance of your application. MiniProlifer helps you understand how your application is performing by allowing you to profile any part of code in ASP.NET Core...

Microservices with ASP.NET Core 3.1

Microservices is a type of Architecture in which application is created as multiple small independent serviceable components. This article will cover in detail how to create microservices with ASP.NET Core, Serilog, Swagger UI, Health Checks & Docker containers....

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *