【问题标题】:Confirmation email got Invalid token确认电子邮件收到无效令牌
【发布时间】:2015-10-08 20:13:36
【问题描述】:

我正在向我的 ASP.NET WebAPI 项目添加确认电子邮件功能。服务器可以正常发送邮件,但是确认链接总是返回“Invalid token”。

我检查了这里指出的一些原因 http://tech.trailmax.info/2015/05/asp-net-identity-invalid-token-for-password-reset-or-email-confirmation/ 但似乎它们都不是根本原因

下面是我的代码:

public async Task<IHttpActionResult> Register(RegisterBindingModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        IdentityResult result;           
            result = await UserManager.CreateAsync(user, model.Password);

            if (!result.Succeeded)
            {
                return GetErrorResult(result);
            }
            try
            {
                await userManager.AddToRoleAsync(user.Id, "Player");

                //Generate email confirmation token                                        
                //var provider = new DpapiDataProtectionProvider("GSEP");
                var provider = new MachineKeyProtectionProvider();
                userManager.UserTokenProvider = new DataProtectorTokenProvider<GSEPUser>(provider.Create("EmailConfirmation"));
                var code = await userManager.GenerateEmailConfirmationTokenAsync(user.Id);
                code = System.Web.HttpUtility.UrlEncode(code);

                EmailHelper emailHelper = new EmailHelper();
                string callBackUrl = emailHelper.GetCallBackUrl(user, code);
                EmailMessage message = new EmailMessage();
                message.Body = callBackUrl;
                message.Destination = user.Email;
                message.Subject = "GSEP Account confirmation";
                emailHelper.sendMail(message);
            }
            catch (Exception e)
            {
                return Ok(GSEPWebAPI.App_Start.Constants.ErrorException(e));
            }         
    }

现在是 EmailHelper

public class EmailHelper
{
    public string GetCallBackUrl(GSEPUser user, string code)
    {

        var newRouteValues = new RouteValueDictionary(new { userId = user.Id, code = code });

        newRouteValues.Add("httproute", true);            
        UrlHelper urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext, RouteTable.Routes);
        string callbackUrl = urlHelper.Action(
                    "ConfirmEmail",
                    "Account",
                    newRouteValues,
                    HttpContext.Current.Request.Url.Scheme
                    );
        return callbackUrl;
    }

    public void sendMail(EmailMessage message)
    {
        #region formatter
        string text = string.Format("Please click on this link to {0}: {1}", message.Subject, message.Body);
        string html = "Please confirm your account by clicking this link: <a href=\"" + message.Body + "\">link</a><br/>";

        html += HttpUtility.HtmlEncode(@"Or click on the copy the following link on the browser:" + message.Body);
        #endregion

        MailMessage msg = new MailMessage();
        msg.From = new MailAddress("myemail@example.com");
        msg.To.Add(new MailAddress(message.Destination));
        msg.Subject = message.Subject;
        msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(text, null, MediaTypeNames.Text.Plain));
        msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(html, null, MediaTypeNames.Text.Html));

        SmtpClient smtpClient = new SmtpClient("smtp-mail.outlook.com", Convert.ToInt32(587));
        System.Net.NetworkCredential credentials = new System.Net.NetworkCredential("myemail@example.com", "mypassword!");
        smtpClient.Credentials = credentials;
        smtpClient.EnableSsl = true;
        smtpClient.Send(msg);
    }
}

还有2个MachineKey类

public class MachineKeyProtectionProvider : IDataProtectionProvider
{
    public IDataProtector Create(params string[] purposes)
    {
        return new MachineKeyDataProtector(purposes);
    }
}

public class MachineKeyDataProtector : IDataProtector
{
    private readonly string[] _purposes;

    public MachineKeyDataProtector(string[] purposes)
    {
        _purposes = purposes;
    }

    public byte[] Protect(byte[] userData)
    {
        return MachineKey.Protect(userData, _purposes);
    }

    public byte[] Unprotect(byte[] protectedData)
    {
        return MachineKey.Unprotect(protectedData, _purposes);
    }
}

正如一些指令所指出的,我还在 Web.config 中添加了 machineKey 标记。 最后是我的确认电子邮件 API

    [AllowAnonymous]
    [HttpGet]
    public async Task<IHttpActionResult> ConfirmEmail(string userId, string code)
    {
        if (userId == null || code == null)
        {
            return Ok("Confirm error");
        }
        IdentityResult result;
        try
        {
            result = await UserManager.ConfirmEmailAsync(userId, code);
        }
        catch (InvalidOperationException ioe)
        {
            // ConfirmEmailAsync throws when the userId is not found.
            return Ok("UserID not found");
        }

        if (result.Succeeded)
        {
            return Ok("Confirmation succesfully");
        }
        else
        {
            return Ok(result.Errors);
        }
    }

请告诉我我哪里出错了

【问题讨论】:

    标签: asp.net email confirm


    【解决方案1】:

    我知道这是一个旧线程。但我想添加答案,因为它可以帮助其他人。

    您正在使用以下代码

    string callbackUrl = urlHelper.Action(
                        "ConfirmEmail",
                        "Account",
                        newRouteValues,
                        HttpContext.Current.Request.Url.Scheme
                        );
    

    并且UrlHelper.Action已经在最新的 MVC 版本中为您进行 url 编码。因此,在您的代码中,您执行了两次编码(一次在 Register 内,另一次在 GetCallBackUrl 内使用 urlHelper.Action),这就是您收到无效令牌错误的原因。

    【讨论】:

      猜你喜欢
      • 2015-12-13
      • 1970-01-01
      • 2018-03-19
      • 2015-01-06
      • 2015-02-16
      • 2016-12-11
      • 1970-01-01
      • 2016-06-07
      • 1970-01-01
      相关资源
      最近更新 更多