We just launched our ASP.net MVC (using Identity 2.0) web app 6 days ago and have had over 5000 people register for accounts, great!
The issue is 3-5% of those users who click the confirmation email link (and same seems to go for reset password) get shown the error screen presumably because they have an invalid token.
I read on StackOverflow here that you need to encode the url to avoid special characters throwing it off and then decode it right before you validate it. I did that to no effect though, some users were still getting errors on their validation token.
I also read that having different MachineKey's could be a reason tokens aren't processed as being valid. Everything is hosted on Azure so I presumed (and saw on SO) it was or should taken care of
So with 30-50 people emailing us for the past 6 days now about issues, I got desperate while I tried to come up with a solution and set my confirmEmail action to be the following:
[AllowAnonymous]
public ActionResult ConfirmEmail(string userId = null, string code = null)
{
if (userId == null || code == null)
{
return View("Error");
}
else
{
var emailCode = UserManager.GenerateEmailConfirmationToken(userId);
var result = UserManager.ConfirmEmail(userId, emailCode);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
}
I thought to myself there is no way in heck this could fail, it literally just generates a token and then immediately uses it - yet somehow it still fails (by fails I mean the user sees the error page)
My best guess as to a possible solution so far is this answer from SO (Asp.NET - Identity 2 - Invalid Token Error, halfway down)
Every time when a UserManager is created (or new-ed), a new dataProtectionProvider is generated as well. So when a user receives the email and clicks the link the AccountController is already not the old one, neither are the _userManager and it's token provider. So the new token provider will fail because it has no that token in it's memory. Thus we need to use a single instance for the token provider.
But this is no longer necessary with all the OWIN stuff, right?
Is that really the issue still? If so, what the heck ASP.net Identity team? Why?
Some of the things I have changed:
The default Register Action recommends sending confirmation emails the following way:
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
Where I have the following in my Register Action
string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id, "Confirm your XXXXXXX account");
And then I specify the following in the SendEmailConfirmationTokenAsync
private async Task<string> SendEmailConfirmationTokenAsync(string userID, string subject)
{
string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
var callbackUrl = Url.Action("ConfirmEmail", "Account",
new { userId = userID, code = code }, protocol: Request.Url.Scheme);
// construct nice looking email body
await UserManager.SendEmailAsync(userID, subject, htmlBody);
return callbackUrl;
}
To me, both sections are equivalent, is this not the case?
And then the only other thing I can think of is how I added my db class, but that shouldn't affect the UserManager should it?
The top part of my account controller looks like the following (which is how the provided example from MS came + adding my database):
private readonly SiteClasses db = new SiteClasses();
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
{
UserManager = userManager;
SignInManager = signInManager;
}
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
...
We are using the recommended email provider sendgrid and I have personally never been able to replicate this issue (after creating ~60 test accounts manually) and most people seem to get along fine.
Part of this could be errors resulting from the users themselves, but this seems to be happening for a bunch of non tech-savvy people who just click on the link and expect it to work like a normal confirmation email should. Most users are coming to us from their iPhone where I would presume they are just using Apple's default mail client but not positive. I don't know of any spam filter or email settings that would remove the query string values from links.
I'm getting somewhat desperate for answers as I am trying to launch a great new startup but am getting hung up by ASP.net Identity technicalities or bugs or whatever is going on.
Advice on where to look or how to set up things would be greatly appreciated