I need to confirm the user's email by link but ASP.Net Identity fails on this action. Below the code is how I generate a token:
string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); // Return Opd+0yNG++ZXFpRS19A8j8OgI7dzCAKTYWotHYvuu0nyYsH4SIQS+bHwkbmqQDlwvGAy5fyxxUsu4yIzHwF+PD6QNPU+PwBnIDUGMC9++FkMlqegqHVKpA59qvbokfI0yByYqLoZfD1EUpWExddDN0BN/SVNSgOKlyzd928k+k4O2/TnfSf/JFj8x1NUKuaj
And how I try to check and confirm it:
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
Logger.Error(string.Format("userId: {0}, code: {1}", userId, code));
return View("Error");
}
// args values: userId: b96bf253-62e1-4c5e-bf5f-5bc527df9fd9 code: Opd 0yNG ZXFpRS19A8j8OgI7dzCAKTYWotHYvuu0nyYsH4SIQS bHwkbmqQDlwvGAy5fyxxUsu4yIzHwF PD6QNPU PwBnIDUGMC9 FkMlqegqHVKpA59qvbokfI0yByYqLoZfD1EUpWExddDN0BN/SVNSgOKlyzd928k k4O2/TnfSf/JFj8x1NUKuaj
var result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded) // This is false
{
return View("ConfirmEmail");
}
Logger.Error(string.Concat<string>(result.Errors)); // Wrong marker
return View("Error");
}
There are no null-values I checked in the debugger. Why can it fail?
P.S. I use Npgsql if it is important and all works perfect except this.
Firstly you must be determine token is correct or not. On your generate token, note that token and check your database after that.
string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
UserManager.EmailService = new EmailService();
await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
Also this is my email service for check.
public class EmailService : IIdentityMessageService
{
public System.Threading.Tasks.Task SendAsync(IdentityMessage message)
{
// Plug in your email service here to send an email.
var mail = new MailMessage
{
Subject = message.Subject,
Body = message.Body,
IsBodyHtml = true
};
mail.To.Add(message.Destination);
mail.From = new MailAddress("me@mail.com","me");
var smtp = new SmtpClient
{
Host = "smtp.mail.com",
Port = 25,
UseDefaultCredentials = false,
Credentials = new System.Net.NetworkCredential("me@mail.com", "password"),
EnableSsl = true
};
// Enter seders User name and password
smtp.Send(mail);
return System.Threading.Tasks.Task.FromResult(0);
}
When you get email check link, it must be match with callbackurl. When you click url this method should be call.
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
return View("Error");
var result = await UserManager.ConfirmEmailAsync(userId, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
In this method you must be see code variable is match with your code at your identity table.
Also there is important problem with Identity Framework. There is no dependency injection container or etc. on MVC out of the box. You must be create your own IoC. I always prefer structuremap to do this. Because lifetime manager better than others(ninject, unity etc.). Also I'm using current user object for reduce request to database or session manager.
public class CurrentUser : ICurrentUser
{
private readonly ApplicationDbContext _context;
private readonly IIdentity _identity;
private ApplicationUser _user;
public CurrentUser(IIdentity identity, ApplicationDbContext context)
{
_identity = identity;
_context = context;
}
public ApplicationUser User
{
get { return _user ?? (_user = _context.Users.Find(_identity.GetUserId())); }
}
}
In my MVC registry ( please check structuremap docs), i register everything that i need on
public class MvcRegistry : Registry
{
public MvcRegistry()
{
For<BundleCollection>().Use(BundleTable.Bundles);
For<RouteCollection>().Use(RouteTable.Routes);
For<IIdentity>().Use(() => HttpContext.Current.User.Identity);
For<IUserStore<ApplicationUser>>().Use<UserStore<ApplicationUser>>();
For<DbContext>().Use(() => new ApplicationDbContext());
For<IAuthenticationManager>().Use(() => HttpContext.Current.GetOwinContext().Authentication);
For<HttpSessionStateBase>().Use(() => new HttpSessionStateWrapper(HttpContext.Current.Session));
For<HttpContextBase>().Use(() => new HttpContextWrapper(HttpContext.Current));
For<HttpServerUtilityBase>().Use(() => new HttpServerUtilityWrapper(HttpContext.Current.Server));
}
}
With this I always use same object for same user also i prevent object reference problem and tight coupling problems as well.