Custom Login And Register With Identity In ASP.NET Core 3.1

Introduction

 
Hii guys! This article explains how to create a custom login and register with the identity manager in asp.net core 3.1. We will create a register page with all the validations and check if the user email already exists or not and then we will validate the password constraints and display a proper message for each error on the page.
 
After this, we will create a login page and use .net core IdentityManager classes for this process. At last, we will see how to log out identity users and authorize the dashboard. If an unauthorized user accesses the pages, then the web auto redirects it to access a denied page or a login page.
 
Before startgin the login process, I recommended you set up migration already in your project and install core identity packages from the Nuget Package Manager.
 
Step 1
 
Create UserLoginDto.cs class for login and set validation for email, password. 
  1. public class UserLoginDto  
  2.    {  
  3.        [Required(ErrorMessage = "Email is required")]  
  4.        [EmailAddress(ErrorMessage = "Invalid email address")]  
  5.        public string Email { getset; }  
  6.   
  7.        [Required(ErrorMessage = "Password is required")]  
  8.        [DataType(DataType.Password)]  
  9.        public string Password { getset; }  
  10.   
  11.        [Display(Name = "Remember me")]  
  12.        public bool RememberMe { getset; }  
  13.    }  
Step 2
 
Create UserRegistrationDto.cs class for registering and setting validations for all the properties. 
  1. public class UserRegistrationDto  
  2.    {  
  3.        [Required(ErrorMessage = "Name is required")]  
  4.        [StringLength(100)]  
  5.        public string Name { getset; }   
  6.         
  7.        [EmailAddress(ErrorMessage = "Invalid email address")]  
  8.        [Required(ErrorMessage = "Email is required")]  
  9.        public string Email { getset; }  
  10.        [Required(ErrorMessage = "Password is required")]  
  11.        public string Password { getset; }  
  12.   
  13.        [Required(ErrorMessage = "Confirm password is required")]  
  14.        [Compare("Password", ErrorMessage = "The Password and Confirm Password do not match.")]  
  15.        public string ConfirmPassword { getset; }  
  16.        
  17.        public string PhoneNumber { getset; }  
  18.    }  
Step 3
 
AccountController.cs and inject the UserManager<IdentityUser> and SignInManager<IdentityUser> identity classes into its constructor.
  1.  public class AccountController : Controller  
  2.     {  
  3.          
  4.         private readonly UserManager<IdentityUser> userManager;  
  5.         private readonly SignInManager<IdentityUser> signInManager;  
  6.         public AccountController(UserManager<IdentityUser> userManager,  
  7.             SignInManager<IdentityUser> signInManager)  
  8.         {  
  9.             
  10.             this.userManager = userManager;  
  11.             this.signInManager = signInManager;  
  12.         }  
  13. }  
Step 4 - Create a Register method like this
 
In this register method, we first register the user by email via the FindByEmailAsync method. If the user is null, then add a user using the CreateAsync method otherwise if any error occurs, then display all the errors to the view accordingly. 
  1. [HttpGet, AllowAnonymous]  
  2.       public IActionResult Register()  
  3.       {  
  4.           UserRegistrationDto model = new UserRegistrationDto();  
  5.           return View(model);  
  6.       }  
  1. [HttpPost, AllowAnonymous]  
  2.        public async Task<IActionResult> Register(UserRegistrationDto request)  
  3.        {  
  4.            if (ModelState.IsValid)  
  5.            {  
  6.                var userCheck = await userManager.FindByEmailAsync(request.Email);  
  7.                if (userCheck == null)  
  8.                {  
  9.                    var user = new IdentityUser  
  10.                    {  
  11.                        UserName = request.Email,  
  12.                        NormalizedUserName = request.Email,  
  13.                        Email = request.Email,  
  14.                        PhoneNumber = request.PhoneNumber,  
  15.                        EmailConfirmed = true,  
  16.                        PhoneNumberConfirmed = true,  
  17.                    };  
  18.                    var result = await userManager.CreateAsync(user, request.Password);  
  19.                    if (result.Succeeded)  
  20.                    {  
  21.                        return RedirectToAction("Login");  
  22.                    }  
  23.                    else  
  24.                    {  
  25.                        if (result.Errors.Count() > 0)  
  26.                        {  
  27.                            foreach (var error in result.Errors)  
  28.                            {  
  29.                                ModelState.AddModelError("message", error.Description);  
  30.                            }  
  31.                        }  
  32.                        return View(request);  
  33.                    }  
  34.                }  
  35.                else  
  36.                {  
  37.                    ModelState.AddModelError("message""Email already exists.");  
  38.                    return View(request);  
  39.                }  
  40.            }  
  41.            return View(request);  
  42.   
  43.        }  
Step 5
 
Now create login methods like this,
 
