In module 30, Security & Administration, we built the Users management tools including the create and edit pages. Recall we don’t allow the administrators or staff to change the password as this goes against best practices and would create a security hole. Typically, if a user forgets their password there is a, “Forgot Password?”, link on the login screen the user can click to reset their password. In this module we are going to develop the Reset Password feature for the application.
Create an Smtp Server using a Google account
Fortunately, ASP.Net Identity Core provides most of the the tools we will need to create the Reset Password feature. However, Identity Core does require an email service to send password reset links, email confirmation messages, and other account-related notifications. One simple way to add email support is to configure a dedicated Gmail account as your application’s SMTP server.
In this section, we will walk through how to create a Gmail account, enable SMTP access via App Passwords, and configure your ASP.NET Core application to use it.
Create a Dedicated Gmail Account
It’s best to use a Gmail account created specifically for your application, rather than your personal email. For example:
yourapp.smtp@gmail.com
Our application is named FredsCars so this would translate to:
FredsCars.smtp@gmail.com
This email is likely taken however, so you may need to modify it to make it unique.
If you are unfamiliar with google or have never created a google account open a browser, navigate to https://www.google.com, and click the Sign in button in the upper right hand corner.

NOTE: We are using the terms Google account and Gmail account interchangeably here. But we are really creating a Google account. A Google account has many services such as maps, calendar, and chat. Mail, called gmail in Google, is just another one of these services.
If you have no accounts created for Google your screen will look like the following and you will click “Create account” .

If you have an existing or multiple existing Google accounts your screen will look like the following and you will click, “Use another account”.

From there you will click “Create account”.

From the “Create account” drop down options, select “For my personal use”.
DO NOT SELECT “FOR WORK OR MY BUSINESS”. This option may have a fee and is also much less trivial to set up then for personal use.

You’ll then be taken to a screen to enter your first and last name for the new Google account. Enter your first and last names and click the Next button.

On the Basic Information screen enter your birthday and optionally your gender then click the Next button.

On the Create an email address screen, select the “Create your own Gmail address” selection. Enter an email such as [yourapp].smtp@gmail where you replace yourapp with your application name such as FredsCars. Note you don’t actually type in “@gmail”. It will already be in the text field and uneditable. So just type yourapp.smtp before it.

Next, on the “Create a strong password” screen, create a password and enter it into the Password and Confirm text fields. Make sure the two passwords match. Keep in mind this is the password used to sign into the Google account and not the one we will use to configure the Smtp service in our ASP.Net Core MVC application. We will create an app password for that in a later section.

Next, you’ll have to verify some information before Google creates your new account. Scan the QR code with your phone’s camera, tap the link in your camera app, and follow the directions on your phone to complete.

Turn on 2-Step Verification
In order to create an app password, we must first enable 2-Step verification.
Your browser should have automatically signed you in to the main google screen in the last step when you verified your information. If not sign in using the “Sign In” button in the upper right hand corner using the password you just created for the new account.

Once signed in, click your avatar in the upper right hand corner and then click, “Manage your Google Account”.

From the manage your Google Account page, click “Security & sign-in”.

On the “Security & sign-in” page, click “2-Step Verification under, “How you sign in to Google”. Mine shows I have already activated 2-Step verification.

On the “2-Step Verification” page, click “Add a phone number”.

On the “2-Step Verification phones” page, click “Add a backup 2-Step Verification phone”.

On the “Add a phone number” screen, enter a phone number for a phone you can receive text messages at and click next.

Now click the back arrow in the upper left hand corner until you are back on the “Security & sign-in page.

Now, 2-Step verification should show as on.

Create an App Password
Once 2-Step Verification has been activated an “App passwords” option should appear on the “Security & sign-in” page. If it does click it. However, there is a documented bug for google where sometimes this button will not show up. If you do not see this option, navigate to https://myaccount.google.com/apppasswords.
On the “App passwords” page, enter the name of your application and click the Create button.

The “Generated app password” modal window pops up with your generated password for the app name you just entered. Copy the password to paste into the appsettings.json configuration file in a later section then click the Done button.

Back on the “App passwords” page, your newly created app name should appear which is linked to the password you provided.

