Login and Logout in ASP.NET Core Identity

Login and Logout in ASP.NET Core Identity

In this article, I will discuss how to Implement the Login and Logout Functionalities in ASP.NET Core Identity. Please read our previous article discussing the User Registration Functionality using ASP.NET Core Identity. In this article, I will also show you how to show or hide the LOGIN, LOGOUT, and REGISTER links based on whether the user is logged in or not.

Login and Logout in ASP.NET Core Identity

In ASP.NET Core Identity, login and logout functionalities are important for managing user sessions. Login involves authenticating a user based on their credentials (like username and password), while logout terminates the user’s session and clears any authentication cookies. 

Let us see what we want to achieve. If the user is not logged in, we need to display the Login and Register links, as shown in the image below.

How to Implement the Login and Logout Functionalities in ASP.NET Core Identity

When the user clicks on the Login link, we need to open the following page and allow the user to enter the Email and Password.

How to Implement the Login and Logout Functionalities in ASP.NET Core Identity

Once the user is logged in, we need to hide the Login and Register links and display the Logout link along with the logged-in user email address, as shown in the image below.

How to Implement the Login and Logout Functionalities in ASP.NET Core Identity

Let us proceed and see how we can implement this using ASP.NET Core Identity:

How to Implement the Login Functionality in ASP.NET Core Identity?

To implement the login functionality in an ASP.NET Core application using ASP.NET Core Identity, we need to implement the following.

  • Login View Model to hold the login data, i.e., User Name and Password.
  • Login View to take the User Name and Password from the user
  • A Pair of Login Action Methods in the AccountController to render the login view and handle the post request.
Create a Login View Model

To log in a user, we need their Email or Username and Password and whether they want a persistent cookie or session cookie. So, this View Model will include properties like Email, Password, and RememberMe. So, create a class file named LoginViewModel.cs and then copy and paste the following code:

using System.ComponentModel.DataAnnotations;
namespace ASPNETCoreIdentityDemo.Models
{
    public class LoginViewModel
    {
        [Required]
        [EmailAddress]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }

        [Display(Name = "Remember Me")]
        public bool RememberMe { get; set; }
    }
}
Get Login Action Method in Account Controller:

Add the following HttpGet Login action method to the Account Controller. This method will be invoked whenever the user clicks on the Login button in the Navigation menu.

[HttpGet]
public IActionResult Login()
{
    return View();
}
Login View:

Create a view named Login.cshtml within the Views/Account directory. This view should have a form that collects the necessary login information (like Email, Password, and RememberMe) and posts it to the login post action method in your controller. Once you create the Login.cshtml view, copy and paste the following code into it. LoginViewModel is the model for this view.

@model LoginViewModel

@{
    ViewBag.Title = "User Login";
}

<h1>User Login</h1>

<div class="row">
    <div class="col-md-12">
        <form method="post">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Email"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <div class="checkbox">
                    <label asp-for="RememberMe">
                        <input asp-for="RememberMe" />
                        @Html.DisplayNameFor(m => m.RememberMe)
                    </label>
                </div>
            </div>
            <button type="submit" class="btn btn-primary">Login</button>
        </form>
    </div>
</div>
Post Login Action Method in Account Controller:

We need to use the following PasswordSignInAsync method of the SignInManager class. The PasswordSignInAsync method in ASP.NET Core Identity is used to sign in a user with their username and password. 

Task<SignInResult> PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure)

The following are method parameters and return type:

  • userName: This is the username of the user trying to sign in. It’s typically an email address or a user identifier.
  • password: The password associated with the user account.
  • isPersistent: If set to true, the sign-in cookie will persist across browser sessions. In other words, the user remains signed in even after closing and reopening the browser. If false, the cookie is session-based and will expire when the browser is closed.
  • lockoutOnFailure: Determines if the user account should be locked out after a number of failed sign-in attempts. If set to true, the account will be locked out (for a duration set in the Identity configuration) after the specified number of failed attempts. This helps to protect against brute-force attacks. At this point, we will not use this property and will set the value to false. In our upcoming article, when we discuss the user Lockout functionality, we will use this property.

Return Type: The method returns a Task<SignInResult>. The SignInResult can have different properties indicating the result of the sign-in attempt, such as Succeeded, IsLockedOut, IsNotAllowed, or RequiresTwoFactor, which allows you to handle different scenarios in your code.

Implementing Post Login Action Method

Next, add the following HttpPost Login action method to the AccountController. As you can see, we use the SignInManager class PasswordSignInAsync method to sign in to the user. Here, we have only handled the Succeeded and Failure status. As we progress, we will see how to handle the RequiresTwoFactor and IsLockedOut status.

