asp.netasp.net-web-apiasp.net-identityreset-password

ASP.Net Identity reset password with website and web api


We have 2 separate ways for people to perform a forgot password: 1. Via the website - mywebsite.com/account/forgotpassword 2. Via our App - Clicking the "Forgot Password" passes the users email address to our asp.net web api which in turn creates the code and sends the email.

Clicking the link in the email brings up the correct page where we can enter the email and new password. When resetting the password using option 1, it works fine. Using option 2 gives an invalid token error. Both the website and web api are on the same server.

Here's the Web api code:

    var user = await UserManager.FindByEmailAsync(email);            
    var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);

    var callbackUrl = $"/Account/ResetPassword?userId={user.Id}&code={code}";            
    var path = System.Web.Hosting.HostingEnvironment.MapPath("~/Content/emails/ResetPassword.html");

    var body = GenerateEmailBody(user);
    await UserManager.SendEmailAsync(user.Id, "Reset Password", body);

Here's the website code

    var user = await UserManager.FindByEmailAsync(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");
    }

    // 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);
    var body = GenerateEmailBody(user);
    await UserManager.SendEmailAsync(user.Id, "Reset Password", body);

I verified that the UserTokenProvider is the same in both as well.

 manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("IdentityStuff"));

I read that MachineKey can cause this issue except we're only using a single server.

An additional piece of info that is worth mentioning is when the website was created, it didn't have identity added to it. This was an after thought. The web api started off with identity.


Solution

  • For clarity purposes I thought I would answer my question.

    Instead of have the reset logic in both the website and api, I have both the website and our app call the api to reset the password.

    I did, however run into an additional problem when using a load balancer. Since one server may create the code for the reset and another may respond to the link, we run into the same issue.

    To fix this problem, I have a DNS entry that always points to a single server which is used to create the code as well as actually reset the password. This eliminates the issue of getting an invalid token.

    HTH.