locked
"Invalid token" when trying to reset a password, even though the token is exactly what was generated RRS feed

  • Question

  • User-1158769213 posted

    I have scaffolded all of the Identity pages and am struggling to get the password reset to work. The code I have is very little changed from the scaffolded code.

    The forgotten password page generates and encodes a token as follows...

          string code = await _userManager.GeneratePasswordResetTokenAsync(user);
          code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
    

    This is then used to build a URL for a link sent out by email. A sample link looks like this...

    https://localhost:44398/Account/ResetPassword?code=Q2ZESjhFR3lOUnB4QWcxRWtIc0FuR2JHUnhNUmlGSUVZUDZPMFRWU3FKTFZLeXRETHdaNVo4NE10NGdyc2J0c1JLZDZ3N3BEcHQrbEFydk4valVCTm13WHJlWWpjMkJvbEdHWmRocmh3R3crTG5ncFdiN3FRMUNIQXNtYW5KeWd5NjhEZ3BOWlZhM3o5Tlg0NitWdGhiTVNjM1lMNFI2OERma0NzcE1hYXRES2duUWtUOWthc1E2ajkwQmwxYUpUUGFuM1VzUlVkRGlXalV1WmhLMEFaZ2pLbFY5Qms4ZWV6SFhaTUorWTZ5MUYvWFlr

    Note that I'm not using the standard URLs, but that shouldn't make any difference.

    The reset password page gets the token in the OnGet method and stores it in the model...

          Input = new InputModel {
            Code = code
          };

    Note that I am not using Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)) as in the scaffolded code, as that corrupted the token (see below).

    The OnPostAsync method picks up the token from the model and attempts to reset the password...

        IdentityResult result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password);

    As I said, with the exception of that one change, this is all exactly as in the scaffolded code. Furthermore, I put breakpoints in the code, and could see that at every stage, right up to and including the line where I call ResetPasswordAsync, I had exactly the same token. That's why I changed the line that decoded it, as I ended up with something different.

    However, the call to ResetPasswordAsync always fails with "Invalid token"

    I have spent a lot of time searching around, and it seems that the main causes of this problem are...

    1) Using mismatched methods to generate and use the token, eg using GeneratePasswordResetTokenAsync to get the token but ConfirmEmailAsync when using it (or vice versa). As you can see from my code, I'm using GeneratePasswordResetTokenAsync to generate it and ResetPasswordAsync when I use it, so this shouldn't be a problem.

    2) Encoding and decoding. As I mentioned above, I can see when debugging that the token I have is exactly the same at all stages. I checked it when it was generated, and in both OnGet and OnPostAsync in the reset page. In all three cases, the token was identical.

    3) Not having a security stamp in the AspNetUsers table. I have that, and all users have a non-null security stamp. Also, all users were created using the user manager's CreateAsync method.

    Anyone any idea why it fails when I try to reset the password?

    Thanks

    Tuesday, December 22, 2020 10:49 PM

All replies

  • User-1330468790 posted

    Hi  Yossu,

     

    The only problem is that you changed the actual token code as you mentioned.

    The reset password page gets the token in the OnGet method and stores it in the model...

          Input = new InputModel {
            Code = code
          };

    Note that I am not using Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)) as in the scaffolded code, as that corrupted the token (see below).

     

    Please bear in mind that the token code has been encoded and decoded in 'ForgotPassword' page and 'ResetPassword' page respectively. 

    You have to decode the token as how you encode it.

    Encode:

    code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));

    Decode:

    Code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code))

      

    Note that I am not using Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code)) as in the scaffolded code, as that corrupted the token (see below).

    I don't see the corruption in your description and it should not be the reason why you quit to decode the token.

    If you have mentioned it, could you please specify it?

      

    Regarding you guesses about the causes:

    1) Using mismatched methods to generate and use the token, eg using GeneratePasswordResetTokenAsync to get the token but ConfirmEmailAsync when using it (or vice versa). As you can see from my code, I'm using GeneratePasswordResetTokenAsync to generate it and ResetPasswordAsync when I use it, so this shouldn't be a problem.

    Correct. The methods you use to generate 'ResetToken' and consume it are correct. It won't be the reason.

    2) Encoding and decoding. As I mentioned above, I can see when debugging that the token I have is exactly the same at all stages. I checked it when it was generated, and in both OnGet and OnPostAsync in the reset page. In all three cases, the token was identical.

    This is the reason. You should check the 'original' generated token (A) and Based64-encoded token (B) and compare them with the token (C) you get in the 'OnGet' page and another one (D) which you could get from decoding.  Result should be => A = D, B = C.

    3) Not having a security stamp in the AspNetUsers table. I have that, and all users have a non-null security stamp. Also, all users were created using the user manager's CreateAsync method.

    Currently you have nothing to do with the security stamp since you don't change any other codes in the scaffolded identity.

     

    Hope this helps.

    Best regards,

    Sean

    Wednesday, December 23, 2020 4:03 AM
  • User-1158769213 posted

    Hmm, very confused. When I tried it yesterday with the code you showed (ie the original scaffolded code), I got an exception when calling...

    Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code))

    When I looked at the value of the token that was coming in to the OnPostAsync method, I could see that it was exactly the same as the token that had been generated when requesting the link. That's why I took that line out and used the token value that was coming in.

    However, I just reinstated that line of code, and now it works fine. I really don't know why, this is exactly what I had yesterday.

    Oh well, as long as it works. Thanks for the reply.

    Wednesday, December 23, 2020 2:25 PM
  • User-1330468790 posted

    Hi Yossu,

     

    Very glad to see that currently you codes are working. 

    Regarding your confusion, the problem might be caused by the occasional compilation error.

    For instance, you were trying to change some codes for learning purpose and failed. Then you recovered the codes but forget to rebuild and the problem occured.

    This is just a guess. No need to worry about that.

     

    If you find any anwsers help in your threads, you could mark the answer which will help others who face the similar problem.

    Thank you very much for understanding.

     

    Best regards,

    Sean

    Friday, December 25, 2020 4:16 AM