[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(LoginViewModel model)
{
    if (ModelState.IsValid)
    {
        var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);

        if (result.Succeeded)
        {
            // Handle successful login
            return RedirectToAction(nameof(HomeController.Index), "Home");
        }
        if (result.RequiresTwoFactor)
        {
            // Handle two-factor authentication case
        }
        if (result.IsLockedOut)
        {
            // Handle lockout scenario
        }
        else
        {
            // Handle failure
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return View(model);
        }
    }

    // If we got this far, something failed, redisplay form
    return View(model);
}

In this example, model.Email and model.Password are provided by the user and model.RememberMe determines if the sign-in should be persistent. The method then handles different sign-in outcomes like success, two-factor authentication requirement, account lockout, or failure. If the sign-in is successful, then it is redirected to the Home Controller Index action method.

Create Logout Action in Account Controller

To log out the user, we need to use the following SignOutAsync() method of the SignInManager class. The SignOutAsync() method in ASP.NET Core Identity is used to sign out the currently logged-in user by clearing the user’s cookies or tokens that are used for authentication. 

Task SignOutAsync()

Implementing Post Logout Action Method

So, add the following Post Logout Action Method to your AccountController. This action method will log out the user and redirect them to the home controller’s index action method.

[HttpPost]
public async Task<IActionResult> Logout()
{
    await signInManager.SignOutAsync();
    return RedirectToAction("index", "home");
}
Link to Register, Login, and Logout:

We need to provide the links in navigation menus so that the user can trigger the Register, Login, and Logout operations. So, open the _Layout.cshtml view and copy and paste the following code. Here, we are Injecting the SignInManager service, and using the SignInManager IsSignedIn method, we are checking if the User (ClaimsPrincipal object) is signed in or not, and based on this, we are displaying the respective links.

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - ASPNETCoreIdentityDemo</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/ASPNETCoreIdentityDemo.styles.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container-fluid">
                <a class="navbar-brand" asp-controller="Home" asp-action="Index">ASPNETCoreIdentityDemo</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                    <ul class="navbar-nav ml-auto">
                        @*If the user is signed-in display Logout link*@
                        @if (SignInManager.IsSignedIn(User))
                        {
                            <li class="nav-item">
                                <form method="post" asp-controller="account" asp-action="logout">
                                    <button type="submit" style="width:auto"
                                            class="nav-link btn btn-link py-0">
                                        Logout @User?.Identity?.Name
                                    </button>
                                </form>
                            </li>
                        }
                        else
                        {
                            <li class="nav-item">
                                <a class="nav-link" asp-controller="account" asp-action="register">
                                    Register
                                </a>
                            </li>
                            <li class="nav-item">
                                <a class="nav-link" asp-controller="account" asp-action="login">
                                    Login
                                </a>
                            </li>
                        }
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2023 - ASPNETCoreIdentityDemo - <a 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>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

With the above changes in place, run the application and test the Register, Login, and Logout functionalities. It should work as expected. Below, I provide brief information about the Session and Persistent cookies in ASP.NET Core Identity.

Session Cookie vs Persistent Cookie in ASP.NET Core Identity

In ASP.NET Core Identity, cookies are used to maintain authentication state and user information across HTTP requests. A cookie is issued upon successful login by the server, which is then sent to the client (browser). Then, from the next subsequent requests, the browser will send that cookie to the server. The server uses this cookie to know whether the user is already authenticated and logged in. Two main types of cookies are used in this context: Session Cookies and Persistent Cookies. Understanding their differences is important for managing authentication and user sessions effectively.

Session Cookies in ASP.NET Core:
  • Lifetime: Session Cookies are temporary and are deleted when the user closes the browser. They do not have a specific expiry time set.
  • Purpose: They are primarily used to store user information while the user is actively browsing and interacting with the Web Application.
  • Use Case: This is ideal for scenarios where the user’s authenticated state should not persist beyond the current browser session, such as on public or shared computers that multiple users can access.
Persistent Cookies in ASP.NET Core Identity
  • Lifetime: Persistent Cookies have a specified expiry time and are stored on the user’s device even after the browser is closed.
  • Purpose: They are used to remember users on subsequent visits, preventing them from logging in every time they access the application.
  • Use Case: Suitable for personal devices where users expect to remain logged in across sessions.

In the next article, I will discuss Custom Password Policy in ASP.NET Core Identity. In this article, I try to explain how to Implement Login and Logout in ASP.NET Core Identity. I hope you enjoy this article.

2 thoughts on “Login and Logout in ASP.NET Core Identity”

  1. This looks excellent. I need to implement login security in an app I’m developing. But I don’t understand where the user name (email address) and password for registered users are stored? There is no mention of a database in your example. I need to link the user with other features in the database. If passwords are not stored in the database that is great. More secure. But I don’t get it.

Leave a Reply

Your email address will not be published. Required fields are marked *