Create the ViewModel classes
Now that we have our Smtp server up and ready to send emails for the application, let’s get back to coding.
In this section we will create the ViewModel classes for the controllers and views of the Reset Password feature.
Create a class named ForgotPasswordViewModel in the Models\ViewModels folder of the FredsCars project and fill it with the code shown below.
FredsCars\Models\ViewModels\ForgotPasswordViewModel.cs
using System.ComponentModel.DataAnnotations;
namespace FredsCars.Models.ViewModels
{
public class ForgotPasswordViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; } = string.Empty;
}
}
The view model above has one required Property named Email and is decorated with the EmailAddress data annotation attribute. The EmailAddress data annotation is a validator which validates an email address.
Create a second class named ResetPasswordViewModel in the Models\ViewModels folder of the FredsCars project and fill it with the code below.
FredsCars\Models\ViewModels\ResetPasswordViewModel.cs
using System.ComponentModel.DataAnnotations;
namespace FredsCars.Models.ViewModels
{
public class ResetPasswordViewModel
{
[Required]
public string UserId { get; set; } = string.Empty;
[Required]
public string Token { get; set; } = string.Empty;
[Required]
[DataType(DataType.Password)]
public string Password { get; set; } = string.Empty;
[Required]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "The password and confirm password do not match.")]
public string ConfirmPassword { get; set; } = string.Empty;
}
}
The view model code shown above has four required properties; UserId, Token, Password, and ConfirmPassword.
The Password and ConfirmPassword properties have the DataType data annotation applied specifying with the DataType enumeration set to Password as the attribute’s parameter that both properties are to be rendered with an input control of type password in a form. When the user types instead of displaying the text a user types, Asterixis or bullets are shown as the characters being typed.

Create a SmtpSettings class
We are going to create an EmailSender service in a later section. But first let’s create a SmtpSettings class the EmailSender service can utilize to get the configuration information it needs to communicate with the SmtpServer.
The IOptions Pattern
Up until now everything we have registered into our DI container has been a service and we registered them using the AddScoped method of the IServiceCollection in Program.cs
builder.Services.AddScoped<IVehicleRepository, EFVehicleRepository>();
builder.Services.AddScoped<IVehicleTypeRepository, EFVehicleTypeRepository>();
builder.Services.AddScoped<IRolesRepository, EFRolesRepository>();
Here, we will register the SmtpSettngs class in our DI container as a configuration rather then a service using the IServiceCollection Configure method.
This is the precursor to using a pattern known as the IOptions pattern which we will see in action throughout the process of creating the EmailSender service.
Create a class named SmtpSettings in the Models folder of the FredsCars project.
FredsCars\Models\SmtpSettings.cs
namespace FredsCars.Models
{
public class SmtpSettings
{
public string Host { get; set; } = "";
public int Port { get; set; }
public bool EnableSsl {get; set;}
public string Username { get; set; } = "";
public string Password { get; set; } = "";
public string FromAddress { get; set; } = "";
public string FromName { get; set; } = "";
}
}
Add the SmtpSettings configuration
In this section we will add an SmtpSettings section to our secrets.json (for development) and appsettings.production.json (for production) configuration files.
Modify the secrets.json file with the following changes shown below replacing yourAppName and YourAppPassword with the values you used to set up your Google Smtp Server. Remember you can get to the secrets.json file by right clicking on the Project name in Visual Studio and selecting “Manage User Secrets”.
C:\Users\[YourUserName]\AppData\Roaming\Microsoft\UserSecrets\FredsCarsMvc\secrets.json
{
"ConnectionStrings": {
"FredsCarsMvcConnection": "Server=(localdb)\\MSSQLLocalDB;Database=FredsCarsMvc;Trusted_Connection=True;MultipleActiveResultSets=true",
"FCMvcIdentityConnection": "Server=(localdb)\\MSSQLLocalDB;Database=FCMvcIdentity;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"SmtpSettings": {
"Host": "smtp.gmail.com",
"Port": 587,
"EnableSsl": true,
"UserName": "yourAppName.smtp@gmail.com",
"Password": "yourAppPassword",
"FromAddress": "yourAppName.smtp@gmail.com",
"FromName": "FredsCars Support"
}
}
Now, add the same SmtpSettngs section to the appsettings.production.json file in the root of the FredsCars project also replacing yourAppName and YourAppPassword with the values you used in your Google Smtp Server setup. Remember in Visual Studio appsettings.production.json is nested under appsettings.json. You have to click the arrow on the left of application.json to get to it.

