Identity password reset token is invalid

2019-02-04 06:47发布

问题:

I'm writting MVC 5 and using Identity 2.0.

Now I m trying to reset password. But i always getting "invalid token" error for reset password token.

    public class AccountController : Controller
{
    public UserManager<ApplicationUser> UserManager { get; private set; }

    public AccountController()
        : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
    {
    }

and i set DataProtectorTokenProvider,

        public AccountController(UserManager<ApplicationUser> userManager)
    {   
        //usermanager config
        userManager.PasswordValidator = new PasswordValidator { RequiredLength = 5 };  
        userManager.EmailService = new IddaaWebSite.Controllers.MemberShip.MemberShipComponents.EmailService(); 

        var provider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider();
        userManager.UserTokenProvider = new Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider<ApplicationUser>(provider.Create("UserToken"))
                                                    as IUserTokenProvider<ApplicationUser, string>;




        UserManager = userManager;

    }

i generate password reset before sending mail

 [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> ManagePassword(ManageUserViewModel model)
    {
        if (Request.Form["email"] != null)
        {
          var email = Request.Form["email"].ToString();
          var user = UserManager.FindByEmail(email);
          var token = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
           //mail send
        }
   }

i click link in mail and i'm getting passwordreset token and using

var result = await UserManager.ResetPasswordAsync(model.UserId, model.PasswordToken, model.NewPassword);

the result always false and it says "Invalid Token". Where should i fix ?

回答1:

UserManager.GeneratePasswordResetTokenAsync() very often returns string that contains '+' characters. If you pass parameters by query string, this is the cause ('+' character is a space in query string in URL).

Try to replace space characters in model.PasswordToken with '+' characters.



回答2:

[HttpPost]
[ValidateAntiForgeryToken]
publicasync Task<ActionResult> ManagePassword(ManageUserViewModel model)
{
    if (Request.Form["email"] != null)
    {
      var email = Request.Form["email"].ToString();
      var user = UserManager.FindByEmail(email);
      var token = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
       //before send mail
      token = HttpUtility.UrlEncode(token);   
  //mail send

    }
}

And on password reset action decode token HttpUtility.UrlDecode(token);



回答3:

I have found that the 'Invalid Token' error also occurs when the SecurityStamp column is NULL for the user in the AspNetUsers table in the database. The SecurityStamp won't be NULL with the out-of-the-box MVC 5 Identity 2.0 code, however a bug had been introduced in our code when doing some customization of the AccountController that cleared out the value in the SecurityStamp field.



回答4:

Many answers here URLEncode the token before sending to get around the fact that the token (being a base 64 encoded string) often contains the '+' character. Solutions must also take into account that the token ends with '=='.

I was struggling with this issue & it turns out many users within a large organisation were using Scanmail Trustwave Link Validator(r) which was not symmetrically encoding and decoding URLEncoded stings in the email link (at the time of writing).

The easiest way was to use Mateusz Cisek's answer and send a non URLEncoded token and simply replace the space characters back to +. In my case this was done in an angular SPA so the Javascript becomes $routeParams.token.replace(/ /g,'+').

The caveat here will be if using AJAX to send the token and rolling your own query string parsing algorithm - many examples split each parameter on '=', which will of course not include the '==' at the end of the token. Easy to work around by using one of the regex solutions or looking for the 1st '=' only.