Registration And Login Functionality In ASP.NET Core 3.0 MVC Web Application Using Identity

Introduction

In this article, I will discuss how to create registration and login functionalities in ASP.NET Core web applications using Identity. ASP.NET Core Identity is an API that supports login functionality in ASP.NET Core MVC web applications. Login information can be stored in identity when creating a new user account. Identity can be configured with an SQL Server database to store user details such as username, password, and email id. So in this article, I will explain how to use identity to register, log, and logout of a user account.

Creating ASP.NET Core MVC web application

Create ASP.NET Core MVC web application as follows.

MVC web

Select ASP.NET Core Web Application from the template and provide the project name and location where this project can be saved.

ASP.NET

Select the MVC template along with the ASP.NET Core version and click on the create button for the application to be created.

Now we have to add ASP.NET Core Identity in our application. To add identity right click on the project name, click on the Add option, and the click on New Scaffolded Item.

 Core Identity

Select the Identity option and click on the Add button.

Now we can select login and register razor pages from ASP.NET Core Identity.

Razor pages

Select the layout page for the razor pages, provide the data context class for database-related operations and User class to customize the given registration razor page. Now click on the Add button.

 Layout page

As we can see identity is added inside the Areas folder. Inside the Area folder, both data as well as razor pages have been added.

Now we have to add user authentication into this application, for that we have to modify startup.cs class. We have to add support for razor pages inside the application.

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

Inside the ConfigureServices method, we have to call the function AddRazorPages.

To include identity in this application we have to call the UseAuthentication function inside the configure method.

app.UseAuthentication();

Now we need to add URLs for login and register pages, for that we have to call a function called MapRazorPages inside UseEndpoints.

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
    endpoints.MapRazorPages();
});

Now we need to create a database for the application.

using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations.Schema;
namespace LoginAuth.Areas.Identity.Data
{
    // Add profile data for application users by adding properties to the ApplicationUser class
    public class ApplicationUser : IdentityUser
    {
        [PersonalData]
        [Column(TypeName="nvarchar(100)")]
        public string FirstName { get; set; }
        [PersonalData]
        [Column(TypeName = "nvarchar(100)")]
        public string LastName { get; set; }
    }
}

Here we are adding properties for first name and last name. Inside the user table, it will be personal data, that is why we need to add attribute personal data. We can also define the data types of each property with the help of an attribute column.

Now we have to perform a migration operation to insert these newly created properties inside the SQL Server database.

For that go to tools, then click on Nuget Package Manager. Click on Package Manager Console.

To create Migration, we can execute the Add-Migration command.

 Add-Migration

To update the database, execute the command Update-Database.

We can see the newly created database LoginAuthDB in SQL Server below

 SQL Server

In order to display form controls for first name and last name inside the registration form, we have to update the input model inside register. cshtml.cs file by adding corresponding properties inside the input model.

public class InputModel
{
    [Required]
    [DataType(DataType.Text)]
    [Display(Name = "First Name")]
    public string FirstName { get; set; }
    [Required]
    [DataType(DataType.Text)]
    [Display(Name = "Last Name")]
    public string LastName { get; set; }
    [Required]
    [EmailAddress]
    [Display(Name = "Email")]
    public string Email { get; set; }
    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }
    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

We also need to add the same inside the OnPostAsync function for saving the record.

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

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);
            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }
    // If we got this far, something failed, redisplay form
    return Page();
}

Here we are passing both first name and last name.

Now we need to update the registration form inside Register. cshtml.

@page
@model RegisterModel
@{
    ViewData["Title"] = "Register";
}

<h1>@ViewData["Title"]</h1>
<div class="row">
    <div class="col-md-4">
        <form asp-route-returnUrl="@Model.ReturnUrl" method="post">
            <h4>Create a new account.</h4>
            <hr />
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Input.FirstName"></label>
                <input asp-for="Input.FirstName" class="form-control" />
                <span asp-validation-for="Input.FirstName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.LastName"></label>
                <input asp-for="Input.LastName" class="form-control" />
                <span asp-validation-for="Input.LastName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Email"></label>
                <input asp-for="Input.Email" class="form-control" />
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.Password"></label>
                <input asp-for="Input.Password" class="form-control" />
                <span asp-validation-for="Input.Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Input.ConfirmPassword"></label>
                <input asp-for="Input.ConfirmPassword" class="form-control" />
                <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Register</button>
        </form>
    </div>
</div>
@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

A new user can now register in our application. After filling in the login information and clicking on the login button, once the user is logged in to the application corresponding email id link and logout button can be displayed by including the _LoginPartial.cshtml file inside _Logout.cshtml file as follows

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - LoginAuth</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">LoginAuth</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <partial name="_LoginPartial.cshtml" />
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>
    <footer class="border-top footer text-muted">
        <div class="container">
            © 2020 - LoginAuth - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>

We can also provide an authentication feature to the application so that only authenticated users will be able to log in inside the application. For that, we have added the Authorize attribute inside the Home Controller as follows.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using LoginAuth.Models;
using Microsoft.AspNetCore.Authorization;
namespace LoginAuth.Controllers
{
    [Authorize]
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }
        public IActionResult Index()
        {
            return View();
        }
        public IActionResult Privacy()
        {
            return View();
        }
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

Output

If we run the application, we will be redirected to the login page because the user is not authenticated.

Log in page

Registered user details are stored inside the SQL Server database as follows

Database

Once a user is logged in, the user will be redirected to the home page.

 Home page

Summary

In this article, we have discussed to how to create Registration and login functionalities in ASP.NET Core web applications using Identity. We have added register and login razor pages to register a new user and store user details using identity inside the SQL Server database. We also modified the existing registration page by adding first name and last name properties inside the register. cshtml file. We also provided an authentication feature to the application so that only authenticated users will be able to login inside the application.