Tuesday, 9 June 2020

Entity Framework vs Dapper

Entity Framework vs Dapper?

When trying to decide between using the Entity Framework and Dapper as an ORM, what are the advantages and disadvantages of Entity Framework and Dapper?

Answer

Entity Framework (EF) and Dapper both are object-relational mappers that enable .NET developers to work with relational data using domain-specific objects. Dapper owns the title of King of Micro ORM in terms of performance.

Entity Framework

Advantages

  • Entity Framework allows you to create a model by writing code or using boxes and lines in the EF Designer and generate a new database.
  • You can write code against the Entity Framework, and the system will automatically produce objects for you as well as track changes on those objects and simplify the process of updating the database.
  • One common syntax (LINQ) for all object queries whether it is a database or not and pretty fast if used as intended, easy to implement and less coding required to accomplish complex tasks.
  • The EF can replace a large chunk of code you would otherwise have to write and maintain yourself.
  • It provides auto-generated code
  • It reduces development time and cost.
  • It enables developers to visually design models and mapping of database

Disadvantages

  • You have to think in a non-traditional way of handling data, not available for every database.
  • If there is any schema change in database FE won't work and you have to update the schema in solution as well.
  • The EF queries are generated by the provider that we cannot control.
  • It is not good for a huge domain model.
  • Lazy loading is the main drawbacks of EF

Dapper

Advantages

  • Dapper make it easy to correctly parameterize queries
  • It can easily execute queries (scalar, multi-rows, multi-grids, and no-results)
  • Make it easy to turn results into objects
  • It is very efficient and owns the title of King of Micro ORM in terms of performance.

Disadvantages

  • Dapper can't generate a class model for you
  • It cannot generate queries for you
  • It cannot track objects and their changes
  • The raw dapper library doesn't provide CRUD features, but the "contrib" additional package does provide basic CRUD.

Monday, 8 June 2020

Browser back button issue after logout

The browser Back button is an option to go back to previously visited pages. The back button can be considered as a pointer that is linked to the page previously visited by the user. Browser keeps a stack of the web pages visited as a doubly-linked list.

The back button works by stepping through the history of HTTP requests which is maintained by the browser itself. This history is stored in browsers cache that consists of the entire page content with resources like image and scripts. This enables browser to navigate backwards and forwards through the browser history and have each page displayed instantly from cache without the delay of having it retransmitted over the internet from the server.

Just to handle the scenario of getting page content from server, browsers have a Refresh button that transmits the request to web server and get back the fresh copy of entire page. Internally, this also replaces the copy of the page in the browser's cache.

So, what's the basic reason behind it? It's, browser's Cache!
Now, what can be done to handle the scenario? Surely on logout event one does clear the session. Post which, browsers cache needs to be handled such that browser has no history (this will make back/forward button in browser grayed out disabled.) Here are various ways of how one can do it:

Option #1: Set Response Cache settings in code-behind file for a page
// Code disables caching by browser.
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
Response.Cache.SetNoStore();
Option #2: Set META tag for HTTP cache settings in your ASPX page header
<META Http-Equiv="Cache-Control" Content="no-cache"/>
<META Http-Equiv="Pragma" Content="no-cache"/>
<META Http-Equiv="Expires" Content="0"/>
Option #3: Clear browser's history through JavaScript using script tag
//clears browser history and redirects url
<SCRIPT LANGUAGE="javascript">
function ClearHistory()
{
     var backlen = history.length;
     history.go(-backlen);
     window.location.href = loggedOutPageUrl
}
</SCRIPT>
Option #4: Clear browser's history through JavaScript injecting through code-behind file via Response
protected void LogOut()
{
     Session.Abandon();
     string loggedOutPageUrl = "Logout.aspx";
     Response.Write("<script language="'javascript'">");
     Response.Write("function ClearHistory()");
     Response.Write("{");
     Response.Write(" var backlen=history.length;");
     Response.Write(" history.go(-backlen);");
     Response.Write(" window.location.href='" + loggedOutPageUrl + "'; ");
     Response.Write("}");
     Response.Write("</script>");
}
Option #5: Clear browser's history through JavaScript injecting through code-behind file via Page.ClientScript
Page.ClientScript.RegisterStartupScript(this.GetType(),"clearHistory","ClearHistory();",true);

Sunday, 7 June 2020

ASP.NET Core MVC - Authentication And Role Based Authorization With ASP.NET Core Identity

The ASP.NET Core Identity is a membership system, which allows us to add authentication and authorization functionality to our Application. A user can create his/her own account with it and access the system, which is based on his/her roles or claims. It can configure with SQL Server database. It also provides the features to authenticate a user, which is based on his/her external login providers such as Facebook Google, Twitter etc.

The authentication means who is the user. It is the process of verifying the identity of a user by the credentials. The back-end Application accesses after user successful login, so this login process is called authentication. The user holds the credential, which is the combination of the username and password.

The authorization means what user is allowed to do. It is the mechanism in the Application, which determines what level of access for resources by a particular action authenticates the user. Suppose an Application has a functionality to add and edit a user. The authorization mechanism determines who is allowed to add the user or who can edit a user. 

The source code of this article is available at MSDN Sample

Implement ASP.NET Core Identity

First, create an empty ASP.NET Core project. As this project doesn’t hold default implementation of ASP.NET Core Identity, so we build an Application step by step with ASP.NET Core Identity. We don’t have the default implementation of ASP.NET Core Identity due to which project.json file doesn't have any identity NuGet packages. Now, open the project.json file and modify it to add the dependencies and the tools, as per the code snippet given below. 

  1. {  
  2.   "dependencies": {  
  3.     "Microsoft.NETCore.App": {  
  4.       "version""1.1.0",  
  5.       "type""platform"  
  6.     },  
  7.     "Microsoft.AspNetCore.Razor.Tools": {  
  8.       "version""1.0.0-preview2-final",  
  9.       "type""build"  
  10.     },  
  11.     "Microsoft.EntityFrameworkCore.Tools": {  
  12.       "version""1.0.0-preview2-final",  
  13.       "type""build"  
  14.     },  
  15.     "BundlerMinifier.Core""2.2.306",  
  16.     "Microsoft.AspNetCore.Mvc""1.1.0",  
  17.     "Microsoft.AspNetCore.Server.IISIntegration""1.1.0",  
  18.     "Microsoft.AspNetCore.Server.Kestrel""1.1.0",  
  19.     "Microsoft.AspNetCore.StaticFiles""1.1.0",  
  20.     "Microsoft.EntityFrameworkCore.SqlServer""1.1.0",  
  21.     "Microsoft.EntityFrameworkCore.SqlServer.Design""1.1.0",  
  22.     "Microsoft.Extensions.Configuration.EnvironmentVariables""1.1.0",  
  23.     "Microsoft.Extensions.Configuration.Json""1.1.0",  
  24.     "Microsoft.Extensions.Logging""1.1.0",  
  25.     "Microsoft.Extensions.Logging.Console""1.1.0",  
  26.     "Microsoft.Extensions.Logging.Debug""1.1.0",  
  27.     "Microsoft.Extensions.Options.ConfigurationExtensions""1.1.0",  
  28.     "Microsoft.VisualStudio.Web.BrowserLink.Loader""14.1.0",  
  29.     "Microsoft.AspNetCore.Authentication.Cookies""1.1.0",  
  30.     "Microsoft.AspNetCore.Diagnostics""1.1.0",  
  31.     "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore""1.1.0",  
  32.     "Microsoft.AspNetCore.Identity.EntityFrameworkCore""1.1.0",  
  33.     "Microsoft.AspNetCore.Routing""1.1.0"  
  34.   },  
  35.   
  36.   "tools": {  
  37.     "Microsoft.AspNetCore.Razor.Tools""1.0.0-preview2-final",  
  38.     "Microsoft.AspNetCore.Server.IISIntegration.Tools""1.0.0-preview2-final",  
  39.     "Microsoft.EntityFrameworkCore.Tools""1.0.0-preview2-final"  
  40.   },  
  41.   
  42.   "frameworks": {  
  43.     "netcoreapp1.1": {  
  44.       "imports": [  
  45.         "dotnet5.6",  
  46.         "portable-net45+win8"  
  47.       ]  
  48.     }  
  49.   },  
  50.   
  51.   "buildOptions": {  
  52.     "emitEntryPoint"true,  
  53.     "preserveCompilationContext"true  
  54.   },  
  55.   
  56.   "runtimeOptions": {  
  57.     "configProperties": {  
  58.       "System.GC.Server"true  
  59.     }  
  60.   },  
  61.   
  62.   "publishOptions": {  
  63.     "include": [  
  64.       "wwwroot",  
  65.       "**/*.cshtml",  
  66.       "appsettings.json",  
  67.       "web.config"  
  68.     ]  
  69.   },  
  70.   
  71.   "scripts": {  
  72.     "prepublish": [ "bower install""dotnet bundle" ],  
  73.     "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]  
  74.   }  
  75. }   