FredsCars\appsettings.production.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Serilog": {
"MinimumLevel": {
"Override": {
"Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware": "Error"
}
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
// Production
// "FredsCarsMvcConnection": "[Place Holder]"
// Need production identity connection string
},
"SmtpSettings": {
"Host": "smtp.gmail.com",
"Port": 587,
"EnableSsl": true,
"UserName": "yourAppName.smtp@gmail.com",
"Password": "yourAppPassword",
"FromAddress": "yourAppName.smtp@gmail.com",
"FromName": "FredsCars Support"
}
}
Register the SmtpSettings confirguration in the DI Container
Next, we need to map the values from our configuration files’ SmtpSettings section to the properties of the SmtpSettngs class.
Modify Program.cs with the following changes shown below.
FredsCars\Program.cs
... existing code ...
builder.Services.AddScoped<IVehicleRepository, EFVehicleRepository>();
builder.Services.AddScoped<IVehicleTypeRepository, EFVehicleTypeRepository>();
builder.Services.AddScoped<IRolesRepository, EFRolesRepository>();
builder.Services.Configure<SmtpSettings>(builder.Configuration.GetSection("SmtpSettings"));
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();
var app = builder.Build();
... existing code ...
In the code above we have registered a configuration for the SmtpSettings class in the DI container using the Configure method of the IServiceCollection. This binds the values from the appsettings.[environment].json and secrets.json files to the properties of the SmtpSettings class whenever we inject an instance of the Microsoft generic IOptions class of type SmtpSettings, or IOptions<SmtpSettings> as shown in the following example.
public class MyClass : IMyClass
{
private readonly SmtpSettings _settings;
// Constructor for MyClass
public MyClass(IOptions<SmtpSettings> settings)
{
_settings = settings.Value;
}
In the sample code above we have a class called MyClass where we have used our usual DI pattern to inject an instance of the SmtpSettings configuration:IOptions<SmtpSettings> and stored it in a private class field named _settings.
In the next section we will use this configuration and the IOptions pattern to build the EmailSender service. Below is a screenshot where I have put a breakpoint in Visual Studio debug mode at the end of the EmailSender’s constructor (where the service’s implementation class is named SmtpEmailSender).

In the screenshot above, you can see the SmtpSettings values from the application’s configuration files have been bound to the IOptions<SmtpSettings> instance’s Value property.
Now that we have the SmtpSettings configuration registered in DI and ready to go, and we now know all of the steps to using the IOptions pattern, we are ready to move on and create the actual EmailSender service.
Create the EmailSender Service
Create an interface named IEmailSender in the Models\Repositories folder of the FredsCars project.
FredsCars\Models\Repositories\IEmailSender.cs
namespace FredsCars.Models.Repositories
{
public interface IEmailSender
{
Task SendEmailAsync(string email, string subject, string htmlMessage);
}
}
In the code above, we have defined an interface for an EmailSender service named IEmailSender with one asynchronous method named SendEmailAsync which returns a Task. Remember a Task is like void in a normal synchronous method. The SendEmailAsync method takes in three string parameters:
email: the person the EmailSender service should send the email to.subject: the subject of the email.htmlMessage: the body of the email message.
Next, create a class named SmtpEmailSender in the Models\Repositories folder of the FredsCars project.
FredsCars\Models\Repositories\SmtpEmailSender.cs
using Microsoft.Extensions.Options;
using System.Net;
using System.Net.Mail;
namespace FredsCars.Models.Repositories
{
public class SmtpEmailSender : IEmailSender
{
private readonly SmtpSettings _settings;
public SmtpEmailSender(IOptions<SmtpSettings> settings)
{
_settings = settings.Value;
}
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
{
using var client = new SmtpClient(_settings.Host, _settings.Port)
{
EnableSsl = _settings.EnableSsl,
Credentials = new NetworkCredential(_settings.Username,
_settings.Password)
};
var mail = new MailMessage
{
From = new MailAddress(_settings.FromAddress, _settings.FromName),
Subject = subject,
Body = htmlMessage,
IsBodyHtml = true
};
mail.To.Add(email);
await client.SendMailAsync(mail);
}
}
}
In the code above we define a class named SmtpEmailSender which implements the IEmailSender interface.
public class SmtpEmailSender : IEmailSender
The first thing we do in the class template is perform DI to inject an IOptions object of type SmtpSettings or IOptions<SmtpSettings> to utilize the IOptions pattern we setup and configured in the, “Create a SmtpSettings class” above.
private readonly SmtpSettings _settings;
public SmtpEmailSender(IOptions<SmtpSettings> settings)
{
_settings = settings.Value;
}
Next, we implement the SendEmailAsync method of the IEmailSender interface.
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
{
// method implimentation
}
The first section within the method body creates a SmtpClient object instance in a using block. A SmtpClient object sends email by using the specified SMTP server and port which we get from our IOptions<SmtpSettings> object via the IOptions pattern and pass as parameters to the SmtpClient class’ constructor. We also set the SmtpClient object’s EnableSsl to true and set the Username and Password Credentials via the IOptions pattern and the SmtpSettngs configuration values.
using var client = new SmtpClient(_settings.Host, _settings.Port)
{
EnableSsl = _settings.EnableSsl,
Credentials = new NetworkCredential(_settings.Username,
_settings.Password)
};
Next, we create a MailMessage object. We set the From property using the FromAddress and FromName configuration values again via the IOptions pattern. We set the Subject and Body properties to the matching values of the incoming method parameters and IsBodyHtml to true.
var mail = new MailMessage
{
From = new MailAddress(_settings.FromAddress, _settings.FromName),
Subject = subject,
Body = htmlMessage,
IsBodyHtml = true
};
Then, we add the incoming method parameter named email to the MailMessage object’s To collection which is an address collection that contains the recipients of the mail message using the MailMessage.To.Add method.
mail.To.Add(email);
And finally, we use the SmtpClient object we created earlier at the top of the method to send the MailMessage using the asynchronous Smtp.SendMailAsync method.
await client.SendMailAsync(mail);
Register the EmailSender Service
Now, we just need to register the EmailSender service in Program.cs
Modify the Program.cs file with the code changes shown below.
FredsCars\Program.cs
... existing code ...
builder.Services.AddScoped<IVehicleRepository, EFVehicleRepository>();
builder.Services.AddScoped<IVehicleTypeRepository, EFVehicleTypeRepository>();
builder.Services.AddScoped<IRolesRepository, EFRolesRepository>();
builder.Services.Configure<SmtpSettings>(builder.Configuration.GetSection("SmtpSettings"));
builder.Services.AddTransient<IEmailSender, SmtpEmailSender>();
var app = builder.Build();
... existing code ...
In the above code we used the IServiceCollection.AddTransient method to register the IEmailSender service with the implementation being the SmtpEmailSender.
For the other services, IVehicleTypeRepository and IRolesRepository, we used the AddScoped method to register the service.
Recall that AddScoped uses the same object instance to implement the service for every component be it controller, view, view component, and so on in the HttpRequest. So the same service object instance is used in every layer of the request. A new instance however is created for each new request.
AddTransient on the other hand creates a new service object instance every single time it is needed. Every component in every request that needs the IEmailSender service will create its own instance of the SmtpEmailSender object to implement the service.
In addition to AddScoped and AddTransient, AddSingleton registers a Singleton object meaning that the same object instance is used every single time it is needed throughout the application.
Modify the Account Controller
Modify the AccountController in the Controllers folder of the FredsCars project with the changes shown below.
FredsCars\Controllers\AccountController.cs
using FredsCars.Models.Identity;
using FredsCars.Models.Identity.ViewModels;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
// Added in Module 32
using FredsCars.Models.Repositories;
using FredsCars.Models.ViewModels;
namespace FredsCars.Controllers
{
public class AccountController : Controller
{
private readonly SignInManager<ApplicationUser> _signInManager;
// For Reset Password Feature
private readonly UserManager<ApplicationUser> _userManager;
private readonly IEmailSender _emailSender;
private readonly ILogger<AccountController> _logger;
public AccountController(SignInManager<ApplicationUser> signInManager,
// For Reset Password Feature
UserManager<ApplicationUser> userManager,
IEmailSender emailSender,
ILogger<AccountController> logger)
{
_signInManager = signInManager;
// For Reset Password Feature
_userManager = userManager;
_emailSender = emailSender;
_logger = logger;
}
... existing code ...
public IActionResult ForgotPassword()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
// Security: don't reveal if the email does not exist
// or isn't confirmed
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
return RedirectToAction(nameof(ForgotPasswordConfirmation));
}
// Generate reset token
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
// Build callback URL
var callbackUrl = Url.Action(
nameof(ResetPassword),
"Account",
new { userId = user.Id, token },
protocol: Request.Scheme);
// Send email
await _emailSender.SendEmailAsync(
model.Email,
"Reset Password",
$"Please reset your password by clicking here: <a href='{callbackUrl}'>link</a>");
return RedirectToAction(nameof(ForgotPasswordConfirmation));
}
public IActionResult ForgotPasswordConfirmation()
{
return View();
}
[HttpGet]
public IActionResult ResetPassword(string userId, string token)
{
if (string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(token))
{
// invalid link
return BadRequest("UserId or token was not supplied for password reset.");
}
var model = new ResetPasswordViewModel
{
UserId = userId,
Token = token
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByIdAsync(model.UserId);
if (user == null)
{
// Don't reveal that user no longer exists
return RedirectToAction(nameof(ResetPasswordConfirmation));
}
var result = await _userManager.ResetPasswordAsync(user, model.Token, model.Password);
if (result.Succeeded)
{
return RedirectToAction(nameof(ResetPasswordConfirmation));
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return View(model);
}
[HttpGet]
public IActionResult ResetPasswordConfirmation()
{
return View();
}
}
}
In the controller code changes shown above, we start by adding in three services we will need for the reset password feature to our dependency injection routine; UserManager<ApplicationUser>, IEmailSender, and ILogger<AccountController>.
Next, we added five action methods at the end of the controller class. These new methods are divided into two sections:
- 1)
Forgot password section:deals with letting a user request a reset password link.Action methods- HttpGet ForgotPassword
- HttpPost ForgotPassword
- ForgotPasswordConfirmation
- 2)
Rest Password section:deals with letting the user actually reset their password.- Action methods
- HttpGet ResetPassword
- HttpPost ResetPassword
- ResetPasswordConfirmation
- Action methods
Let’s review the code for each action method.
HttpGet ForgotPassword
The HttpGet version of the ForgotPassword action method simply returns a form for the user to enter the email of their account they are requesting a reset password link for.
public IActionResult ForgotPassword()
{
return View();
}

