ASP.NET Core Identity invalid token on confirmation email

This answer https://stackoverflow.com/a/31297879/2948212 pointed me in the right direction. But as I said it was for a different version and now it is slightly different solution.

The answer is still the same: encode the token in base 64 url, and then decode it in base 64 url. That way both Angular and ASP.NET Core will retrieve the very same code.

I needed to install another dependency to Microsoft.AspNetCore.WebUtilities;

Now the code would be something like this:

public async Task SendPasswordResetEmailAsync(string email)
{
    //_userManager is an instance of UserManager<User>
    var userEntity = await _userManager.FindByNameAsync(email);
    var tokenGenerated = await _userManager.GeneratePasswordResetTokenAsync(userEntity);
    byte[] tokenGeneratedBytes = Encoding.UTF8.GetBytes(tokenGenerated);
    var codeEncoded = WebEncoders.Base64UrlEncode(tokenGeneratedBytes);
    var link = Url.Action("MyAction", "MyController", new { email = email, code = codeEncoded }, protocol: HttpContext.Request.Scheme);
     //this is my service that sends an email to the user containing the generated password reset link
     await _emailService.SendPasswordResetEmailAsync(userEntity , link);
}

and when receiving back the code during the PUT request

[HttpPut]
[AllowAnonymous]
[Route("api/password/{email}")]
public async Task<IActionResult> SendPasswordEmailResetRequestAsync(string email, [FromBody] PasswordReset passwordReset)
{
    //some irrelevant validatoins here
    await _myIdentityWrapperService.ResetPasswordAsync(email, passwordReset.Password, passwordReset.Code);
    return Ok();
}

//in MyIdentityWrapperService
public async Task ResetPasswordAsync(string email, string password, string code)
{
    var userEntity = await _userManager.FindByNameAsync(email);
    var codeDecodedBytes = WebEncoders.Base64UrlDecode(code);
    var codeDecoded = Encoding.UTF8.GetString(codeDecodedBytes);
    await _userManager.ResetPasswordAsync(userEntity, codeDecoded, password);
}

I had a similar issue and I was encoding my token but it kept on failing validation and the problem turned out to be this : options.LowercaseQueryStrings = true; Do not set true on options.LowercaseQueryStrings this alters the validation token's integrity and you will get Invalid Token Error.

// This allows routes to be in lowercase
services.AddRouting(options =>
{
     options.LowercaseUrls = true;
      options.LowercaseQueryStrings = false;
});


After scaffolding the ConfirmEmail page in my Asp.Net Core 3.0 project I ran into the same problem.

Removing the following line from the OnGetAsync method in ConfirmEmail.cshtml.cs fixed the problem:

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

In the scaffolded Login page, the code is added to the callbackUrl which is then URL encoded using HtmlEncoder.Default.Encode(callbackUrl). When the link is clicked the decoding is automatically done and the code is like it should be to confirm the email.

UPDATE:

I noticed that during the Forgot Password process the code is Base64 encoded before being put in the callbackUrl which then means that the Base64 decode IS necessary.

A better solution would thus be to add the following line to wherever the code is generated before adding it to the callbackUrl.

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

Here is a link to the issue which has been fixed.


I have tried the answers above, but this guide has helped me. Basically, you would need to encode the code, otherwise, you would encounter some weird bugs. To summarise you would need to do this:

string code = HttpUtility.UrlEncode(UserManager.GenerateEmailConfirmationToken(userID));

After this, if it is applicable to you, decode code:

string decoded = HttpUtility.UrlDecode(code)