This file includes NuGet packages for both Entity Framework Core and ASP.NET Identity. 

Now, create custom ApplicationUser class, which inherits IdentityUser class. This class holds the additional field for the identity user. The IdentityUser class holds user basic information such as Email, UserName, Password etc. The ApplicationUser class extends this class. It is used to store the user information. The code snippet is given below for the same. 

  1. using Microsoft.AspNetCore.Identity.EntityFrameworkCore;  
  2.   
  3. namespace IdentitySampleApplication.Data  
  4. {  
  5.     public class ApplicationUser:IdentityUser  
  6.     {  
  7.         public string Name { get; set; }  
  8.     }  
  9. }    

Now, create custom ApplicationRole class, which inherits IdenityRole class. This class holds the additional fields for the identity role.The IdentityRole class holds the role information such as RoleName. The ApplicationRole class extends this class.  It is used to store the role information. The code snippet is given below for the same. 

  1. using Microsoft.AspNetCore.Identity.EntityFrameworkCore;  
  2. using System;  
  3.   
  4. namespace IdentitySampleApplication.Data  
  5. {  
  6.     public class ApplicationRole:IdentityRole  
  7.     {  
  8.         public string Description { get; set; }  
  9.         public DateTime CreatedDate { get; set; }  
  10.         public string IPAddress { get; set; }  
  11.     }  
  12. }   

To perform the database operations, we create an IdentityDbContext class named ApplicationDbContext, as per the code snippet given below. 

  1. using Microsoft.AspNetCore.Identity.EntityFrameworkCore;  
  2. using Microsoft.EntityFrameworkCore;  
  3.   
  4. namespace IdentitySampleApplication.Data  
  5. {  
  6.     public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>  
  7.     {  
  8.         public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)  
  9.         {  
  10.   
  11.         }  
  12.     }  
  13. }   

This class needs to know that which type Application user and role are dealing with the Application. We passed ApplicationUser and ApplicationRole as a parameter, while creating the object of ApplicationDbContext class. Here, the third parameter represents the primary key data type for both IdentityUser and IdentityRole.

The Application needs to configure to implement ASP.NET Core Identity. Now, add the identity in the method Configure of the Startup class. The code snippet is given below to add the identity in the Application.

  1. app.UseIdentity();   

As the concept of Dependency Injection is central to ASP.NET Core Application, we register context and identity to Dependency Injection during the Application start up. Thus, we register these as a Service in the ConfigureServices method of the StartUp class, as per the code snippet given below. 

  1. public void ConfigureServices(IServiceCollection services)  
  2.      {  
  3.          services.AddDbContext<ApplicationDbContext>(options =>  
  4.              options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));  
  5.   
  6.          services.AddIdentity<ApplicationUser, ApplicationRole>()  
  7.              .AddEntityFrameworkStores<ApplicationDbContext>()  
  8.              .AddDefaultTokenProviders();              
  9.          services.AddMvc();  
  10.      }   

Here, the DefaultConnection is the connection string, which is defined in appsettings.json file, as per the code snippet given below. 

  1. {  
  2.   "ConnectionStrings": {  
  3.     "DefaultConnection""Data Source=DESKTOP-RG33QHE;Initial Catalog=IdentitySampleDb;User ID=sa; Password=****"  
  4.   },  
  5.   "Logging": {  
  6.     "IncludeScopes"false,  
  7.     "LogLevel": {  
  8.       "Default""Debug",  
  9.       "System""Information",  
  10.       "Microsoft""Information"  
  11.     }  
  12.   }  
  13. }   

