7

I am working in an ASP.NET MVC 5 application. Users are able to register and login without any issues. However, when one user forgets his/her password, the forgot password process (already in place) doesn't do anything! No emails are sent to the user with a click here to reset password link.

Currently my ForgotPassword action method looks like this:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindByNameAsync(model.Email);
        if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
        {
            // Don't reveal that the user does not exist or is not confirmed
            return View("ForgotPasswordConfirmation");
        }
    }

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

I am guessing that is left to the developers to implement. I Googled around and found nothing that was straight forward.

What is the easiest way to allow this?

1 Answer 1

8

Forget password action to generate reset token:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
    if (ModelState.IsValid)
    {
        var user = await UserManager.FindByNameAsync(model.Email);
        if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
        {
            // Don't reveal that the user does not exist or is not confirmed
            return View("ForgotPasswordConfirmation");
        }

        // For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
        // Send an email with this link
        string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
        var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);      
        await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
        return RedirectToAction("ForgotPasswordConfirmation", "Account");
    }

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

Reset password action to reset password based on generated token:

[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
    return code == null ? View("Error") : View();
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }
    var user = await UserManager.FindByNameAsync(model.Email);
    if (user == null)
    {
        // Don't reveal that the user does not exist
        return RedirectToAction("ResetPasswordConfirmation", "Account");
    }
    var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
    if (result.Succeeded)
    {
        return RedirectToAction("ResetPasswordConfirmation", "Account");
    }
    AddErrors(result);
    return View();
}

Relevent view models:

public class ResetPasswordViewModel
{
    public string Email { get; set; }
    public string Password { get; set; }
    public string ConfirmPassword { get; set; }
    public string Code { get; set; }
}

public class ForgotPasswordViewModel
{
    public string Email { get; set; }
}

But you need to configure Email service before sending emails.

public class EmailService : IIdentityMessageService
{
    public Task SendAsync(IdentityMessage message)
    {
        return configSendGridasync(message);
    }

    private Task configSendGridasync(IdentityMessage message)
    {
        var myMessage = new SendGridMessage();
        myMessage.AddTo(message.Destination);
        myMessage.From = new System.Net.Mail.MailAddress(
                      "[email protected]", "My name");
        myMessage.Subject = message.Subject;
        myMessage.Text = message.Body;
        myMessage.Html = message.Body;

        var credentials = new NetworkCredential("userName","Password");

        // Create a Web transport for sending email.
        var transportWeb = new Web(credentials);

       // Send the email.
       if (transportWeb != null)
       {
           return transportWeb.DeliverAsync(myMessage);
       }
       else
       {
           return Task.FromResult(0);
       }
   }
}

At the end you need to register this class Identity in your user manager configurator add following lines:

public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
    var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));

    // some code here

    manager.EmailService = new EmailService();
}

See Account Confirmation and Password Recovery with ASP.NET Identity (C#) as a step by step tutorial.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @SamFarajpourGhamari, I think something is up with await UserManager.SendEmailAsync() because I am still not receiving any emails!
You need to configure Email Service as well. See my updated post.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.