Change Password in ASP.NET Core Identity

How to Change Password in ASP.NET Core Identity

In this article, I will discuss How to Implement Change Password in ASP.NET Core Identity. Please read our previous article discussing How to Store Tokens in ASP.NET Core Identity.

Change Password Flow:

A logged-in user can change his password. So, we need to display the Set Password button to the logged-in user, as shown in the below image.

How to Implement Change Password in ASP.NET Core Identity

Once the user clicks the Set Password link, it will open the following Change Password page. Here, the user needs to provide his current and new passwords, confirm his passwords, and click on the Update button, as shown in the image below.

How to Change Password in ASP.NET Core Identity

Once the user clicks on the Update button, if the provided credentials are correct and valid, then the password should be updated, and the user should see the following confirmation message:

Change Password in ASP.NET Core Identity

Why is an old password required while changing the password in the ASP.NET Core Identity?

Requiring the old password while changing a password in ASP.NET Core Identity (or any other authentication system) is a common security practice. This requirement is essential for several reasons:

  • Verification of Identity: Asking for the old password ensures that the legitimate account owner requests to change the password. It’s a form of re-authentication to verify the user’s identity.
  • Protection Against Unauthorized Changes: If a user leaves their device unattended and logged in, the old password requirement safeguards against unauthorized password changes by someone else.
  • Consistency with Security Best Practices: This practice aligns with general security guidelines and best practices for user account management. It’s a part of ensuring that sensitive changes to an account, like password modifications, are performed securely.
  • Compliance with Regulations: Certain regulatory standards and data protection laws might mandate re-authentication for sensitive operations like changing passwords, especially in systems dealing with sensitive data.
How do you implement the change password in ASP.NET Core Identity?

Changing a user’s password in ASP.NET Core Identity typically involves the following steps:

  • Get the Current User: Retrieve the currently logged-in user. This can usually be done via the UserManager class provided by ASP.NET Core Identity.
  • Verify the Current Password: Verifying the user’s password is standard practice before allowing a password change. This can be done using the UserManager.CheckPasswordAsync method.
  • Change the Password: If the current password is verified, you can then change the password using the UserManager.ChangePasswordAsync method.
  • Handle the Result: After attempting to change the password, you should check the result to see if it was successful and handle any errors.
  • Sign-in the User Again: It’s a good idea to automatically sign the user in again with the new password details after changing the password.
Change Password View Model:

First, create a model for the change password form. This model will include the Current Password, the New Password, and the Confirm Password properties. So, create a class file named ChangePasswordViewModel.cs and copy and paste the following code.

using System.ComponentModel.DataAnnotations;
namespace ASPNETCoreIdentityDemo.Models
{
    public class ChangePasswordViewModel
    {
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Current Password")]
        public string OldPassword { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "New Password")]
        public string NewPassword { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm New Password")]
        [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }
}
ChangePassword Get Action Method

Please add the following ChangePassword HttpGet Action Method within the Account Controller. This action method will display the change password form, which the user can use to change his/her password.

[Authorize]
[HttpGet]
public IActionResult ChangePassword()
{
    return View();
}
ChangePassword View

Please add a view named ChangePassword.cshtml within the Views/Account directory and copy and paste the following code. The model for this view is ChangePasswordModel.

@model ChangePasswordViewModel
@{
    ViewData["Title"] = "ChangePassword";
}

<h2>Change Password</h2>
<hr />
<div class="row">
    <div class="col-md-12">
        <form method="post" asp-action="ChangePassword" asp-controller="Account">
            <div asp-validation-summary="All" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="OldPassword"></label>
                <input asp-for="OldPassword" class="form-control" />
                <span asp-validation-for="OldPassword" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="NewPassword"></label>
                <input asp-for="NewPassword" class="form-control" />
                <span asp-validation-for="NewPassword" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="ConfirmPassword"></label>
                <input asp-for="ConfirmPassword" class="form-control" />
                <span asp-validation-for="ConfirmPassword" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Update</button>
        </form>
    </div>
</div>
ChangePassword Post Action Method

Please add the following ChangePassword HttpPost Action Method within the Account Controller. This action method will change the user’s password.

[Authorize]
[HttpPost]
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        //fetch the User Details
        var user = await userManager.GetUserAsync(User);
        if (user == null)
        {
            //If User does not exists, redirect to the Login Page
            return RedirectToAction("Login", "Account");
        }