HttpPost ForgotPassword
The HttpPost version of ForgotPassword generates a Reset Password Token and sends a reset password link to the user’s email account. The link will contain the token for the user to submit in a form post along with the new password.
Let’s take a closer look at the code shall we?
The method receives as a parameter a view model of type ForgotPasswordViewModel which we created earlier in this module and contains one required property named email of type string.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
// generate token and send email
}
After checking that the model state is valid, we fetch the user whose password needs to be reset via the FindByEmailAsync method of the UserManager service.
var user = await _userManager.FindByEmailAsync(model.Email);
Next, we check to make sure that the query to fetch the user did not come back null and that the user has confirmed their email.
// Security: don't reveal if the email does not exist
// or isn't confirmed
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
return RedirectToAction(nameof(ForgotPasswordConfirmation));
}
If either condition is true we redirect to the ForgotPasswordConfirmation action which will show the Forgot Password Confirmation page with the following message:

We never let the user know if the email they enter does not exist in the system or is not verified and confirmed. We just show them the above message:
“If an account exists for the email…”
Next, we generate a password reset token for the specified user via the UserManager.GeneratePasswordResetTokenAsync method.
// Generate reset token
var token = await _userManager.GeneratePasswordResetTokenAsync(user);
Then, we build the URL that will be in the email sent to the user within the reset password link via the Urlhelper.Action method and store it in a variable named callbackUrl.
// Build callback URL
var callbackUrl = Url.Action(
nameof(ResetPassword),
"Account",
new { userId = user.Id, token },
protocol: Request.Scheme);
Here we build a URL that will point to the ResetPassword action of the Account controller with two route values that will be in the query string of the URL; the userId (a guid in string format) and the reset password token we just generated.
We then send an email to the user with the reset password link with the callbackUrl we created in the previous step embedded in the body via the IEmailSender service’s SendEmailAsync method.
// Send email
await _emailSender.SendEmailAsync(
model.Email,
"Reset Password",
$"Please reset your password by clicking here: <a href='{callbackUrl}'>link</a>");
Now that a reset password link has been emailed to the user, we can legitimately redirect to the ForgotPasswordConfirmation action and webpage since the user does exist and their email is marked in the system as confirmed.
return RedirectToAction(nameof(ForgotPasswordConfirmation));
[Default]HttpGet ForgotPasswordConfirmation
The ForgotPasswordConfirmation action as we have already seen displays a message that the reset password email has been sent.
public IActionResult ForgotPasswordConfirmation()
{
return View();
}

