I\'m using Asp.Net-Identity-2 and I\'m trying to verify email verification code using the below method. But I am getting an \"Invalid Token\"
Maybe this is an old thread but, just for the case, I've been scratching my head with the random occurrence of this error. I've been checking all threads about and verifying each suggestion but -randomly seemed- some of the codes where returned as "invalid token". After some queries to the user database I've finally found that those "invalid token" errors where directly related with spaces or other non alphanumerical characters in user names. Solution was easy to find then. Just configure the UserManager to allow those characters in user's names. This can be done just after the user manager create event, adding a new UserValidator setting to false the corresponding property this way:
public static UserManager<User> Create(IdentityFactoryOptions<UserManager<User>> options, IOwinContext context)
{
var userManager = new UserManager<User>(new UserStore());
// this is the key
userManager.UserValidator = new UserValidator<User>(userManager) { AllowOnlyAlphanumericUserNames = false };
// other settings here
userManager.UserLockoutEnabledByDefault = true;
userManager.MaxFailedAccessAttemptsBeforeLockout = 5;
userManager.DefaultAccountLockoutTimeSpan = TimeSpan.FromDays(1);
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
userManager.UserTokenProvider = new DataProtectorTokenProvider<User>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromDays(5)
};
}
return userManager;
}
Hope this could help "late arrivals" like me!
Make sure that the token that you generate doesn't expire rapidly - I had changed it to 10 seconds for testing and it would always return the error.
if (dataProtectionProvider != null) {
manager.UserTokenProvider =
new DataProtectorTokenProvider<AppUser>
(dataProtectionProvider.Create("ConfirmationToken")) {
TokenLifespan = TimeSpan.FromHours(3)
//TokenLifespan = TimeSpan.FromSeconds(10);
};
}
Because you are generating token for password reset here:
string code = UserManager.GeneratePasswordResetToken(user.Id);
But actually trying to validate token for email:
result = await UserManager.ConfirmEmailAsync(id, code);
These are 2 different tokens.
In your question you say that you are trying to verify email, but your code is for password reset. Which one are you doing?
If you need email confirmation, then generate token via
var emailConfirmationCode = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
and confirm it via
var confirmResult = await UserManager.ConfirmEmailAsync(userId, code);
If you need password reset, generate token like this:
var code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
and confirm it like this:
var resetResult = await userManager.ResetPasswordAsync(user.Id, code, newPassword);
In my case, our AngularJS app converted all plus signs (+) to empty spaces (" ") so the token was indeed invalid when it was passed back.
To resolve the issue, in our ResetPassword method in the AccountController, I simply added a replace prior to updating the password:
code = code.Replace(" ", "+");
IdentityResult result = await AppUserManager.ResetPasswordAsync(user.Id, code, newPassword);
I hope this helps anyone else working with Identity in a Web API and AngularJS.
Other than that, I've seen the code itself fail if it's not encoded.
I've recently started encoding mine in the following fashion:
string code = manager.GeneratePasswordResetToken(user.Id);
code = HttpUtility.UrlEncode(code);
And then when I'm ready to read it back:
string code = IdentityHelper.GetCodeFromRequest(Request);
code = HttpUtility.UrlDecode(code);
To be quite honest, I'm surprised that it isn't being properly encoded in the first place.
We have run into this situation with a set of users where it was all working fine. We have isolated it down to Symantec's email protection system which replaces links in our emails to users with safe links that go to their site for validation and then redirects the user to the original link we sent.
The problem is that they are introducing a decode... they appear to do a URL Encode on the generated link to embed our link as a query parameter to their site but then when the user clicks and clicksafe.symantec.com decodes the url it decodes the first part they needed to encode but also the content of our query string and then the URL that the browser gets redirected to has been decoded and we are back in the state where the special characters mess up the query string handling in the code behind.