ASP.NET Core Identity Series Eleven

In this section, we mainly introduce the password reset function. When a user forgets his password, he needs to choose to reset the password. It is very convenient to use ASP.NET Core Identity to reset the password.

1. ASP.NET Core Identity reset password

Next, let’s take a look at how to reset the password in Identity. When a user forgets the password, we will bring him to the forgotten password page, as shown in the following figure:

7c5607b25c45858e0c2c3a3c6dd0699c.png

This page will send a password reset URL to the mailbox specified by the user. When the user clicks on this URL, it will carry the corresponding token and Email link to the reset password page. The following figure shows the reset password page:

a2a29eb3172b90f07f20a4d496225f29.png

2. Enable the Token generation function in Identity

Add the AddDefaultTokenProviders extension method to the application to enable token generation. The following code:

builder.Services.AddIdentity<AppUser, IdentityRole>().
AddEntityFrameworkStores<AppIdentityDbContext>().
AddDefaultTokenProviders();

When resetting the password, Identity will generate a token every time an email is sent. When the user clicks the link address in the email, Identity will verify the Token and ensure that the Token is available and has not expired before the user resets the password. This is a good idea. feature, we set the valid time of Token by adding the following code:

//dot net 6
builder.Services.Configure<DataProtectionTokenProviderOptions>(opts => opts.TokenLifespan = TimeSpan.FromHours(10));

Here we set the effective time of Token to be 10, which means that the user can change the password within 10 hours

3. Create a forgotten password function

Add a new method called ForgotPassword to AccountController. This method is mainly used to send an email to the mailbox specified by the user, and the user can use this link to reset the password. Add another ForgotPasswordConfirmation method to prompt the user that the email has been sent successfully

[AllowAnonymous]
 public IActionResult ForgotPassword()
 {
     return View();
 }
 [HttpPost]
 [Allow Anonymous]
 public async Task<IActionResult> ForgotPassword([Required] string email)
 {
     if (!ModelState. IsValid)
     {
         return View(email);
     }
     var appUser = await _userManager. FindByEmailAsync(email);
     if (appUser == null)
     {
         return RedirectToAction(nameof(ForgotPasswordConfirmation));
     }
     var token = await _userManager.GeneratePasswordResetTokenAsync(appUser);
     var url = Url. Action("ResetPassword", "Account", new { Email = email, Token = token }, Request. Scheme);
     _emailService.Send(appUser!.Email  "[email protected]", "Reset Password", $"{url}");
     return RedirectToAction("ForgotPasswordConfirmation");
 }
 [Allow Anonymous]
 public IActionResult ForgotPasswordConfirmation()
 {
     return View();
 }

The following 4 lines of code are more important:

var appUser = await _userManager.FindByEmailAsync(email);
if (appUser == null)
{
     return RedirectToAction(nameof(ForgotPasswordConfirmation));
}
var token = await _userManager.GeneratePasswordResetTokenAsync(appUser);
var url = Url. Action("ResetPassword", "Account", new { Email = email, Token = token }, Request. Scheme);
_emailService.Send(appUser!.Email  "[email protected]", "Reset Password", $"{url}");

We use the GeneratePasswordResetTok of the UserManager class

The enAsync method creates a token for resetting the password. We add this token to the link address and send the link address to the user. The user uses this link to reset the password.

Add the following two View files in Views->Account, ForgotPassword.cshtml & amp;ForgotPasswordConfirmation.cshtml, the code is as follows:

ForgotPassword.cshtml

@{
    ViewData["Title"] = "Forgot Password";
}
<div class="container">
    <form asp-action="ForgotPassword" method="post">
        <div class="mb-3 row align-items-center">
            <div class="col-sm-1">
                <label class="control-label">Email</label>
            </div>
            <div class="col-sm-11">
                <input name="email" class="form-control" />
            </div>
        </div>
        <div class="mb-3 row align-items-center">
            <div class="col-sm-11 offset-sm-1">
                <button class="btn btn-primary" type="submit">Send email</button>
            </div>
        </div>
    </form>
</div>

ForgotPasswordConfirmation.cshtml

@{
    ViewData["Title"] = "Forgot password confirmation";
}


<h1>Forgot password confirmation</h1>
<p>
    The email has been sent, please check the email and reset the password
</p>

4. Test the function of forgetting the password

Run the application to enter the forgotten password URL and enter the email address, click to send email. The following figure:

6bc669b007c5413d1d5e452018842c0d.png

After sending the email, it will jump to the following page:

