0

I am trying to confirm email from my users and it always works locally on development environment but it always fails in hosted server. I cannot RDP to this server and this is third-party server where I have subscribed. It gives "Invalid Token" error everytime. Nothing else.

Is there any workaround for this ? Please advise.

Thanks Adam

This is how the code is generated and encoded.

    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var newcode = HttpUtility.UrlEncode(code);

This is how the code is decoded and checked in 'ConfirmEmail' action.

    var newcode = HttpUtility.UrlDecode(code);
var result = await _userManager.ConfirmEmailAsync(user, newcode);

This is the full code that generates the Token.

        [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
    {
        ViewData["ReturnUrl"] = returnUrl;
        try
        {
            if (ModelState.IsValid)
            {
                string defaultUserRole = "UnAssigned";
                // Send an email with this link
                var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
                var result = await _userManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    if (_roleManager != null)
                    {
                        var rolesAdded = await AddRoles();
                        if (rolesAdded == false)
                        {
                            throw new Exception("Unable to add user roles in database.");
                        }
                        var resultAddRole = await _userManager.AddToRoleAsync(user, defaultUserRole);
                        if (resultAddRole.Succeeded == false)
                        {
                            throw new Exception("Unable to add user to UnAssigned Role.");
                        }
                    }
                    var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                    var newcode = HttpUtility.UrlEncode(code);
                    var callbackUrl = Url.Action(nameof(ConfirmEmail), "Account", new { userId = user.Id, code = newcode }, protocol: HttpContext.Request.Scheme);
                    await _emailSender.SendEmailAsync(model.Email, "Confirm your account", $"Please confirm your account by clicking this link: <a href='{callbackUrl}'>link</a>");
                    _logger.LogInformation("Email sent.");
                    UserInfoViewModel uiVM = new UserInfoViewModel(user.UserName, user.Email, defaultUserRole);
                    return RedirectToAction(nameof(AccountController.ConfirmRegistration), "Account", uiVM);
                }
                else
                {
                    if (result.Errors.ToList().Count > 0)
                    {
                        string errorInfo = result.Errors.ToList()[0].Code + " : " + result.Errors.ToList()[0].Description;
                        return RedirectToAction(nameof(HomeController.Error), "Home", new { errorMessage = "Result Errors : " + errorInfo });
                    }
                    else
                    {
                        return RedirectToAction(nameof(HomeController.Error), "Home", new { errorMessage = "Unknown error. Please contat system admin." });
                    }
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }
        catch (Exception ex)
        {
            return RedirectToAction(nameof(HomeController.Error), "Home", new { errorMessage = "Final Exception : "+  ex.ToString() });
        }
    }

This is the full code that validates the token. This is the default scaffolding from Visual Studio 2017.

        [HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> ConfirmEmail(string userId, string code)
    {
        try
        {
            if (userId == null || code == null)
            {
                return RedirectToAction(nameof(AccountController.AppsArkLogin), "Account");
            }
            if (_userManager == null)
            {
                throw new Exception("User manager is null.");
            }
            var user = await _userManager.FindByIdAsync(userId);
            if (user == null)
            {
                throw new ApplicationException($"Unable to load user with ID '{userId}'.");
            }
            var newcode = HttpUtility.UrlDecode(code);
            var result = await _userManager.ConfirmEmailAsync(user, newcode);
            if (result == null)
            {
                return RedirectToAction(nameof(HomeController.Error), "Home", new { errorMessage = "ConfirmEmailAsync result is null." });
            }
            if (result.Succeeded)
            {
                //return RedirectToAction(nameof(HomeController.Error), "Home", new { errorMessage = "This is working." });
                return View("ConfirmEmail");
            }
            else
            {
                if (result.Errors.ToList().Count > 0)
                {
                    string errorInfo = result.Errors.ToList()[0].Code + " : " + result.Errors.ToList()[0].Description;
                    return RedirectToAction(nameof(HomeController.Error), "Home", new { errorMessage = errorInfo });
                }
                else
                {
                    throw new Exception("Unknown error. Please contact system admin.");
                }
            }
        }
        catch (Exception ex)
        {
            return RedirectToAction(nameof(HomeController.Error), "Home", new { errorMessage = ex.ToString() });
        }
    }
trailmax
  • 34,305
  • 22
  • 140
  • 234
Adam
  • 1,221
  • 4
  • 13
  • 27
  • Hello, you mean when users click on the link in the mail they have the error : "invalid token"? Your code seems fine to be honest. If you cannot reproduce the issue to debug it locally that is going to be hard to investigate. Maybe you could check that the token once decoded is the same you have in database? – Nerevar May 11 '18 at 13:40
  • Depending on the asp.net mvc version (that could be different in production) you might not need to decode the token. – Nerevar May 11 '18 at 13:45
  • I spent hours on this. I have even went to the extent of simply cutting and pasting the token and submitting the "Form" using httpPost. It all works great locally. When it is deployed to my "Hosted Server" it just fails. I think, this function "var result = await _userManager.ConfirmEmailAsync" is the main problem. I guess I have to device my own "Token Confirmation" logic. Thank you for checking into my code. – Adam May 11 '18 at 15:45
  • I guess this is the right reason. (https://stackoverflow.com/a/47286936/6647929) The machine behavior. The problem is in ASP.NET Core 2.0 there is no Web.Config file. I guess I need to pass these parameters in JSON settings. – Adam May 11 '18 at 15:52
  • @Adam, you can use web.config see [web.config file](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/index?view=aspnetcore-2.1&tabs=aspnetcore2x#webconfig-file) with IIS. – Mark G May 11 '18 at 17:48
  • I had a similar problem and I solved it by defining a common place for the dataprotection keys. learn.microsoft.com/en-us/aspnet/core/security/data-protection/implementation/key-storage-providers?view=aspnetcore-2.1 – aloji Aug 29 '18 at 16:02

0 Answers0