In this login method, we first register the user by email via the FindByEmailAsync method. If the user is null and the email is not confirmed then add "Email not confirmed yet" error message otherwise if the user password is not matched then display the "Invalid credentials" error message and then sign in the user using the PasswordSignInAsync method. If the user is logged in successfully, add it to the identity claim with UserRole key otherwise display an error message accordingly.
  1. [HttpGet]  
  2.  [AllowAnonymous]  
  3.  public IActionResult Login()  
  4.  {  
  5.      UserLoginDto model = new UserLoginDto();  
  6.      return View(model);  
  7.  }  
  1. [HttpPost]  
  2.      [AllowAnonymous]  
  3.      public async Task<IActionResult> Login(UserLoginDto model)  
  4.      {  
  5.          if (ModelState.IsValid)  
  6.          {  
  7.              var user = await userManager.FindByEmailAsync(model.Email);  
  8.              if (user != null && !user.EmailConfirmed)  
  9.              {  
  10.                  ModelState.AddModelError("message""Email not confirmed yet");  
  11.                  return View(model);  
  12.   
  13.              }  
  14.              if (await userManager.CheckPasswordAsync(user, model.Password) == false)  
  15.              {  
  16.                  ModelState.AddModelError("message""Invalid credentials");  
  17.                  return View(model);  
  18.   
  19.              }  
  20.   
  21.              var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, true);  
  22.   
  23.              if (result.Succeeded)  
  24.              {  
  25.                  await userManager.AddClaimAsync(user, new Claim("UserRole""Admin"));  
  26.                  return RedirectToAction("Dashboard");  
  27.              }  
  28.              else if (result.IsLockedOut)  
  29.              {  
  30.                  return View("AccountLocked");  
  31.              }  
  32.              else  
  33.              {  
  34.                  ModelState.AddModelError("message""Invalid login attempt");  
  35.                  return View(model);  
  36.              }  
  37.          }  
  38.          return View(model);  
  39.      }  
Step 6
 
Create Register.cshtml view,
  1. @model UserRegistrationDto  
  2. @{  
  3.     ViewData["Title"] = "Register";  
  4.     Layout = "~/Views/Shared/_Layout.cshtml";  
  5. }  
  6. <div class="container">  
  7.     <h1>Register</h1>  
  8.     @Html.ValidationSummary()  
  9.     <form asp-action="Register" asp-controller="Account" method="post">  
  10.   
  11.         <div class="registerdiv">  
  12.             <div class="form-group col-sm-12">  
  13.                 <label asp-for="@Model.Name" class="col-lg-3 control-label"></label>  
  14.                 <br>  
  15.                 <div class="col-lg-9">  
  16.                     @Html.TextBoxFor(m => m.Name, new { placeholder = "First Name", @class = "form-control" })  
  17.                     @Html.ValidationMessageFor(m => m.Name)  
  18.                 </div>  
  19.             </div>  
  20.   
  21.   
  22.             <div class="form-group col-sm-12">  
  23.                 <label asp-for="@Model.Email" class="col-lg-3 control-label"></label>  
  24.                 <br>  
  25.                 <div class="col-lg-9">  
  26.                     @Html.TextBoxFor(m => m.Email, new { placeholder = "Email", @class = "form-control" })  
  27.                     @Html.ValidationMessageFor(m => m.Email)  
  28.                 </div>  
  29.             </div>  
  30.   
  31.             <div class="form-group col-sm-12">  
  32.                 <label asp-for="@Model.PhoneNumber" class="col-lg-3 control-label"></label>  
  33.                 <br>  
  34.                 <div class="col-lg-9">  
  35.                     @Html.TextBoxFor(m => m.PhoneNumber, new { placeholder = "Phone Number", @class = "form-control" })  
  36.                     @Html.ValidationMessageFor(m => m.PhoneNumber)  
  37.                 </div>  
  38.             </div>  
  39.   
  40.   
  41.             <div class="form-group col-sm-12">  
  42.                 <label asp-for="@Model.Password" class="col-lg-3 control-label"></label>  
  43.                 <br>  
  44.                 <div class="col-lg-9">  
  45.                     @Html.PasswordFor(m => m.Password, new { placeholder = "Password", @class = "form-control" })  
  46.                     @Html.ValidationMessageFor(m => m.Password)  
  47.                 </div>  
  48.             </div>  
  49.   
  50.             <div class="form-group col-sm-12">  
  51.                 <label asp-for="@Model.ConfirmPassword" class="col-lg-3 control-label"></label>  
  52.                 <br>  
  53.                 <div class="col-lg-9">  
  54.                     @Html.PasswordFor(m => m.ConfirmPassword, new { placeholder = "Confirm Password", @class = "form-control" })  
  55.                     @Html.ValidationMessageFor(m => m.ConfirmPassword)  
  56.                 </div>  
  57.             </div>  
  58.   
  59.             <div class="form-group">  
  60.                 <div class="col-sm-12 btn-submit">  
  61.                     <button type="submit" class="btn btn-success">Sign Up</button>  
  62.   
  63.                 </div>  
  64.             </div>  
  65.         </div>  
  66.     </form>  
  67. </div>  