efee98958f4dc8017c8db98a79cbc34b.png

Let’s open the mailbox to check the email just sent:

49e0b11d0b4d3d46e33d25354d06cbbc.png

5. Change password

When opening this link, this link will redirect the user to the ResetPassword method of AccountController, so we need to add the ResetPassword method and ResetPasswordConfirmation method (the page that jumps after changing the password), the code is as follows:

[AllowAnonymous]
public IActionResult ResetPassword(string token, string email)
{
    var resetPassword = new ResetPassword() { Token = token, Email = email };
    return View(resetPassword);
}


[HttpPost]
[Allow Anonymous]
public async Task<IActionResult> ResetPassword(ResetPassword resetPassword)
{
    if (!ModelState. IsValid)
        return View(resetPassword);
    var appUser = await _userManager.FindByEmailAsync(resetPassword.Email);
    if (appUser == null)
        RedirectToAction("ResetPasswordConfirmation");


    var resetPassResult = await _userManager.ResetPasswordAsync(appUser, resetPassword.Token, resetPassword.Password);


    if (!resetPassResult. Succeeded)
    {
        foreach (var error in resetPassResult.Errors)
            ModelState.AddModelError(error.Code, error.Description);
        return View();
    }
    return RedirectToAction("ResetPasswordConfirmation");
}

When the user opens the address in the email, the ResetPassword method of the Get version will be called. The main function of this method is to get the token and Email and bind the data corresponding to these two parameters to the View. Next the user enters the new password and confirms the password in the view (i.e. ResetPassword.cshtml) and clicks the button to save. This will call the Post version of the ResetPassword method, which uses model binding to get the new password and user email and token. code show as below:

073d6abf2e3aca5eb7c726fd75f6ce88.png

Next, we need to create a ResetPassword.cs class in the Models file

public class ResetPassword
{
    [Required]
    [DisplayName("Password")]
    public string Password { get; set; } = null!;


    [Compare("Password", ErrorMessage = "Password and confirmation password do not match")]
    [DisplayName("Confirm password")]
    public string ConfirmPassword { get; set; } = null!;


    public string Email { get; set; } = null!;
    public string Token { get; set; } = null!;
}

5e4913edcec4f5ebb4a27e4c5207914e.png

We create 2 views ResetPassword & amp; ResetPasswordConfirmation in the inner Views->Account folder using the following code:

ResetPassword.cshtml

@model ResetPassword
@{
    ViewData["Title"] = "Reset Password";
}
<div class="text-danger" asp-validation-summary="All"></div>


<form asp-action="ResetPassword" method="post">
    <div class="mb-3 row align-items-center">
        <div class="col-sm-1">
            <label asp-for="Password"></label>
        </div>
        <div class="col-sm-11">
            <input asp-for="Password" class="form-control" />
        </div>
    </div>
    <div class="mb-3 row align-items-center">
        <div class="col-sm-1">
            <label asp-for="ConfirmPassword"></label>
        </div>
        <div class="col-sm-11">
            <input asp-for="ConfirmPassword" class="form-control" />
        </div>
    </div>
    <input type="hidden" asp-for="Email" class="form-control" />
    <input type="hidden" asp-for="Token" class="form-control" />
    <div class="mb-3 row align-items-center">
        <div class="col-sm-11 offset-sm-1">
            <button class="btn btn-primary" type="submit">Submit</button>
        </div>
    </div>
</form>

ResetPasswordConfirmation.cshtml

@{
    ViewData["Title"] = "Reset password confirmation";
}


<h1>Reset password confirmation</h1>


<p>
    Password has been reset. Please <a asp-action="Login">login</a>
</p>

The ResetPassword view is for users to modify passwords

The ResetPasswordConfirmation view prompts that the user password has been changed successfully

6. Test and modify the password feature

First we enter the forgotten password page, (url –https://localhost:7296/Ac

count/ForgotPassword) Enter the email. Click Send Email, and an email will be sent to our designated mailbox, and the url received in the mailbox will be input into the browser. As shown below:

0ac0202ad302cf21afa16cdb28ba439b.png

Note that Token and Email are added to the url query string. These two values are passed to View through the ResetPassword Get version method. Now we can modify our password. After modification, we will jump to the following page:

55226607d72ce31cd072707d3a9aed80.png

Summary

In this section, we mainly implemented the function of forgetting the password, registering the token and setting the declaration period of the token, and realizing the function of resetting the password

Source code address:

https://github.com/bingbing-gui/Asp.Net-Core-Skill/tree/master/AspNetCore.Identity/Identity