Category - MVC

Role based Authorization in ASP .NET core
Jun 14, 2024

What is Authorization?  Authorization verifies whether a user has permission to use specific applications or services. While authentication and authorization are distinct processes, authentication must precede authorization, ensuring the user's identity is confirmed before determining their access rights.    When logging into a system, a user must provide credentials like a username and password to authenticate. Next, the authorization process grants rights. For example, an administrative user can create a document library to add, edit, and delete documents, while a non-administrative user can only read documents in the library.  Types of Authorization:  Simple Authorization  Role-Based Authorization  Claim-Based Authorization  Policy-Based Authorization  I have implemented an example of role-based authorization in .NET. Step 1: Create one new MVC Web Application with the Authentication type “Individual Account”.  Step 2: Register Identity with DefaultTokenProvider in the program.cs file. builder.Services.AddIdentity<IdentityUser, IdentityRole>(options => options.SignIn.RequireConfirmedAccount = false) .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); For better understanding, I have added one page to add a new role.  Create a new method in the controller and add the following code. [HttpGet] public IActionResult Admin() { return View(); } Create a new model Role.cs.   namespace Authorization.Models { public class Role { public string RoleName { get; set; } } } Create html page for add role.  @model Role @{ ViewData["Title"] = "Admin"; } <h1>Admin</h1> <div class="row"> <div class="col-md-12"> <form method="post" action="@Url.Action("Admin","Home")"> <div class="form-group"> <label>Role Name</label> <input type="text" class="form-control" style="width:30%;" asp-for="RoleName" placeholder="Role name" required> </div> <br /> <button class="btn btn-success" type="submit">Add</button> </form> </div> </div> I have created a simple page, You can modify the page as per your requirements.  Add a new method in the controller and add the following code. And declare RoleManager<IdentityRole> and inject in the constructor. private readonly RoleManager<IdentityRole> _roleManager; public HomeController(RoleManager<IdentityRole> roleManager) { _roleManager = roleManager; } [HttpPost] public async Task<IActionResult> Admin(Role role) { var result = _roleManager.RoleExistsAsync(role.RoleName).Result; if (!result) { await _roleManager.CreateAsync(new IdentityRole(role.RoleName)); } return RedirectToAction("Admin"); } Set a new tab in _Layout.cshtml file to redirect to the Add role page.  <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Admin">Add new role</a> </li> Run the project and you will see the output. Here, You can add a new role.  Add a new field in register.cshtml using the following code to assign a role to the user.  <div class="form-floating mb-3"> <select asp-for="Input.Role" class="form-control" aria-required="true"> <option value="">Select role</option> @foreach (var item in Model.RoleList) { <option value="@item.Name">@item.Name</option> } </select> <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span> </div> To get the list of roles you can add the following code in your register.cshtml.cs file. And Add  RoleList = _roleManager.Roles in OnPostAsync method also. public IQueryable<IdentityRole> RoleList { get; set; } public async Task OnGetAsync(string returnUrl = null) { ReturnUrl = returnUrl; RoleList = _roleManager.Roles; ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); } Run the project and see the output. Now, Assign the role to the user, and for that add the following code in OnPostAsync after the user is created. await _userManager.AddToRoleAsync(user, Input.Role); Full code of register.cshtml.cs file. using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.WebUtilities; using System.ComponentModel.DataAnnotations; using System.Text; namespace Authorization.Areas.Identity.Pages.Account { public class RegisterModel : PageModel { private readonly SignInManager<IdentityUser> _signInManager; private readonly UserManager<IdentityUser> _userManager; private readonly RoleManager<IdentityRole> _roleManager; private readonly IUserStore<IdentityUser> _userStore; private readonly IUserEmailStore<IdentityUser> _emailStore; private readonly ILogger<RegisterModel> _logger; //private readonly IEmailSender _emailSender; public RegisterModel( UserManager<IdentityUser> userManager, IUserStore<IdentityUser> userStore, SignInManager<IdentityUser> signInManager, ILogger<RegisterModel> logger, RoleManager<IdentityRole> roleManager ) { _userManager = userManager; _userStore = userStore; _emailStore = GetEmailStore(); _signInManager = signInManager; _roleManager = roleManager; _logger = logger; } [BindProperty] public InputModel Input { get; set; } public IQueryable<IdentityRole> RoleList { get; set; } public string ReturnUrl { get; set; } public IList<AuthenticationScheme> ExternalLogins { get; set; } public class InputModel { [Required] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; } [Required] [Display(Name = "Role")] public string Role { 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; } } public async Task OnGetAsync(string returnUrl = null) { ReturnUrl = returnUrl; RoleList = _roleManager.Roles; ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); } public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl ??= Url.Content("~/"); RoleList = _roleManager.Roles; ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); if (ModelState.IsValid) { var user = CreateUser(); await _userStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); await _emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); var result = await _userManager.CreateAsync(user, Input.Password); if (result.Succeeded) { _logger.LogInformation("User created a new account with password."); await _userManager.AddToRoleAsync(user, Input.Role); var userId = await _userManager.GetUserIdAsync(user); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); 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); } } return Page(); } private IdentityUser CreateUser() { try { return Activator.CreateInstance<IdentityUser>(); } catch { throw new InvalidOperationException($"Can't create an instance of '{nameof(IdentityUser)}'. " + $"Ensure that '{nameof(IdentityUser)}' is not an abstract class and has a parameterless constructor, or alternatively " + $"override the register page in /Areas/Identity/Pages/Account/Register.cshtml"); } } private IUserEmailStore<IdentityUser> GetEmailStore() { if (!_userManager.SupportsUserEmail) { throw new NotSupportedException("The default UI requires a user store with email support."); } return (IUserEmailStore<IdentityUser>)_userStore; } } } Now, register one new user and assign a role to them. For example, I have created one user and assign “Admin” to them. I have added two new methods in the controller and added a default view for that.  [Authorize(Roles = "User")] public IActionResult UserRoleCheck() { return View(); } [Authorize(Roles = "Admin")] public IActionResult AdminRoleCheck() { return View(); } I have set the Authorize attribute with the role name on both authorization methods. Now, I am running the project and clicking on Admin Role, It will open the page of admin because the logged user role and method role both are the same.  If I click on User Role, It will give an Access denied error. Because logged user role and method role both are different. Here, I am using by default access denied page of identity. You can use custom page also, Just set this path to program.cs file. builder.Services.ConfigureApplicationCookie(options => { options.AccessDeniedPath = "/Identity/Account/AccessDenied"; // Customize this path as per your application's structure }); Using this way you will implement the role-based authorization in your application. Conclusion  By properly implementing authorization in your applications, you can ensure that resources and sensitive information are accessible only to authorized users. Remember to choose the appropriate authorization technique based on your application’s requirements and complexity.