Step 7
 
Create Login.cshmt view,
  1. @model UserLoginDto  
  2. @{  
  3.     ViewData["Title"] = "Login";  
  4.     Layout = "~/Views/Shared/_Layout.cshtml";  
  5. }  
  6.   
  7.   
  8. <div class="container">  
  9.     <h1>Login</h1>  
  10.     @Html.ValidationSummary()  
  11.     <form asp-action="Login" asp-controller="Account" method="post">  
  12.   
  13.         <div class="logindiv">  
  14.               
  15.             <div class="form-group col-sm-12">  
  16.                 <label asp-for="@Model.Email" class="col-lg-3 control-label"></label>  
  17.                 <br>  
  18.                 <div class="col-lg-9">  
  19.                     @Html.TextBoxFor(m => m.Email, new { placeholder = "Email", @class = "form-control" })  
  20.                     @Html.ValidationMessageFor(m => m.Email)  
  21.                 </div>  
  22.             </div>  
  23.   
  24.             <div class="form-group col-sm-12">  
  25.                 <label asp-for="@Model.Password" class="col-lg-3 control-label"></label>  
  26.                 <br>  
  27.                 <div class="col-lg-9">  
  28.                     @Html.PasswordFor(m => m.Password, new { placeholder = "Password", @class = "form-control" })  
  29.                     @Html.ValidationMessageFor(m => m.Password)  
  30.                 </div>  
  31.             </div>  
  32.   
  33.             <div class="form-group">  
  34.                 <div class="col-sm-12 btn-submit">  
  35.                     <button type="submit" class="btn btn-success">Login</button>  
  36.                     <a asp-action="Register" asp-controller="account">Don't have an account?Click to Register</a>  
  37.   
  38.                 </div>  
  39.             </div>  
  40.         </div>  
  41.     </form>  
  42. </div>  
Step 8
 
Create _LoginPartial.cshmtl partial view, which displayed the logged-in user name and a logout button will appear after logging into the website. 
  1. @using Microsoft.AspNetCore.Identity  
  2. @inject SignInManager<IdentityUser> SignInManager  
  3. @inject UserManager<IdentityUser> UserManager  
  4.   
  5. <ul class="navbar-nav">  
  6.     @if (SignInManager.IsSignedIn(User))  
  7.     {  
  8.         <li class="nav-item">  
  9.             <a class="nav-link text-dark" title="Manage">Hello @User.Identity.Name!</a>  
  10.         </li>  
  11.         <li class="nav-item">  
  12.             <a class="nav-link btn btn-link text-dark" asp-controller="account" asp-action="Logout">Logout</a>  
  13.         </li>  
  14.     }  
  15.     else  
  16.     {  
  17.         <li class="nav-item">  
  18.             <a class="nav-link text-dark" asp-controller="account" asp-action="Register">Register</a>  
  19.         </li>  
  20.         <li class="nav-item">  
  21.             <a class="nav-link text-dark" asp-controller="account" asp-action="login">Login</a>  
  22.         </li>  
  23.     }  
  24. </ul>  
Step 9
 
Create Dashbord action and add an Authorize attribute to it so that without login nobody can access the dashboard view.
  1. [Authorize]  
  2.   public IActionResult Dashboard()  
  3.   {  
  4.       return View();  
  5.   }  
Step 10
 
Create Logout action  for logging out the identity user 
  1. public async Task<IActionResult> Logout()  
  2.       {  
  3.           await signInManager.SignOutAsync();  
  4.           return RedirectToAction("login""account");  
  5.       }  
Step 11
 
Set custom redirect URL paths from the ConfigureServices in Startup.cs class.
  1. public void ConfigureServices(IServiceCollection services)  
  2.        {  
  3.            services.AddDbContext<ApplicationDbContext>(options =>  
  4.                options.UseSqlServer(  
  5.                    Configuration.GetConnectionString("DefaultConnection")));  
  6.            services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)  
  7.                .AddEntityFrameworkStores<ApplicationDbContext>();  
  8.            services.AddControllersWithViews();  
  9.            services.AddRazorPages();  
  10.   
  11.            services.ConfigureApplicationCookie(options =>  
  12.            {  
  13.                options.LoginPath = $"/account/login";  
  14.                options.LogoutPath = $"/account/logout";  
  15.                options.AccessDeniedPath = $"/account/accessDenied";  
  16.            });  
  17.        }  
 
OutPut
 
 
 
 
If I want to go to the dashboard from the URL then it auto-redirects to the login page.
 
 

Summary

 
In this article, I have discussed how we can create a custom login page using User Manager of Identity User type class and register using SignInManager of IdentityUser type class.
 
And after logging into the website, display the identity Email to view and logout the user from the web using the same identity.
 
At last, we saw how to custom redirect to the login page on unauthorized access or the invalid request.