Back to: ASP.NET Core Identity Tutorials
Role and Claim-Based Authorization in ASP.NET Core MVC Views
In this article, I will discuss How to Implement Role and Claim-Based Authorization in ASP.NET Core MVC Views with an Example. Please read our previous article discussing Role-Based vs Claims-Based Authorization in ASP.NET Core Identity.
Role-Based Authorization in ASP.NET Core MVC Views
We have already discussed how to perform role-based authorization checks in ASP.NET Core MVC Views. This is useful when we want to show or hide UI elements based on whether the logged-in user has the proper role to access them. We need to show the Manage navigation menu item only if the user is signed in and a member of the Admin role.
The navigation menu is in the layout view. To check if the user is signed, we need to inject the ASP.NET Core Identity SignInManager service into the layout view as follows.
@using Microsoft.AspNetCore.Identity @inject SignInManager<ApplicationUser> SignInManager
Then, we need to use the following two methods to check whether the user is signed in and has an Admin role.
- IsSignedIn(User): This method will check if the current user is signed in. It returns true if the user represented by the given ClaimsPrincipal is signed in; otherwise, it returns false. This method needs to be invoked using the SignInManager service.
- IsInRole(“Admin”): This IsInRole method determines whether the current user is in a specified role. It returns true if the current user is in the specified role; otherwise, it is false. This method needs to be invoked using the ClaimsPrincipal object.
We have already implemented this in our Show or Hide Navigation Menus Based on User Role article. So, the following is the Layout.cshtml file where we implement the above:
@using Microsoft.AspNetCore.Identity @inject SignInManager<ApplicationUser> SignInManager <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>@ViewData["Title"] - ASPNETCoreIdentityDemo</title> <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /> <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" /> <link rel="stylesheet" href="~/ASPNETCoreIdentityDemo.styles.css" asp-append-version="true" /> </head> <body> <header> <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3"> <div class="container-fluid"> <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Home</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between"> <ul class="navbar-nav flex-grow-1"> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="SecureMethod">Secure</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="NonSecureMethod">Non Secure</a> </li> @if (SignInManager.IsSignedIn(User) && User.IsInRole("Admin")) { <li class="nav-item dropdown"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button" data-bs-toggle="dropdown" aria-expanded="false"> Manage </a> <ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink"> <a class="dropdown-item" asp-controller="Administration" asp-action="ListUsers">Users</a> <a class="dropdown-item" asp-controller="Administration" asp-action="ListRoles">Roles</a> </ul> </li> } </ul> <ul class="navbar-nav ml-auto"> @*If the user is signed-in display Logout link*@ @if (SignInManager.IsSignedIn(User)) { <li class="nav-item"> <form method="post" asp-controller="account" asp-action="logout"> <button type="submit" style="width:auto" class="nav-link btn btn-link py-0"> Logout @User?.Identity?.Name </button> </form> </li> } else { <li class="nav-item"> <a class="nav-link text-dark" asp-controller="account" asp-action="register"> Register </a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-controller="account" asp-action="login"> Login </a> </li> } </ul> </div> </div> </nav> </header> <div class="container"> <main role="main" class="pb-3"> @RenderBody() </main> </div> <footer class="border-top footer text-muted"> <div class="container"> © 2023 - ASPNETCoreIdentityDemo - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a> </div> </footer> <script src="~/lib/jquery/dist/jquery.min.js"></script> <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script> <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script> <script src="~/js/site.js" asp-append-version="true"></script> @await RenderSectionAsync("Scripts", required: false) </body> </html>
Claim-Based Authorization in ASP.NET Core MVC Views
Implementing claim-based authorization in ASP.NET Core MVC views involves checking for specific claims within the current user’s identity and then rendering content based on the presence or absence of these claims. This can be done directly in the Razor views. Here’s a step-by-step guide on how to implement this:
In our ListRoles view, we want to display the Edit button only if the signed-in user has satisfied EditRolePolicy. Similarly, we must display the Delete button only if the signed-in user has satisfied DeleteRolePolicy.
To satisfy EditRolePolicy, the logged-in user must have an Edit Role claim. Similarly, to satisfy DeleteRolePolicy, the logged-in user must have a Delete Role claim. We have already discussed how to assign claims to use in our Claims Based Authorization in the ASP.NET Core Identity article. For this to be implemented, we have already created the following ClaimsStore class:
using System.Security.Claims; namespace ASPNETCoreIdentityDemo.Models { public static class ClaimsStore { public static List<Claim> GetAllClaims() { return new List<Claim>() { new Claim("Create Role", "Create Role"), new Claim("Edit Role", "Edit Role"), new Claim("Delete Role", "Delete Role") }; } } }
Then to implement the Edit Role Policy and Delete Role Policy for your application, please configure the same in the Program.cs class file as follows:
builder.Services.AddAuthorization(options => { options.AddPolicy("EditRolePolicy", policy => policy.RequireClaim("Edit Role")); options.AddPolicy("DeleteRolePolicy", policy => policy.RequireClaim("Delete Role")); });
To check if the signed-in user satisfies EditRolePolicy and DeleteRolePolicy, we need to use the IAuthorizationService service:
Understanding IAuthorizationService Service:
IAuthorizationService is a service provided by ASP.NET Core that allows us to perform authorization checks. It has the AuthorizeAsync method, which we can use to determine if a user meets specific authorization requirements. The following is the signature of the AuthorizeAsync method. As you can see, this is an extension method that takes two parameters.
public static Task<AuthorizationResult> AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, string policyName)
The AuthorizeAsync method typically requires two parameters:
- A ClaimsPrincipal, which represents the user.
- The name of the policy to evaluate against this user.
The AuthorizeAsync method of the IAuthorizationService in ASP.NET Core provides a way to perform authorization checks programmatically within your code. Here’s a closer look at how to use this method:
var isAuthorized = await _authorizationService.AuthorizeAsync(User, “YourPolicyName”).Succeeded;
In this example, the AuthorizeAsync method checks if the current user (User) satisfies the policy “YourPolicyName”. The Succeeded property returns true if satisfied. Otherwise, it will return false.
Benefits of Using AuthorizeAsync
- Flexibility: It allows for more complex authorization scenarios than what’s possible with just attributes.
- Control: You can handle the outcome of the authorization check however you wish, providing custom responses.
- Dynamic Authorization: Useful in scenarios where the policy or the resource is not known at compile time.
Using AuthorizeAsync Method in ASP.NET Core MVC Views:
To check if the signed-in user satisfies EditRolePolicy and DeleteRolePolicy, inject the IAuthorizationService service into the view as follows:
@using Microsoft.AspNetCore.Authorization @inject IAuthorizationService authorizationService;
You can access claims in your Razor views using the User property. Pass the user and the name of the policy as parameters to the AuthorizeAsync() method of IAuthorizationService. Succeeded property returns true if the policy is satisfied. Otherwise false. So, modify the ListRoles.cshtml file as follows:
@model IEnumerable<ApplicationRole> @using Microsoft.AspNetCore.Authorization @inject IAuthorizationService authorizationService; @{ ViewBag.Title = "All Roles"; } <h1>All Roles</h1> @if (Model.Any()) { <a class="btn btn-primary mb-3" style="width:auto" asp-action="CreateRole" asp-controller="Administration">Add New Role</a> foreach (var role in Model) { <div class="card mb-3"> <div class="card-header"> Role Id : @role.Id </div> <div class="card-body"> <h5 class="card-title">@role.Name</h5> <h3 class="card-title">@role.Description</h3> </div> <div class="card-footer"> <form asp-action="DeleteRole" asp-route-roleId="@role.Id" method="post"> @if ((await authorizationService.AuthorizeAsync(User, "EditRolePolicy")).Succeeded) { <a asp-controller="Administration" asp-action="EditRole" asp-route-roleId="@role.Id" class="btn btn-primary"> Edit </a> } @if ((await authorizationService.AuthorizeAsync(User, "DeleteRolePolicy")).Succeeded) { <span id="confirmDeleteSpan_@role.Id" style="display:none"> <span>Are you sure you want to Delete?</span> <button type="submit" class="btn btn-danger">Yes</button> <a href="#" class="btn btn-primary" onclick="confirmDelete('@role.Id', false)">No</a> </span> <span id="deleteSpan_@role.Id"> <a href="#" class="btn btn-danger" onclick="confirmDelete('@role.Id', true)">Delete</a> </span> } </form> </div> </div> } } else { <div class="card"> <div class="card-header"> No roles created yet </div> <div class="card-body"> <h5 class="card-title"> Use the button below to create a role </h5> <a class="btn btn-primary" style="width:auto" asp-controller="Administration" asp-action="CreateRole"> Create Role </a> </div> </div> } <script> function confirmDelete(uniqueId, isTrue) { var deleteSpan = 'deleteSpan_' + uniqueId; var confirmDeleteSpan = 'confirmDeleteSpan_' + uniqueId; if (isTrue) { $('#' + deleteSpan).hide(); $('#' + confirmDeleteSpan).show(); } else { $('#' + deleteSpan).show(); $('#' + confirmDeleteSpan).hide(); } } </script>
With these changes, run the application and see whether it works as expected. Authorization Checks in views alone are not enough. That means it is not enough to just show or hide UI elements on the view. The respective controller actions must also be protected. Otherwise, the user can directly type the URL in the address bar and access the resources.
IAuthorizationService Service Injection in Multiple Views
If you need IAuthorizationService in multiple views, consider importing this service inside the _ViewImports.cshtml file so that you do not have to import it in every individual view. So, modify the _ViewImports.cshtml file as follows:
@using ASPNETCoreIdentityDemo @using ASPNETCoreIdentityDemo.Models @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @using Microsoft.AspNetCore.Authorization
In the next article, I will discuss How to Add or Remove Role Claims in ASP.NET Core Identity. In this article, I explain How to Implement Role and Claim-Based Authorization in ASP.NET Core MVC Views with an Example. I hope you enjoy the Role and Claim Based Authorization article in ASP.NET Core MVC Views.