ASP .NET Core Two-Factor Authentication
Feb 21, 2024

What is Authentication?  Authentication is the process of validating the identity of a user or system attempting to access a protected resource. In C# programming, authentication is commonly implemented in various scenarios, including web applications, desktop applications, and services.  Types of Authentications  Basic Authentication  Password-based Authentication  Multi-Factor Authentication  Token-based Authentication  Let’s understand authentication with example. Here I am taking one example of MFA (Two-factor authentication).  Step 1: Create the MVC Web Application  Open Visual Studio and select File >> New >> Project. After selecting the project, a “New Project” dialog will open. Select ASP.NET Core web app (Model-View-Controller) and press Next and enter project name and click Next.      Choose 'Individual Account' as the authentication type and click 'Create' to generate the project.      Step 2: Adding QR Codes to configure two-factor authentication  We will be using a QR code to configure and sync the Google authenticator app with our web app. Download the qrcode.js JavaScript library from https://davidshimjs.github.io/qrcodejs/ and put it into the “wwwroot\lib” folder in your application. Now, your “wwwroot” folder will have the following structure.      Now, Add new scaffolded item in your project by right click on Area folder and select New scaffolded Item under Add section.  Select Identity section on left sidebar and click on Add.      Now, Select the identity files that you have to add to your project but select file “Account/Manage/EnableAuthenticator” is compulsory for 2FA.  Select the DbContext Class of your project and click on add.   Open the “Views\Manage\EnableAuthenticator.cshtml” file. You will find @section Scripts at the end of the file. Put the following code in it.  @section Scripts { @await Html.PartialAsync("_ValidationScriptsPartial") <script src="~/lib/qrcode/qrcode.js"></script> <script type="text/javascript"> new QRCode(document.getElementById("qrCode"), { text: "@Html.Raw(Model.AuthenticatorUri)", width: 200, height: 200 }); </script> }   Note: Change your script path as per your folder structure.  This “EnableAuthenticator.cshtml” file already has a div with the id “qrCode” (see the code snippet below). We are generating a QR code inside that div using the qrcode.js library. We are also defining the dimensions of the QR code in terms of width and height.  So finally, your “EnableAuthenticator.cshtml” file will look like this. @page @model EnableAuthenticatorModel @{ ViewData["Title"] = "Configure authenticator app"; ViewData["ActivePage"] = ManageNavPages.TwoFactorAuthentication; } <partial name="_StatusMessage" for="StatusMessage" /> <h3>@ViewData["Title"]</h3> <div> <p>To use an authenticator app go through the following steps:</p> <ol class="list"> <li> <p> Download a two-factor authenticator app like Microsoft Authenticator for <a href="https://go.microsoft.com/fwlink/?Linkid=825072">Android</a> and <a href="https://go.microsoft.com/fwlink/?Linkid=825073">iOS</a> or Google Authenticator for <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&amp;hl=en">Android</a> and <a href="https://itunes.apple.com/us/app/google-authenticator/id388497605?mt=8">iOS</a>. </p> </li> <li> <p>Scan the QR Code or enter this key <kbd>@Model.SharedKey</kbd> into your two factor authenticator app. Spaces and casing do not matter.</p> <div class="alert alert-info">Learn how to <a href="https://go.microsoft.com/fwlink/?Linkid=852423">enable QR code generation</a>.</div> <div id="qrCode"></div> <div id="qrCodeData" data-url="@Model.AuthenticatorUri"></div> </li> <li> <p> Once you have scanned the QR code or input the key above, your two factor authentication app will provide you with a unique code. Enter the code in the confirmation box below. </p> <div class="row"> <div class="col-md-6"> <form id="send-code" method="post"> <div class="form-floating mb-3"> <input asp-for="Input.Code" class="form-control" autocomplete="off" placeholder="Please enter the code."/> <label asp-for="Input.Code" class="control-label form-label">Verification Code</label> <span asp-validation-for="Input.Code" class="text-danger"></span> </div> <button type="submit" class="w-100 btn btn-lg btn-primary">Verify</button> <div asp-validation-summary="ModelOnly" class="text-danger" role="alert"></div> </form> </div> </div> </li> </ol> </div> @section Scripts { @await Html.PartialAsync("_ValidationScriptsPartial") <script src="~/lib/qrcode/qrcode.js"></script> <script type="text/javascript"> new QRCode(document.getElementById("qrCode"), { text: "@Html.Raw(Model.AuthenticatorUri)", width: 200, height: 200 }); </script> } When we execute the program, a QR code will be generated in this View. Then you can set up two factor authentication using the Google authenticator with the help of this QR code.  Step 3: Configure two-factor authentication  Before running the application, we need to apply migrations to our app. Navigate to Tools >> NuGet Package Manager >> Package Manager Console. It will open the Package Manager Console. Put in the “Update-Database” command and hit Enter. This will update the database using Entity Framework Code First Migrations. Run the application and click on “Register” in the top right corner of the homepage. You can see a user registration page. Fill in the details and click on the “Register” button as shown in the image below.  Upon successful registration, you will be logged into the application and navigated to the home page. Here, you can see your registered Email id at the top right corner of the page. Click on it to navigate to the “Manage your account” page. Select “TwoFactorAuthentication” from the left menu. You will see a page similar to that shown below.       Click on the “Set up authenticator app” button. You can see a QR code generated on your screen — it is asking for a “Verification Code”, also as shown in the image below.    You need to install the Google Authenticator/Microsoft Authenticator app on your smartphone. It will allow you to scan this QR code in order to generate a Verification Code and complete a two-factor authentication setup.  Open Microsoft Authenticator and click on verified IDs at the bottom. Click on “Scan a barcode” and scan the QR code generated by the web app. This will add a new account to Microsoft authenticator and generate a six-digit pin on your mobile screen. This is our two-factor authentication code. This is a TOTP ( time-based one-time password). You can observe that it keeps on changing frequently (life span of 30 seconds).  Put this pin in the Verification Code textbox and click on verify. Upon successful verification, you will see a screen similar to the one shown below. This will give you the recovery codes for your account that will help to recover your account in case you are locked out. Take a note of these codes and keep them somewhere safe.    Logout of the application and click on login again. Enter your registered email id and password and click on login.    Now you can see a the two-factor authentication screen asking for the Authenticator code. Put in the code that is generated in your Google Authenticator app and click on Login. You will be successfully logged into the application and navigated to the home page. 

magnusminds website loader