HttpGet ResetPassword
Once the HttpPost ForgotPassword action method emails the link with the callbackUrl to the user, they can click the link from within their email client.
You can see in the below screenshot that the link’s URL points to Account/ResetPassword (the ResetPassword action in the Account controller) and has the two querystring parameters that were added as route values via the UrlHelper.Action method in the HttpPost ForgotPassword action; userId and token.

Once the user clicks the link a browser opens up and is redirected to https://localhost:40443/Account/ResetPassword?userid=[guid]&token=[ResetToken] where they can type in the new password.

The HttpGet ResetPassword action then receives two string parameters from the URL; userId (a guid in string format) and token (the reset password token).
[HttpGet]
public IActionResult ResetPassword(string userId, string token)
{
...
}
We then create an instance of the ResetPassword view model and set its UserId and Token properties to the values of the method’s incoming matching parameters from the URL and return the ResetPassword form in a ViewResult passing the view model object as the model to the view.
var model = new ResetPasswordViewModel
{
UserId = userId,
Token = token
};
return View(model);
HttpPost ResetPassword
The HttpPost ResetPassword action method receives an instance of the ResetPassword view model class as a parameter which contains the UserId, Token (the reset password token), and Password (the new password to reset to) properties as well as ConfirmPassword.
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
{
...
}
After checking that the model state is valid we fetch the user who is resetting their password via the UserManager.FindByIdAsync method passing to it the UserId in the view model sent up by the view.
var user = await _userManager.FindByIdAsync(model.UserId);
Next, we check that the query did indeed return a user and that the returned user is not null. If the user is null we redirect the browser to the ResetPasswordConfirmation action. Again we never reveal that the user does not exist.
if (user == null)
{
// Don't reveal that user no longer exists
return RedirectToAction(nameof(ResetPasswordConfirmation));
}
If the user does exist then we reset the user’s password via the UserManager.ResetPasswordAsync method passing to it the user we just fetched and the reset password token and password from the incoming view model parameter of the method. The ResetPasswordAsync method returns an IdentityResult object which we assign to a variable named result.
var result = await _userManager.ResetPasswordAsync(user, model.Token, model.Password);
Then, we check that the reset was successful by checking that the IdentityResult.Succeeded property is true. And if so, redirect the browser to the ResetPasswordConfirmation action which will render a view with the message:
“Your password has been reset successfully. You can now log in with your new password.“
if (result.Succeeded)
{
return RedirectToAction(nameof(ResetPasswordConfirmation));
}
If resetting the password is not successful we add all of the errors contained in the IdentityResult object as Model errors to the ModelState in a foreach block and re-render the view for the user to correct the errors.
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
return View(model);
HttpGet ResetPasswordConfirmation
The ResetPasswordConfirmation action simply returns a view with a message that the password has been reset.
Add the Razor Views
Now its time to add the razor views to support the Account controller with the reset password feature action methods.
Create the ForgotPassword view
Create Razor file named ForgotPassword.cshtml in the Views\Account folder of the FredsCars project.
FredsCars\Views\Account\ForgotPassword.cshtml
@model ForgotPasswordViewModel
@{
ViewData["Title"] = "Forgot Password";
}
<div class="container-fluid mt-3">
<h3 class="text-center bg-warning-subtle py-2"
style="border: 1px solid black;">
Forgot Password
</h3>
</div>
<form asp-action="ForgotPassword" method="post">
<div class="container">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="mb-3 col-4">
<label asp-for="Email" class="form-label fw-bold"></label>
<input asp-for="Email" class="form-control bg-warning-subtle" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Send reset link</button>
</div>
</form>
The razor code above starts off with our usual boiler plate razor code to show the blue header of the webpage where in this case the header says, “Forgot Password”. Take note however that we have changed the background color of the header throughout the Reset Password section from a light blue, the Bootstrap bg-primary-subtle class, to a light yellow, the Bootstrap bg-warning-subtle class. Also take note of that the model of the class is the ForgotPassword view model, ForgotPasswordViewModel.
@model ForgotPasswordViewModel
@{
ViewData["Title"] = "Forgot Password";
}
<div class="container-fluid mt-3">
<h3 class="text-center bg-warning-subtle py-2"
style="border: 1px solid black;">
Forgot Password
</h3>
</div>

Next, we have a simple form that is wired to post to the HttpPost ForgotPassword method of the Account controller.
<form asp-action="ForgotPassword" method="post">
...
</form>
The body of the form has a validation summary tag helper, one form group that binds to the Email property of the views model, ForgotPasswordViewModel, and a submit button.
<form asp-action="ForgotPassword" method="post">
<div class="container">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="mb-3 col-4">
<label asp-for="Email" class="form-label fw-bold"></label>
<input asp-for="Email" class="form-control bg-warning-subtle" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-primary">Send reset link</button>
</div>
</form>
This renders a view where a user can type in the email associated with their ASP.Net Core Identity account in the FredsCars FCMvcIdentity database.