        // ChangePasswordAsync Method changes the user password
        var result = await userManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);

        // The new password did not meet the complexity rules or the current password is incorrect.
        // Add these errors to the ModelState and rerender ChangePassword view
        if (!result.Succeeded)
        {
            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
            return View();
        }

        // Upon successfully changing the password refresh sign-in cookie
        await signInManager.RefreshSignInAsync(user);

        //Then redirect the user to the ChangePasswordConfirmation view
        return RedirectToAction("ChangePasswordConfirmation", "Account");
    }

    return View(model);
}
ChangePasswordConfirmation Get Action Method

Optionally, you can create a confirmation view that informs the user their password has been successfully changed. Please add the following ChangePasswordConfirmation HttpGet Action Method within the Account Controller. This action method will execute once the user successfully changes the password.

[Authorize]
[HttpGet]
public IActionResult ChangePasswordConfirmation()
{
    return View();
}
ChangePasswordConfirmation View

Please add a view named ChangePasswordConfirmation.cshtml view within the Views/Account directory and then copy and paste the following code.

@{
    ViewData["Title"] = "Change Password Confirmation";
}

<h4>
    Your password is successfully changed.
</h4>
Adding the Change Password Link in Menu:

We need to display the Change Password menu only to the logged-in user. So, modify our _Layout.cshtml file as follows:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<ApplicationUser> 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-area="" asp-controller="Home" asp-action="Index">Home</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-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>

                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="SecureMethod">Secure</a>
                        </li>

                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="NonSecureMethod">Non Secure</a>
                        </li>

                        @if (SignInManager.IsSignedIn(User) && User.IsInRole("Admin"))
                        {
                            <li class="nav-item dropdown">
                                <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button" 
                                data-bs-toggle="dropdown" aria-expanded="false">
                                    Manage
                                </a>
                                <ul class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
                                    <a class="dropdown-item" asp-controller="Administration"
                                       asp-action="ListUsers">Users</a>
                                    <a class="dropdown-item" asp-controller="Administration"
                                       asp-action="ListRoles">Roles</a>
                                </ul>
                            </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>
                            <li class="nav-item">
                                <a class="nav-link text-dark" asp-controller="Account" asp-action="ChangePassword">
                                    Set Password
                                </a>
                            </li>
                        }
                        else
                        {
                            <li class="nav-item">
                                <a class="nav-link text-dark" asp-controller="account" asp-action="register">
                                    Register
                                </a>
                            </li>
                            <li class="nav-item">
                                <a class="nav-link text-dark" 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-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
    <script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.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>

Now, run the application and test the change password functionality, and it should work as expected.

ChangePasswordAsync Method in ASP.NET Core Identity

The ChangePasswordAsync method in ASP.NET Core Identity is used for managing user passwords in an application. It provides a way for users to change their existing passwords. The following is the signature:

Task<IdentityResult> ChangePasswordAsync(TUser user, string currentPassword, string newPassword)

Here,

  • TUser user: The user entity whose password needs to be changed. This is usually an instance of the user class derived from IdentityUser.
  • string currentPassword: The current password of the user.
  • string newPassword: The new password that the user wants to set.
  • Task<IdentityResult>: The method returns a Task which, when awaited, yields an IdentityResult. The IdentityResult indicates the success or failure of the password change operation and any errors.
Usage Scenario

In a typical usage scenario:

  • A user inputs their current and new passwords in a form.
  • The application retrieves the authenticated user’s information.
  • ChangePasswordAsync is called with the user’s information and the provided passwords.
  • The method validates the current password, ensures the new password meets the policy requirements, and then updates the password if all checks pass.
  • The result (success or failure) is returned, and appropriate feedback is given to the user.
RefreshSignInAsync Method in ASP.NET Core Identity

The RefreshSignInAsync method is used to refresh the sign-in session for a user. This is useful in scenarios where the user’s security profile has changed, and you want to ensure that the change is immediately reflected in the user’s current session without requiring them to log out and log back in. For example, if you change a user’s roles, claims, or password, you might call RefreshSignInAsync to update the user’s cookie with the new security information. The following is the method signature for the RefreshSignInAsync method:

Task RefreshSignInAsync(TUser user);

Here,

  • Task: This is the return type of the method. Task is used because the method operates asynchronously. It represents an asynchronous operation that does not return a value.
  • TUser user: This is the parameter of the method. It is an instance of TUser, representing the user whose sign-in information is to be refreshed.

When you call RefreshSignInAsync, it does the following:

  • Creates a new security stamp for the user (if the user has a security stamp).
  • Re-issues the authentication cookie with updated claims and security stamp. This effectively updates the user’s session with the latest security information.

In the next article, I will discuss How to Add a Password to a Local Account Linked to an External Login in ASP.NET Core Identity. I explain How to Implement Change Password in ASP.NET Core Identity in this article. I hope you enjoy this article, Change Password in ASP.NET Core Identity.

Leave a Reply

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