问题
As per the title I have attempted to setup a simple ASP.NET Core 2.2 Web API project (so nothing to do with MVC Razor Pages or an UI).
I have been following this Microsoft guide Account confirmation and password recovery in ASP.NET Core but obviously adapted it for my Web API project which consists of the following four endpoints:
- CreateUserAsync
- ConfirmEmailAsync
- PasswordResetEmailAsync
- ResetPasswordAsync
Names are pretty self-explanatory the first two endpoints deal with creating a user, sending out an email using SendGrid and then the second endpoint deals with validating the confirmation token and confirming the user account.
So far so good all works as intended the token is correctly validated and the AspNetUser table row field EmailConfirmed
is set to true (1). Apologies for got to mention at this point I had already scaffolded up the AspNetUser related tables using Entity Framework Migrations (Add-Migration
command).
Now the third and fourth endpoints deal with the scenario of a user requesting a password reset token to then use to reset their password so endpoint number 3 PasswordResetEmailAsync
reuses the same code as ConfirmEmailAsync
endpoint first validating the user exists with UserManager.FindByNameAsync
method and then passes this type ApplicationUser
object (derived from IdentityUser
) into the FindByNameAsync
method which then hits the backing store and pulls out the user we created earlier (this works as intended):
var result = await _userManager
.ResetPasswordAsync(user, token, theNewPassword)
.ConfigureAwait(false);
This always returns the following (used Object Exporter add-in for this):
{
"Errors":
[
{
"Code": "InvalidToken",
"Description": "Invalid token."
}
],
"Succeeded": false,
"_errors":
}
As you can see from the JSON snippet above the token is deemed invalid however when I compared the generated token to the one being passed into the ResetPasswordAsync endpoint its the same:
CfDJ8NKO7RlW/idPs+/v0VVN8vv99DYu3b4zYHddSIlvUBHjcThOMG85zegMIJ6AUpQJXpC0Ouk0QW8hVVDIRUgT7u0TnUU+kJePskU2UMRaul0NtddoeJ30LsOe62K8lMqm9OyC8+CsU9nZZFKIWRDoJxnGCAJiznVukulFX9BxAhnFN+ocOHZBEwIVw9w/86wJRI1WKegiemcgWmepEZalBzU8bWHQHzKbHd4aIfahfK4OVcYNwPo0K1+5ypE4Jx1Mpg==
The above password is sanitised when generating the reset token email using WebUtility.UrlEncode
method and decoded back in the ResetPasswordAsync
endpoint using WebUtility.UrlDecode
.
I used KDiff3 to confirm both token strings are identical and there is only one user in the AspNetUser table yet the token is always invalid and the reset password process fails.
The StartUp.cs class is nothing special looks like the example provided in the above tutorial I mentioned, key point being that I am adding an Identity provider on startup:
services.AddDefaultIdentity<ApplicationUser>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<AuthContext>();
This is driving me crazy and I have lost confidence in ASP.NET Core 2.2 Identity with regards to working with a Web API project. Why am I doing it this way? The rest of the world doesn't use MVC or Razor Pages, in this day and age everything is headless and the front end (whatever it may be React, Angular etc) consumes endpoints and the other reason is that I am build a bunch of Microservices sitting behind an Azure Application Gateway.
Thanks in advance.
来源:https://stackoverflow.com/questions/56013673/asp-net-core-2-2-identity-usermanager-resetpasswordasync-always-fails-with-inv