'Invalid token.' error while email confirmation ASP.NET MVC

I am trying to encapsulate Register and Login logic into my files in MVC. Everything works fine, but i always get 'Invalid token.' error after email confirmation procedure :(

Please, could you help? I am getting crazy after two days of searching the solution!

I noticed that the GenerateEmailConfirmationToken method always generates different token as it is shown in a log file. Is it ok? If the token is not stored, it should be always the same, right?

Here is my repository constructor with preparation of ASP.NET managers:

    UserStore<MyUser> store = null;
    UserManager<MyUser, string> userManager = null;
    SignInManager<MyUser, string> signInManager = null;
    IAuthenticationManager authenticationManager = null;
    AbstractLogger logger = AbstractLogger.GetInstance();

public MyUserRepository(MyDbContext context) : base(context)
        {
            store = new UserStore<MyUser>(context);
            userManager = new UserManager<MyUser>(store);

            authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
            signInManager = new SignInManager<MyUser, string>(userManager, authenticationManager);

            userManager.PasswordValidator = new PasswordValidator()
            {
                RequireDigit = false,
                RequiredLength = 6,
                RequireLowercase = false,
                RequireNonLetterOrDigit = false,
                RequireUppercase = false
            };

            userManager.UserValidator = new UserValidator<MyUser>(userManager)
            {
                RequireUniqueEmail = true,
                AllowOnlyAlphanumericUserNames = true
            };

            // Configure user lockout defaults
            userManager.UserLockoutEnabledByDefault = true;
            userManager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
            userManager.MaxFailedAccessAttemptsBeforeLockout = 5;

            userManager.EmailService = new EmailService();


            var provider = new DpapiDataProtectionProvider("MyApp.org");
            userManager.UserTokenProvider = new DataProtectorTokenProvider<MyUser, string>(provider.Create("UserToken")) as IUserTokenProvider<MyUser, string>;
        }

Here is the registration method:

public void Register(MyUser user, string password)
        {
            IdentityResult result = userManager.Create(user, password);

            //For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
            //Send an email with this link
            string code = string.Empty;

            for (int i = 0; i < 10; i++)
            {
                code = userManager.GenerateEmailConfirmationToken(user.Id);
                logger.LogInfo("USER: " + user.Id + " CODE: " + code);
            }


            logger.LogInfo(
                string.Format(
                "New user {0}; {1}; {2}",
                user.Email,
                user.Id,
                code
                ));


            string callbackUrl = string.Format("http://localhost:5320/Account/ConfirmEmail?userId={0}&code={1}", user.Id, code); // }, protocol: HttpContext.Current.Request.Url.Scheme);

            userManager.SendEmail(user.Id, "Potvrzení registrace", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
        }

Here is the confirmation method:

    public IdentityResult ConfirmEmail(string userId, string code)
            {
                return userManager.ConfirmEmail(userId, code);
            }

Here is the log output:

USER: 1371ccfd-e8fd-46ed-8bfb-9d51f68aca63 

CODE: AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAADOWwoIJFDEqzZ8IvsDocNQAAAAACAAAAAAAQZgAAAAEAACAAAABakUsHl8hIMJX5U5sOc4zEgxUY8ikanoiKoZyIJkttZgAAAAAOgAAAAAIAACAAAADeBcyc9fhA+UR93KdPyWf8zzbJJjAcleIzf4CHCTr3OmAAAABUmOOqZs1FhaSRTcT2gV4V7JRhXNqYuJxJzB0gbo5DDfX1d010qH7YYNe4+iBh6JwdpKXR4tmsPKpojUx3RyPTbKIU8X39CJGqeWAFXAnDZMWKH2ztSn5M5h8V1zrotZRAAAAAVbRUJlIZeKgN/FH5//NQWRBFqKc9GSq0TvMWkYgZeAOyIfTh+JAMoXA4FrYnmJswLZC44zmlZPdisKnsT81ArA==

USER: 1371ccfd-e8fd-46ed-8bfb-9d51f68aca63 

CODE: AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAADOWwoIJFDEqzZ8IvsDocNQAAAAACAAAAAAAQZgAAAAEAACAAAABwzJP5VCa/GBicSwTV4Jwu2kt3XvX3xeklIFeJPiYB5QAAAAAOgAAAAAIAACAAAACfIv9bgzQJ9gwyc6Qhn/ml5iQU2qgvO83RiQGbEK/U32AAAACTT2WpFg2BdwLZzWI033SeNK3rUckzxkFkbeFGY7LlkuOhnrjsg/IMyv5YM8sFst8her1bPFi0NDvheSdIWIzWtBQFQZi2VuHRZz3+RiLQllIT/OS/94f1h+yx93QzIGhAAAAAVvPIboy1DrTKpv1easktkMW/olF+MT10MuNlQivcx5wDUSuvzql5GM6GY87Nkm1lFzp9+n0XNWEpbFRqilBuMA==

Use this UserManager.GeneratePasswordResetTokenAsync method to resolve this issue..

string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
return RedirectToAction("ResetPassword", "Account", new { userId = user.Id, code = code });

Please use Url.Action instead of creating the url using string concatenation. Url.Action does the encoding behind the scenes in latest MVC versions and you can avoid the encoding and decoding operations.

Below is the code snippet you can use.

string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new
    {
        userId = user.Id,
        code = code,
        returnUrl = model.ReturnUrl
    }, protocol: Request.Url.Scheme);

If you don't want to use Url.Action then you can use HttpUtility.UrlEncode(code); to encode the token while generating the url

Thanks. The problem was really in encoding. The code should not be the same for everytime.

Comments
  • You need to turn off post validation
  • stackoverflow.com/questions/25405307/… and stackoverflow.com/questions/27535233/…
  • Sorry, but I do not understand... In the first link, a GeneratePasswordResetToken is used. It is not my case. It also is not an encoding problem, because the code is different for every time (see log).