确认电子邮件获得无效令牌

时间:2015-07-19 17:43:47

标签: asp.net email confirm

我正在为我的ASP.NET WebAPI项目添加确认电子邮件功能。服务器可以发送电子邮件,但是,确认链接始终返回“无效令牌”。

我查看了这里指出的一些原因 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);
        }
    }

请告诉我哪里出错了

1 个答案:

答案 0 :(得分:0)

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

您使用以下代码

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

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