Now, we have configured settings to create the database, so we have time to create a database, using migration. For database migration , we need to follow the following steps.

  1. Tools -> NuGet Package Manager -> Package Manager Console.
  2. Run PM> Add-Migration MyFirstMigration to scaffold a migration to create the initial set of tables for our model. If we receive an error , which states the term `add-migration' is not recognized as the name of a cmdlet, then close and reopen Visual Studio.
  3. Run PM> Update-Database to apply the new migration to the database. Since our database doesn't exist yet, it will be created for us before the migration is applied.

Application Roles

This section demonstrates that how to create, edit and delete the identity roles. There is a RoleManager, class which exposes the role related APIs. This creates a role in the Application and store in the database.

Now, we proceed to the controller. We create a controller named ApplicationRoleController under the Controllers folder of the Application. It has all ActionResult methods for the end user interface of the operations. We create RoleManager instance. Subsequently, we inject this in the controller's constructor to get its object. The following is a partial code snippet for the ApplicationRoleController in which RoleManager is injected, using constructor Dependency Injection. 

  1. using IdentitySampleApplication.Data;  
  2. using IdentitySampleApplication.Models;  
  3. using Microsoft.AspNetCore.Http;  
  4. using Microsoft.AspNetCore.Identity;  
  5. using Microsoft.AspNetCore.Mvc;  
  6. using System;  
  7. using System.Collections.Generic;  
  8. using System.Linq;  
  9. using System.Threading.Tasks;  
  10.   
  11. namespace IdentitySampleApplication.Controllers  
  12. {  
  13.     public class ApplicationRoleController : Controller  
  14.     {  
  15.         private readonly RoleManager<ApplicationRole> roleManager;  
  16.   
  17.         public ApplicationRoleController(RoleManager<ApplicationRole> roleManager)  
  18.         {  
  19.             this.roleManager = roleManager;  
  20.         }  
  21.     }  
  22. }   

We can notice that Controller takes RoleManager as a constructor parameter. ASP.NET Core Dependency Injection will take care of passing an instance of RoleManager into ApplicationRoleController. The controller is developed to handle the operations requests for the Application identity role. Now, let's develop the user interface for the Role Listing, Add Role, Edit Role and Delete Role. Let's see each one by one.

Role List View

This is the first view when the Application is accessed or the entry point of the Application is executed. It shows the role listing, as shown in Figure 1. The role data is displayed in a tabular format and on this view, it has linked to adding a new role, edit a role and delete a role.

To pass the data from controller to view, create named ApplicationRoleListViewModel view model, as per the code snippet, mentioned below. This view model is used for role listing. 

  1. namespace IdentitySampleApplication.Models  
  2. {  
  3.     public class ApplicationRoleListViewModel  
  4.     {  
  5.         public string Id { get; set; }  
  6.         public string RoleName { get; set; }  
  7.         public string Description { get; set; }  
  8.         public int NumberOfUsers { get; set; }  
  9.     }  
  10.  

Now, we create action method, which returns an index view with the data. The code snippet of an Index action method in ApplicationRoleController is mentioned below. 

  1. [HttpGet]  
  2.      public IActionResult Index()  
  3.      {  
  4.          List<ApplicationRoleListViewModel> model = new List<ApplicationRoleListViewModel>();  
  5.          model = roleManager.Roles.Select(r => new ApplicationRoleListViewModel  
  6.          {  
  7.              RoleName = r.Name,  
  8.              Id = r.Id,  
  9.              Description = r.Description,  
  10.              NumberOfUsers = r.Users.Count  
  11.          }).ToList();  
  12.          return View(model);  
  13.      }   

Now, we create an index view, as per the code snippet, mentioned below under the ApplicationRole folder of views. 

  1. @model IEnumerable<ApplicationRoleListViewModel>  
  2. @using IdentitySampleApplication.Models  
  3. @using IdentitySampleApplication.Code  
  4.   
  5. <div class="top-buffer"></div>  
  6. <div class="panel panel-primary">  
  7.     <div class="panel-heading panel-head">Application Roles</div>  
  8.     <div class="panel-body">  
  9.         <div class="btn-group">  
  10.             <a id="createRoleModal" data-toggle="modal" asp-action="AddEditApplicationRole" data-target="#modal-action-application-role" class="btn btn-primary">  
  11.                 <i class="glyphicon glyphicon-plus"></i>  Add Role  
  12.             </a>  
  13.         </div>  
  14.         <div class="top-buffer"></div>  
  15.         <table class="table table-bordered table-striped table-condensed">  
  16.             <thead>  
  17.                 <tr>  
  18.                     <th>Name</th>  
  19.                     <th>Description</th>  
  20.                     <th>Users</th>                      
  21.                     <th>Action</th>  
  22.                 </tr>  
  23.             </thead>  
  24.             <tbody>  
  25.                 @foreach (var item in Model)  
  26.                 {  
  27.                     <tr>  
  28.                         <td>@item.RoleName</td>  
  29.                         <td>@item.Description</td>    
  30.                         <td>@item.NumberOfUsers</td>                                                
  31.                         <td>                              
  32.                             <a id="addEditApplicationRoleModal" data-toggle="modal" asp-action="AddEditApplicationRole" asp-route-id="@item.Id" data-target="#modal-action-application-role"  
  33.                                class="btn btn-info">  
  34.                                 <i class="glyphicon glyphicon-pencil"></i>  Edit  
  35.                             </a>  
  36.                             @if (item.NumberOfUsers == 0)  
  37.                             {  
  38.                             <a id="deleteApplicationRoleModal" data-toggle="modal" asp-action="DeleteApplicationRole" asp-route-id="@item.Id" data-target="#modal-action-application-role" class="btn btn-danger">  
  39.                                 <i class="glyphicon glyphicon-trash"></i>  Delete  
  40.                             </a>  
  41.                             }  
  42.                         </td>  
  43.                     </tr>  
  44.                 }  
  45.             </tbody>  
  46.         </table>  
  47.     </div>  
  48. </div>  
  49.   
  50. @Html.Partial("_Modal"new BootstrapModel { ID = "modal-action-application-role", AreaLabeledId = "modal-action-application-role-label", Size = ModalSize.Medium })  
  51.   
  52. @section scripts  
  53. {  
  54.     <script src="~/js/application-role-index.js" asp-append-version="true"></script>  
  55. }   

It shows how to create and delete forms in the Bootstrap model popup, so create the Application-role - index.js file, as per the code snippet given below. 

  1. (function ($) {  
  2.     function ApplicationRole() {  
  3.         var $this = this;  
  4.   
  5.         function initilizeModel() {  
  6.             $("#modal-action-application-role").on('loaded.bs.modal'function (e) {  
  7.   
  8.             }).on('hidden.bs.modal'function (e) {  
  9.                 $(this).removeData('bs.modal');  
  10.             });  
  11.         }  
  12.         $this.init = function () {  
  13.             initilizeModel();  
  14.         }  
  15.     }  
  16.     $(function () {  
  17.         var self = new ApplicationRole();  
  18.         self.init();  
  19.     })  
  20. }(jQuery))   

When the Application runs and calls the index() action method from ApplicationRoleController with a HttpGet request, it gets all the roles listed in the UI, as shown in Figure 1.


Figure 1: Application Role Listing

Add/Edit Application Role

To pass the data from UI to a controller to add and edit an Application role, use view model named ApplicationRoleViewModel. The code snippet is given below for the same. 

  1. using System.ComponentModel.DataAnnotations;  
  2.   
  3. namespace IdentitySampleApplication.Models  
  4. {  
  5.     public class ApplicationRoleViewModel  
  6.     {  
  7.         public string Id { get; set; }  
  8.         [Display(Name ="Role Name")]  
  9.         public string RoleName { get; set; }  
  10.         public string Description { get; set; }  
  11.     }  
  12. }   

The ApplicationRoleController has an action method named AddEditApplicationRole, which returns the view to add and edit an Application role. As we add and edit Application, the role is using same UI screen. The code snippet mentioned below is for same action method for both GET and Post requests. 

  1. [HttpGet]  
  2.         public async Task<IActionResult> AddEditApplicationRole(string id)  
  3.         {  
  4.             ApplicationRoleViewModel model = new ApplicationRoleViewModel();  
  5.             if (!String.IsNullOrEmpty(id))  
  6.             {  
  7.                 ApplicationRole applicationRole = await roleManager.FindByIdAsync(id);  
  8.                 if (applicationRole != null)  
  9.                 {  
  10.                     model.Id = applicationRole.Id;  
  11.                     model.RoleName = applicationRole.Name;  
  12.                     model.Description = applicationRole.Description;  
  13.                 }  
  14.             }  
  15.             return PartialView("_AddEditApplicationRole", model);  
  16.         }  
  17.         [HttpPost]  
  18.         public async Task<IActionResult> AddEditApplicationRole(string id, ApplicationRoleViewModel model)  
  19.         {  
  20.             if (ModelState.IsValid)  
  21.             {  
  22.                 bool isExist = !String.IsNullOrEmpty(id);  
  23.                 ApplicationRole applicationRole = isExist ? await roleManager.FindByIdAsync(id) :  
  24.                new ApplicationRole  
  25.                {                     
  26.                    CreatedDate = DateTime.UtcNow                     
  27.                };  
  28.                 applicationRole.Name = model.RoleName;  
  29.                 applicationRole.Description = model.Description;  
  30.                 applicationRole.IPAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString();  
  31.                 IdentityResult roleRuslt = isExist?  await roleManager.UpdateAsync(applicationRole)  
  32.                                                     : await roleManager.CreateAsync(applicationRole);  
  33.                 if (roleRuslt.Succeeded)  
  34.                 {  
  35.                     return RedirectToAction("Index");  
  36.                 }  
  37.             }  
  38.             return View(model);  
  39.         }   

There are three asynchronous methods, which are used of the RoleManager class, which performs actions, as shown below.

  • FindByIdAsync
    This method has role Id as a parameter and returns already existing role, which is based on the input.

  • CreateAsync
    This method has new ApplicationRole as a parameter and creates new role in the Application.

  • UpdateAsync
    This method has an existing ApplicationRole as a parameter and updates that role in the Application. 

The GET request for the AddEditApplicationRole action method returns _AddEditApplicationRole partial view; the code snippet follows under the ApplicationRole folder of views. 

  1. @model ApplicationRoleViewModel  
  2. @using IdentitySampleApplication.Models  
  3.   
  4. <form asp-action="AddEditApplicationRole" role="form">  
  5.     @await Html.PartialAsync("_ModalHeader"new ModalHeader { Heading =$"{(String.IsNullOrEmpty(Model.Id)? "Add" : "Edit")} Application Role"  })  
  6.     <div class="modal-body form-horizontal">  
  7.         <div class="row">  
  8.             <div class="form-group">  
  9.                 <label asp-for="RoleName" class="col-lg-3 col-sm-3 control-label"></label>  
  10.                 <div class="col-lg-6">  
  11.                     <input asp-for="RoleName" class="form-control" />  
  12.                 </div>  
  13.             </div>  
  14.             <div class="form-group">  
  15.                 <label asp-for="Description" class="col-lg-3 col-sm-3 control-label"></label>  
  16.                 <div class="col-lg-6">  
  17.                     <input asp-for="Description" class="form-control" />  
  18.                 </div>  
  19.             </div>  
  20.         </div>  
  21.     </div>  
  22.     @await Html.PartialAsync("_ModalFooter"new ModalFooter { })  
  23. </form>   

When the Application runs and you click on the Add Role button or Edit button in the role listing, it makes a GET request for the AddEditApplicationRole() action; add/edit an Application role screen opens, as shown in Figure 2.


Figure 2: Add Application Role

Delete Application Role

The ApplicationRoleController has an action method named DeleteApplicationRole, which returns the view to delete an Application role named _DeleteApplicationRole. The code snippet mentioned below is for the same action method for both GET and Post requests. 

  1. [HttpGet]  
  2.         public async Task<IActionResult> DeleteApplicationRole(string id)  
  3.         {  
  4.             string name = string.Empty;  
  5.             if (!String.IsNullOrEmpty(id))  
  6.             {  
  7.                 ApplicationRole applicationRole = await roleManager.FindByIdAsync(id);  
  8.                 if (applicationRole != null)  
  9.                 {  
  10.                     name = applicationRole.Name;  
  11.                 }  
  12.             }  
  13.             return PartialView("_DeleteApplicationRole", name);  
  14.         }  
  15.   
  16.         [HttpPost]  
  17.         public async Task<IActionResult> DeleteApplicationRole(string id, FormCollection form)  
  18.         {  
  19.             if(!String.IsNullOrEmpty(id))  
  20.             {  
  21.                 ApplicationRole applicationRole = await roleManager.FindByIdAsync(id);  
  22.                 if (applicationRole != null)  
  23.                 {  
  24.                     IdentityResult roleRuslt = roleManager.DeleteAsync(applicationRole).Result;  
  25.                     if (roleRuslt.Succeeded)  
  26.                     {  
  27.                         return RedirectToAction("Index");  
  28.                     }  
  29.                 }  
  30.                 }  
  31.             return View();  
  32.         }   

Here DeleteAsync method of RoleManager is used, which has an existing Application role as an input parameter. It deletes an existing Application role.

The GET request for the DeleteApplicationRoler action method returns _DeleteUser partial View. The code snippet mentioned below is under the ApplicationRole folder of Views. 

  1. @model string  
  2. @using IdentitySampleApplication.Models  
  3.   
  4. <form asp-action="DeleteApplicationRole" role="form">  
  5.     @Html.Partial("_ModalHeader"new ModalHeader { Heading = "Delete Application Role" })  
  6.   
  7.     <div class="modal-body form-horizontal">  
  8.         Are you want to delete @Model?  
  9.     </div>  
  10.     @Html.Partial("_ModalFooter"new ModalFooter { SubmitButtonText = "Delete" })  
  11. </form>   

When the Application runs and a user clicks on the "Delete" button in the Application role listing, it makes a GET request for the DeleteUser() action, then the delete Application role screen is shown below.


Figure 3: Delete Application Role

Application Users

This section demonstrates that how to create, edit and delete the identity users and how to assign the role to a user. There is a RoleManager class, which exposes the role related APIs and another is UserManager, which exposes the user related APIs. This creates the user in the Application and is stored in the database. 

Now, we proceed to the controller. We create controller named UserController under the Controllers folder of the Application. It has all ActionResult methods for the end user interface of operations. We create both UserManager and RoleManager instances. Subsequently, we inject these in the controller's constructor to get its object. The following is a partial code snippet for the UserController in which UserManager and RoleManager are injected, using constructor Dependency Injection. 

  1. using IdentitySampleApplication.Data;  
  2. using IdentitySampleApplication.Models;  
  3. using Microsoft.AspNetCore.Http;  
  4. using Microsoft.AspNetCore.Identity;  
  5. using Microsoft.AspNetCore.Mvc;  
  6. using Microsoft.AspNetCore.Mvc.Rendering;  
  7. using System;  
  8. using System.Collections.Generic;  
  9. using System.Linq;  
  10. using System.Threading.Tasks;  
  11.   
  12. namespace IdentitySampleApplication.Controllers  
  13. {  
  14.     public class UserController : Controller  
  15.     {  
  16.         private readonly UserManager<ApplicationUser> userManager;  
  17.         private readonly RoleManager<ApplicationRole> roleManager;  
  18.   
  19.         public UserController(UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager)  
  20.         {  
  21.             this.userManager = userManager;  
  22.             this.roleManager = roleManager;  
  23.         }  
  24.     }  
  25. }   

We can notice that Controller takes UserManager and RoleManager as constructor parameters. ASP.NET Core Dependency Injection will take care of passing the instances of both UserManager and RoleManager into UserController. The controller is developed to handle the operations requests for the Application identity user. Now, let's develop the user interface for the user listing, add user, edit user and delete user. Let's see each one by one.

User List View

When we click on top User menu of the Application, it shows the role listing, as shown in Figure 4. The user data is displayed in a tabular format and on this view, it has linked to adding a new user, edit a user and delete a user.

To pass the data from the controller to view, create named UserListViewModel view model, as per the code snippet, mentioned below. This view model is used for the user listing. 

  1. namespace IdentitySampleApplication.Models  
  2. {  
  3.     public class UserListViewModel  
  4.     {  
  5.         public string Id { get; set; }  
  6.         public string Name { get; set; }  
  7.         public string Email { get; set; }      
  8.         public string RoleName { get; set; }      
  9.     }  
  10. }   

Now, we create an action method, which returns an index view with the data. The code snippet of an Index action method in UserController is mentioned below. 

  1. [HttpGet]  
  2.         public IActionResult Index()  
  3.         {  
  4.             List<UserListViewModel> model = new List<UserListViewModel>();  
  5.             model = userManager.Users.Select(u => new UserListViewModel  
  6.             {  
  7.                 Id = u.Id,  
  8.                 Name = u.Name,  
  9.                 Email = u.Email  
  10.             }).ToList();  
  11.             return View(model);  
  12.         }   

Now, we create an index view, as per the code snippet, mentioned below under the User folder of the views. 

  1. @model IEnumerable<UserListViewModel>  
  2. @using IdentitySampleApplication.Models  
  3. @using IdentitySampleApplication.Code  
  4.   
  5. <div class="top-buffer"></div>  
  6. <div class="panel panel-primary">  
  7.     <div class="panel-heading panel-head">Users</div>  
  8.     <div class="panel-body">  
  9.         <div class="btn-group">  
  10.             <a id="createEditUserModal" data-toggle="modal" asp-action="AddUser" data-target="#modal-action-user" class="btn btn-primary">  
  11.                 <i class="glyphicon glyphicon-plus"></i>  Add User  
  12.             </a>  
  13.         </div>  
  14.         <div class="top-buffer"></div>  
  15.         <table class="table table-bordered table-striped table-condensed">  
  16.             <thead>  
  17.                 <tr>  
  18.                     <th>Name</th>  
  19.                     <th>Email</th>                     
  20.                     <th>Action</th>  
  21.                 </tr>  
  22.             </thead>  
  23.             <tbody>  
  24.                 @foreach (var item in Model)  
  25.                 {  
  26.                     <tr>  
  27.                         <td>@item.Name</td>  
  28.                         <td>@item.Email</td>                          
  29.                         <td>  
  30.                             <a id="editUserModal" data-toggle="modal" asp-action="EditUser" asp-route-id="@item.Id" data-target="#modal-action-user"  
  31.                                class="btn btn-info">  
  32.                                 <i class="glyphicon glyphicon-pencil"></i>  Edit  
  33.                             </a>  
  34.                             <a id="deleteUserModal" data-toggle="modal" asp-action="DeleteUser" asp-route-id="@item.Id" data-target="#modal-action-user" class="btn btn-danger">  
  35.                                 <i class="glyphicon glyphicon-trash"></i>  Delete  
  36.                             </a>  
  37.                         </td>  
  38.                     </tr>  
  39.                 }  
  40.             </tbody>  
  41.         </table>  
  42.     </div>  
  43. </div>  
  44.   
  45. @Html.Partial("_Modal"new BootstrapModel { ID = "modal-action-user", AreaLabeledId = "modal-action-user-label", Size = ModalSize.Medium })  
  46.   
  47. @section scripts  
  48. {  
  49.     <script src="~/js/user-index.js" asp-append-version="true"></script>  
  50. }   

It shows all the operational forms in the Bootstrap model popup, so create the user - index.js file, as per the code snippet given below. 

  1. (function ($) {  
  2.     function User() {  
  3.         var $this = this;  
  4.   
  5.         function initilizeModel() {  
  6.             $("#modal-action-user").on('loaded.bs.modal'function (e) {  
  7.   
  8.             }).on('hidden.bs.modal'function (e) {  
  9.                 $(this).removeData('bs.modal');  
  10.             });  
  11.         }  
  12.         $this.init = function () {  
  13.             initilizeModel();  
  14.         }  
  15.     }  
  16.     $(function () {  
  17.         var self = new User();  
  18.         self.init();  
  19.     })  
  20. }(jQuery))   

When the Application runs and calls the index() action method from UserController with a HttpGet request, it gets all the users, which are listed in the UI, as shown in Figure 4.


Figure 4: User Listing

Add User

To pass the data from UI to a controller to add a user, it uses view model named UserViewModel. The code snippet is given below for the same. 

  1. using Microsoft.AspNetCore.Mvc.Rendering;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel.DataAnnotations;  
  4.   
  5. namespace IdentitySampleApplication.Models  
  6. {  
  7.     public class UserViewModel  
  8.     {  
  9.         public string Id { get; set; }  
  10.         public string UserName { get; set; }  
  11.         [DataType(DataType.Password)]  
  12.         public string Password { get; set; }  
  13.         [Display(Name="Confirm Password")]  
  14.         [DataType(DataType.Password)]  
  15.         public string ConfirmPassword { get; set; }  
  16.         public string Name { get; set; }  
  17.         public string Email { get; set; }  
  18.         public List<SelectListItem> ApplicationRoles { get; set; }  
  19.         [Display(Name = "Role")]  
  20.         public string ApplicationRoleId { get; set; }  
  21.     }  
  22. }   

The UserController has an action method, which is named as AddUser, which returns the view to add a user. The code snippet mentioned below is for same action method for both GET and Post requests. 

  1. [HttpGet]  
  2.         public IActionResult AddUser()  
  3.         {  
  4.             UserViewModel model = new UserViewModel();  
  5.             model.ApplicationRoles = roleManager.Roles.Select(r => new SelectListItem  
  6.             {  
  7.                 Text = r.Name,  
  8.                 Value = r.Id  
  9.             }).ToList();  
  10.             return PartialView("_AddUser", model);  
  11.         }  
  12.   
  13.         [HttpPost]  
  14.         public async Task<IActionResult> AddUser(UserViewModel model)  
  15.         {  
  16.             if (ModelState.IsValid)  
  17.             {  
  18.                 ApplicationUser user = new ApplicationUser  
  19.                 {  
  20.                     Name = model.Name,  
  21.                     UserName = model.UserName,  
  22.                     Email = model.Email  
  23.                 };  
  24.                 IdentityResult result = await userManager.CreateAsync(user, model.Password);  
  25.                 if (result.Succeeded)  
  26.                 {  
  27.                     ApplicationRole applicationRole = await roleManager.FindByIdAsync(model.ApplicationRoleId);  
  28.                     if (applicationRole != null)  
  29.                     {  
  30.                         IdentityResult roleResult = await userManager.AddToRoleAsync(user, applicationRole.Name);  
  31.                         if (roleResult.Succeeded)  
  32.                         {  
  33.                             return RedirectToAction("Index");  
  34.                         }  
  35.                     }  
  36.                 }  
  37.             }  
  38.             return View(model);  
  39.         }   

There are two asynchronous methods, which are used of the UserManager class, which performs an action, as shown below.

  • CreateAsync
    This method has new ApplicationUser as a parameter and creates new user in the Application.

  • AddToRoleAsync
    This method has two parameters, where one is an existing Application user and another is the role name. It assigns a role to the Application user. 

The GET request for the AddUser action method returns _AddUser partial view; the code snippet follows under the User folder of views. 

  1. @model UserViewModel  
  2. @using IdentitySampleApplication.Models  
  3.   
  4. <form asp-action="AddUser" role="form">  
  5.     @await Html.PartialAsync("_ModalHeader"new ModalHeader { Heading = "Add User" })  
  6.     <div class="modal-body form-horizontal">  
  7.         <div class="row">  
  8.             <div class="col-lg-6">  
  9.                 <div class="form-group">  
  10.                     <label asp-for="Name" class="col-lg-3 col-sm-3 control-label"></label>  
  11.                     <div class="col-lg-6">  
  12.                         <input asp-for="Name" class="form-control" />  
  13.                     </div>  
  14.                 </div>  
  15.                 <div class="form-group">  
  16.                     <label asp-for="Email" class="col-lg-3 col-sm-3 control-label"></label>  
  17.                     <div class="col-lg-6">  
  18.                         <input asp-for="Email" class="form-control" />  
  19.                     </div>  
  20.                 </div>  
  21.                 <div class="form-group">  
  22.                     <label asp-for="ApplicationRoleId" class="col-lg-3 col-sm-3 control-label"></label>  
  23.                     <div class="col-lg-6">  
  24.                         <select asp-for="ApplicationRoleId" asp-items="@Model.ApplicationRoles" class="form-control">  
  25.                             <option>Please select</option>  
  26.                         </select>  
  27.                     </div>  
  28.                 </div>  
  29.             </div>  
  30.             <div class="col-lg-6">  
  31.                 <div class="form-group">  
  32.                     <label asp-for="UserName" class="col-lg-3 col-sm-3 control-label"></label>  
  33.                     <div class="col-lg-6">  
  34.                         <input asp-for="UserName" class="form-control" />  
  35.                     </div>  
  36.                 </div>  
  37.                 <div class="form-group">  
  38.                     <label asp-for="Password" class="col-lg-3 col-sm-3 control-label"></label>  
  39.                     <div class="col-lg-6">  
  40.                         <input asp-for="Password" class="form-control" />  
  41.                     </div>  
  42.                 </div>  
  43.                 <div class="form-group">  
  44.                     <label asp-for="ConfirmPassword" class="col-lg-3 col-sm-3 control-label"></label>  
  45.                     <div class="col-lg-6">  
  46.                         <input asp-for="ConfirmPassword" class="form-control" />  
  47.                     </div>  
  48.                 </div>  
  49.             </div>  
  50.         </div>  
  51.     </div>  
  52.     @await Html.PartialAsync("_ModalFooter"new ModalFooter { })  
  53. </form>   

When the Application runs and we click on the Add User button, it makes a GET request for the AddUser() action; add a user screen, as shown in Figure 5.


Figure 5: Add User

Edit User

To pass the data from UI to the controller to edit a user, use view model named EditUserViewModel. 

  1. using Microsoft.AspNetCore.Mvc.Rendering;  
  2. using System.Collections.Generic;  
  3. using System.ComponentModel.DataAnnotations;  
  4.   
  5. namespace IdentitySampleApplication.Models  
  6. {  
  7.     public class EditUserViewModel  
  8.     {  
  9.         public string Id { get; set; }  
  10.         public string Name { get; set; }  
  11.         public string Email { get; set; }  
  12.         public List<SelectListItem> ApplicationRoles { get; set; }  
  13.         [Display(Name = "Role")]  
  14.         public string ApplicationRoleId { get; set; }  
  15.     }  
  16. }   

The UserController has an action method named EditUser, which returns the view to edit a user. The code snippet mentioned below is for the same action method for both GET and Post requests. 

  1. [HttpGet]  
  2.         public async Task<IActionResult> EditUser(string id)  
  3.         {  
  4.             EditUserViewModel model = new EditUserViewModel();  
  5.             model.ApplicationRoles = roleManager.Roles.Select(r => new SelectListItem  
  6.             {  
  7.                 Text = r.Name,  
  8.                 Value = r.Id  
  9.             }).ToList();  
  10.   
  11.             if (!String.IsNullOrEmpty(id))  
  12.             {  
  13.                 ApplicationUser user = await userManager.FindByIdAsync(id);  
  14.                 if (user != null)  
  15.                 {  
  16.                     model.Name = user.Name;  
  17.                     model.Email = user.Email;  
  18.                     model.ApplicationRoleId = roleManager.Roles.Single(r => r.Name == userManager.GetRolesAsync(user).Result.Single()).Id;  
  19.                 }  
  20.             }  
  21.             return PartialView("_EditUser", model);  
  22.         }  
  23.   
  24.         [HttpPost]  
  25.         public async Task<IActionResult> EditUser(string id, EditUserViewModel model)  
  26.         {  
  27.             if (ModelState.IsValid)  
  28.             {  
  29.                 ApplicationUser user = await userManager.FindByIdAsync(id);  
  30.                 if (user != null)  
  31.                 {  
  32.                     user.Name = model.Name;  
  33.                     user.Email = model.Email;  
  34.                     string existingRole = userManager.GetRolesAsync(user).Result.Single();  
  35.                     string existingRoleId = roleManager.Roles.Single(r => r.Name == existingRole).Id;  
  36.                     IdentityResult result = await userManager.UpdateAsync(user);  
  37.                     if (result.Succeeded)  
  38.                     {  
  39.                         if (existingRoleId != model.ApplicationRoleId)  
  40.                         {  
  41.                             IdentityResult roleResult = await userManager.RemoveFromRoleAsync(user, existingRole);  
  42.                             if (roleResult.Succeeded)  
  43.                             {  
  44.                                 ApplicationRole applicationRole = await roleManager.FindByIdAsync(model.ApplicationRoleId);  
  45.                                 if (applicationRole != null)  
  46.                                 {  
  47.                                     IdentityResult newRoleResult = await userManager.AddToRoleAsync(user, applicationRole.Name);  
  48.                                     if (newRoleResult.Succeeded)  
  49.                                     {  
  50.                                         return RedirectToAction("Index");  
  51.                                     }  
  52.                                 }  
  53.                             }  
  54.                         }  
  55.                     }  
  56.                 }  
  57.             }  
  58.             return PartialView("_EditUser", model);  
  59.         }   

There are four asynchronous methods, which are used of the UserManager class, which performs an action, as shown below.

  • FindByIdAsync
    This method has an Application user Id as a parameter and returns already existing user, which is based on the input.

  • GetRolesAsync
    This method takes an existing Application user as a parameter and returns the existing roles, which assigned to that particular user.

  • UpdateAsync
    This method has an existing ApplicationUser as a parameter and updates that user in the Application.

  • RemoveFromRoleAsync
    This method has two parameters, where one is an existing Application user and another is assigned the role name. It removes the assigned role from that Application user. 

The GET request for the EditUser action method returns _EditUser partial view; the code snippet follows under the User folder of views. 

  1. @model EditUserViewModel  
  2. @using IdentitySampleApplication.Models  
  3.   
  4. <form asp-action="EditUser" role="form">  
  5.     @await Html.PartialAsync("_ModalHeader"new ModalHeader { Heading = "Edit User" })  
  6.     <div class="modal-body form-horizontal">  
  7.         <div class="row">              
  8.             <div class="form-group">  
  9.                 <label asp-for="Name" class="col-lg-3 col-sm-3 control-label"></label>  
  10.                 <div class="col-lg-6">  
  11.                     <input asp-for="Name" class="form-control" />  
  12.                 </div>  
  13.             </div>              
  14.             <div class="form-group">  
  15.                 <label asp-for="Email" class="col-lg-3 col-sm-3 control-label"></label>  
  16.                 <div class="col-lg-6">  
  17.                     <input asp-for="Email" class="form-control" />  
  18.                 </div>  
  19.             </div>  
  20.   
  21.             <div class="form-group">  
  22.                 <label asp-for="ApplicationRoleId" class="col-lg-3 col-sm-3 control-label"></label>  
  23.                 <div class="col-lg-6">  
  24.                     <select asp-for="ApplicationRoleId" asp-items="@Model.ApplicationRoles" class="form-control">  
  25.                         <option>Please select</option>  
  26.                     </select>  
  27.                 </div>  
  28.                </div>  
  29.             </div>  
  30.     </div>  
  31.     @await Html.PartialAsync("_ModalFooter"new ModalFooter { })  
  32. </form>   

When the Application runs and you click on the Edit button in the User listing, it makes a GET request for the EditUser() action, followed by editing the user screen, which is shown in Figure 6.


Figure 6: Edit User

Delete User

The UserController has an action method named DeleteUser, which returns the view to delete a user. The code snippet mentioned below is for the same action method for both GET and Post requests. 

  1. [HttpGet]  
  2.         public async Task<IActionResult> DeleteUser(string id)  
  3.         {  
  4.             string name = string.Empty;  
  5.             if (!String.IsNullOrEmpty(id))  
  6.             {  
  7.                 ApplicationUser applicationUser = await userManager.FindByIdAsync(id);  
  8.                 if (applicationUser != null)  
  9.                 {  
  10.                     name = applicationUser.Name;  
  11.                 }  
  12.             }  
  13.             return PartialView("_DeleteUser", name);  
  14.         }  
  15.   
  16.         [HttpPost]  
  17.         public async Task<IActionResult> DeleteUser(string id, FormCollection form)  
  18.         {  
  19.             if (!String.IsNullOrEmpty(id))  
  20.             {  
  21.                 ApplicationUser applicationUser = await userManager.FindByIdAsync(id);  
  22.                 if (applicationUser != null)  
  23.                 {  
  24.                     IdentityResult result = await userManager.DeleteAsync(applicationUser);   
  25.                     if (result.Succeeded)  
  26.                     {  
  27.                         return RedirectToAction("Index");  
  28.                     }  
  29.                 }  
  30.             }  
  31.             return View();  
  32.         }   

Here, DeleteAsync method of UserManager is used, which takes an existing Application user as an input parameter. It deletes an existing Application user.

The GET request for the DeleteUser action method returns _DeleteUser partial View. The code snippet mentioned below is under the User folder of Views. 

  1. @model string  
  2. @using IdentitySampleApplication.Models  
  3.   
  4. <form asp-action="DeleteUser" role="form">  
  5.     @Html.Partial("_ModalHeader"new ModalHeader { Heading = "Delete User" })  
  6.   
  7.     <div class="modal-body form-horizontal">  
  8.         Are you want to delete @Model?  
  9.     </div>  
  10.     @Html.Partial("_ModalFooter"new ModalFooter { SubmitButtonText = "Delete" })  
  11. </form>   

When the Application runs and a user clicks on the "Delete" button in the user listing, it makes a GET request for the DeleteUser() action, then the delete user screen is shown below.


Figure 7: Delete User

Authentication and Authorisation

This section demonstrates the login and logout functionality of the Application. As the Application users are already existing in the system with a role, to implement login and logout functionality, create AccountController under the Controllers folder. This controller holds both login and logout action methods. We create SignInManager instance. Now, we inject it in the controller's constructor to get its object. The following is a partial code snippet for the AccountController in which SignInManager is injected, using constructor Dependency Injection. 

  1. using IdentitySampleApplication.Data;  
  2. using IdentitySampleApplication.Models;  
  3. using Microsoft.AspNetCore.Authorization;  
  4. using Microsoft.AspNetCore.Identity;  
  5. using Microsoft.AspNetCore.Mvc;  
  6. using System.Threading.Tasks;  
  7.   
  8.   
  9. namespace IdentitySampleApplication.Controllers  
  10. {  
  11.     public class AccountController : Controller  
  12.     {  
  13.         private readonly SignInManager<ApplicationUser> signInManager;  
  14.   
  15.         public AccountController(SignInManager<ApplicationUser> signInManager)  
  16.         {  
  17.             this.signInManager = signInManager;  
  18.         }  
  19.     }  
  20. }   

Role-Based Authorisation

The Application has a HomeController, which is accessed after successful authentication. It holds an action method named Index, which returns a view by authenticating the username. 

  1. using IdentitySampleApplication.Data;  
  2. using Microsoft.AspNetCore.Authorization;  
  3. using Microsoft.AspNetCore.Identity;  
  4. using Microsoft.AspNetCore.Mvc;  
  5.   
  6. namespace IdentitySampleApplication.Controllers  
  7. {  
  8.     [Authorize]  
  9.     public class HomeController : Controller  
  10.     {  
  11.         private readonly UserManager<ApplicationUser> userManager;  
  12.   
  13.         public HomeController(UserManager<ApplicationUser> userManager)  
  14.         {  
  15.             this.userManager = userManager;  
  16.         }  
  17.         [Authorize(Roles = "User")]  
  18.         public IActionResult Index()  
  19.         {  
  20.             string userName =  userManager.GetUserName(User);  
  21.             return View("Index",userName);  
  22.         }         
  23.     }  
  24. }   

Here, Authorize attribute is used on the controller level, which means that this controller is accessed by only authenticate users. The action method has also used Authorize attribute with roles, which represents that what role can access this action method. The code snippet given above represents that if an authenticate user has the “User” role then he is authorized to access this action. This is role based authorization. 

The GetUserName method of UserManager returns the authenticate user’s username, which is based on the user. 

The authorized GET request for the Index action method returns an Index view. The code snippet follows under the Home folder of views. 

  1. @model string  
  2. <h1> Welocome @Model</h1>   

The Application has a partial view, which is used to show following details on the top header. If the user is not authenticated, it shows Log In button on top. If the user is authenticated, it shows the username and Log Off button. 

  1. @using Microsoft.AspNetCore.Identity  
  2. @using IdentitySampleApplication.Models  
  3. @using IdentitySampleApplication.Data  
  4.   
  5. @inject SignInManager<ApplicationUser> SignInManager  
  6. @inject UserManager<ApplicationUser> UserManager  
  7.   
  8. @if (SignInManager.IsSignedIn(User))  
  9. {  
  10.     <form asp-area="" asp-controller="Account" asp-action="SignOff" method="post" id="logoutForm" class="navbar-right">  
  11.         <ul class="nav navbar-nav navbar-right">  
  12.             <li>  
  13.                 <a asp-area="" asp-controller="Manage" asp-action="Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>  
  14.             </li>  
  15.             <li>  
  16.                 <button type="submit" class="btn btn-link navbar-btn navbar-link">Log off</button>  
  17.             </li>  
  18.         </ul>  
  19.     </form>  
  20. }  
  21. else  
  22. {  
  23.     <ul class="nav navbar-nav navbar-right">        
  24.         <li><a asp-area="" asp-controller="Account" asp-action="Login">Log in</a></li>  
  25.     </ul>  
  26. }   

As per the code snippet given above, the view has Injected Dependecy on its level and created instances of both SignInManager and UserManager. The IsSignedIn method of  SignInManager class checks wheather a user login or not in the application.

Login

To pass the data from UI to a controller to login an Application user, it uses view model named LoginViewModel. The code snippet is given below for the same. 

  1. using System.ComponentModel.DataAnnotations;  
  2.   
  3. namespace IdentitySampleApplication.Models  
  4. {  
  5.     public class LoginViewModel  
  6.     {  
  7.         [Required]  
  8.         public string UserName { get; set; }  
  9.         [Required]  
  10.         [DataType(DataType.Password)]  
  11.         public string Password { get; set; }  
  12.         [Display(Name = "Remember me?")]  
  13.         public bool RememberMe { get; set; }  
  14.     }  
  15. }   

The AccountController has two action methods, where one is for GET request named Login, which returns the view to login and another has same name. It also handles POST request to login an Application user. The code snippet mentioned below is for same action method for both GET and Post requests. 

  1. [HttpGet]  
  2.         public IActionResult Login(string returnUrl = null)  
  3.         {  
  4.             ViewData["ReturnUrl"] = returnUrl;  
  5.             return View();  
  6.         }  
  7.   
  8.         [HttpPost]  
  9.         [AllowAnonymous]  
  10.         [ValidateAntiForgeryToken]  
  11.         public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)  
  12.         {  
  13.             ViewData["ReturnUrl"] = returnUrl;  
  14.             if (ModelState.IsValid)  
  15.             {                  
  16.                 var result = await signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false);  
  17.                 if (result.Succeeded)  
  18.                 {                     
  19.                     return RedirectToLocal(returnUrl);  
  20.                 }                 
  21.                 else  
  22.                 {  
  23.                     ModelState.AddModelError(string.Empty, "Invalid login attempt.");  
  24.                     return View(model);  
  25.                 }  
  26.             }             
  27.             return View(model);  
  28.         }  
  29. private IActionResult RedirectToLocal(string returnUrl)  
  30.         {  
  31.             if (Url.IsLocalUrl(returnUrl))  
  32.             {  
  33.                 return Redirect(returnUrl);  
  34.             }  
  35.             else  
  36.             {  
  37.                 return RedirectToAction(nameof(HomeController.Index), "Home");  
  38.             }  
  39.         }   

Here the Login action method has a parameter named returnUrl, which represents that a user is redirected on a page after login. Suppose an end user is not authenticated and he tries to access the internal page via URL, then this internal page URL is stored in this parameter and the user is redirected on the login screen. Afterwards, the user authenticates from the login screen, followed by redirecting on that URL page rather than a regular redirection.

The SignInManager class exposes API methods, which is used to manage sign in operations. There is a asynchronous method, which is PasswordSignInAsync. This method takes the username and password of a user as inputs and checks its validity and issues the Application cookie, if they are correct.

The GET request for the Login action method returns Login view and the code snippet follows under the Account folder of views. 

  1. @model LoginViewModel  
  2. @using IdentitySampleApplication.Models  
  3.   
  4. <div class="row">  
  5.     <div class="col-md-3"></div>  
  6.     <div class="col-md-6">  
  7.         <div class="top-buffer"></div>  
  8.         <div class="panel panel-primary">  
  9.             <div class="panel-heading">Login</div>  
  10.             <div class="panel-body">  
  11.                 <section>  
  12.                     <form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal">  
  13.                         <h4>Use a local account to log in.</h4>  
  14.                         <hr />  
  15.                         <div asp-validation-summary="All" class="text-danger"></div>  
  16.                         <div class="form-group">  
  17.                             <label asp-for="UserName" class="col-md-2 control-label"></label>  
  18.                             <div class="col-md-10">  
  19.                                 <input asp-for="UserName" class="form-control" />  
  20.                                 <span asp-validation-for="UserName" class="text-danger"></span>  
  21.                             </div>  
  22.                         </div>  
  23.                         <div class="form-group">  
  24.                             <label asp-for="Password" class="col-md-2 control-label"></label>  
  25.                             <div class="col-md-10">  
  26.                                 <input asp-for="Password" class="form-control" />  
  27.                                 <span asp-validation-for="Password" class="text-danger"></span>  
  28.                             </div>  
  29.                         </div>  
  30.                         <div class="form-group">  
  31.                             <div class="col-md-offset-2 col-md-10">  
  32.                                 <div class="checkbox">  
  33.                                     <label asp-for="RememberMe">  
  34.                                         <input asp-for="RememberMe" />  
  35.                                         @Html.DisplayNameFor(m => m.RememberMe)  
  36.                                     </label>  
  37.                                 </div>  
  38.                             </div>  
  39.                         </div>  
  40.                         <div class="form-group">  
  41.                             <div class="col-md-offset-2 col-md-10">  
  42.                                 <button type="submit" class="btn btn-primary">Log in</button>  
  43.                             </div>  
  44.                         </div>  
  45.                     </form>  
  46.                 </section>  
  47.             </div>  
  48.         </div>  
  49.     </div>  
  50.     <div class="col-md-3"></div>  
  51.     </div>   

When the Application runs and you click on the LogIn button, it makes a GET request for the Login() action and show the login screen, as shown in Figure 8.


Figure 8: Login Screen

Now, we login with the valid credentials of a user, which has “User” role as well, followed by redirecting on the Index action method of HomeController. The Index view shows is shown in Figure 9.


Figure 9: Home screen

Now, we login with valid credentials of a user while that user doesn’t have “User” role as well then it redirects on the AccessDenied action method of AccountController. 

  1. public IActionResult AccessDenied()  
  2.         {  
  3.             return View();  
  4.         }   

The AccessDenied action method returns AccessDenied view, which has the code snippet given below. 

  1. <div class="row">  
  2.     <div class="col-md-3"></div>  
  3.     <div class="col-md-6">  
  4.         <div class="top-buffer"></div>  
  5.         <div class="panel panel-danger">  
  6.             <div class="panel-heading">Access Denied</div>  
  7.             <div class="panel-body">  
  8.                 <section>  
  9.                     <h1 class="text-danger">401 ! Access Denied</h1>  
  10.                     <br />  
  11.                     <a href="javascript:void(0)" onClick="backAway()" class="btn btn-success">Back</a>  
  12.                 </section>  
  13.             </div>  
  14.         </div>  
  15.     </div>  
  16.     <div class="col-md-3"></div>  
  17. </div>  
  18.   
  19. @section scripts  
  20. {  
  21.     <script>  
  22.         function backAway() {  
  23.             if (history.length === 1) {  
  24.                 window.location = "http://localhost:50841/"  
  25.             } else {  
  26.                 history.back();  
  27.             }  
  28.         }  
  29.     </script>  
  30. }   

Now, run the Application and login with the valid credentials. Its authentication is successful. This authenticates the user, who doesn’t have ‘User’ roles due to which it’s not authorized to access Index method of HomeController and is being redirected on access denied. The screen given below shows in case of access denied.


Figure 10: Access Denied

Now, create an action method for Logout in AccountController, as per the code snippet given below. 

  1. [HttpPost]  
  2.       [ValidateAntiForgeryToken]  
  3.       public async Task<IActionResult> SignOff()  
  4.       {  
  5.           await signInManager.SignOutAsync();             
  6.           return RedirectToAction("Login");  
  7.       }   

This SignOutAsync clears the user claims, which are stored in cookies. This action method calls, when we click on Logout button, which is placed at the top.