简体   繁体   English

ASP.NET标识电子邮件确认无效标记

[英]ASP.NET Identity Email Confirmation Invalid Token

I'm using Asp.Net Identity. 我正在使用Asp.Net Identity。 I systematically have an Invalid token error when I want to confirm new users with an email confirmation token. 当我想使用电子邮件确认令牌确认新用户时,我系统地出现了无效令牌错误。

Here's my WebApi user controller: 这是我的WebApi用户控制器:

public class UsersController : ApiController
{
    private MyContext _db;
    private MyUserManager _userManager;
    private MyRoleManager _roleManager;


public UsersController()
{
    _db = new MyContext();
    _userManager = new MyUserManager(new UserStore<MyUser>(_db));
    _roleManager = new MyRoleManager(new RoleStore<IdentityRole>(_db));
}

//New user method
[HttpPost]
public async Task<HttpResponseMessage> Register([FromBody]PostUserModel userModel)

{
//New user code
...

var token = await _userManager.GenerateEmailConfirmationTokenAsync(user.Id);
            var message = new IdentityMessage();
            message.Body = string.Format("Hi {0} !\r\nFollow this link to set your password : \r\nhttps://www.mywebsite.com/admin/users/{1}/reset?token={2}", user.UserName, user.Id, HttpUtility.UrlEncode(token));
            message.Subject = "Confirm e-mail";
            message.Destination = user.Email;
            await _userManager.SendEmailAsync(user.Id, message.Subject, message.Body);

            return Request.CreateResponse(HttpStatusCode.OK, res);                     }
    }

To confirm the email, I'm just doing : 要确认电子邮件,我只是这样做:

var result = await _userManager.ConfirmEmailAsync(user.Id, token);

I'm not doing an HttpUtility.UrlDecode because WebApi does it by itself. 我没有做HttpUtility.UrlDecode,因为WebApi自己做。

And my custom user manager class : 我的自定义用户管理器类:

public class MyUserManager : UserManager<MyUser>
    {
        public MyUserManager(IUserStore<MyUser> store)
            : base(store)
        {            
            UserValidator = new UserValidator<MyUser>(this)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };
            // Configure validation logic for passwords
            PasswordValidator = new PasswordValidator
            {
                RequiredLength = 6,
                RequireNonLetterOrDigit = true,
                RequireDigit = true,
                RequireLowercase = true,
                RequireUppercase = true,
            };
            // Configure user lockout defaults
            UserLockoutEnabledByDefault = false;

            EmailService = new EmailService();
            SmsService = new SmsService();

            var dataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("MyApp");

            UserTokenProvider = new Microsoft.AspNet.Identity.Owin.DataProtectorTokenProvider<MyUser>(dataProtectionProvider.Create("UserToken"));            
        }
    }
}

Any idea ? 任何的想法 ? Thank you very much 非常感谢你

I've had the same issue and I managed to solve it without implementing custom provider based on machine keys . 我遇到了同样的问题,我设法解决了这个问题而没有实现基于机器密钥的自定义提供程序

I've had the UserTokenProvider which is set up in the UserManager class wired up by DI as per-request scope. 我有UserTokenProvider ,它在UserManager类中设置,由DI按请求范围连接。 So with each request, I was creating new UserTokenProvider . 因此,对于每个请求,我都在创建新的UserTokenProvider

And when I checked the source code, I noticed that it's using DpapiDataProtectionProvider somewhere inside by default. 当我检查源代码时,我注意到默认情况下它在内部的某个地方使用DpapiDataProtectionProvider And the constructor of DpapiDataProtectionProvider used is: 并且使用的DpapiDataProtectionProvider的构造函数是:

public DpapiDataProtectionProvider()
  : this(Guid.NewGuid().ToString())
{
}

So each time DpapiDataProtectionProvider is created, it generates a new GUID that is used as the app name. 因此,每次创建DpapiDataProtectionProvider ,它都会生成一个用作应用程序名称的新GUID。

There are two solutions to solve this: 有两种解决方案可以解决这个问题:

  1. Wire it up as a singleton. 将其连接成一个单身人士。
  2. Make it use some fixed app name. 让它使用一些固定的应用程序名称。

I decided to use the second one, since the singleton solution would break again on app pool recycles etc. 我决定使用第二个,因为单例解决方案会再次破坏应用程序池回收等。

So now my DI registration (using Simple Injector) looks like this (still using per-request scope even though it might not be necessary): 所以现在我的DI注册(使用Simple Injector)看起来像这样(尽管可能没有必要,仍然使用每个请求范围):

container.Register<IUserTokenProvider<ApplicationUser, string>>(() =>
    new DataProtectorTokenProvider<ApplicationUser>
        (new DpapiDataProtectionProvider("MY APPLICATION NAME").Create("ASP.NET Identity"))
        {
            // all user tokens are only valid for 3 hours
            TokenLifespan = TimeSpan.FromHours(3)
        }, Lifestyle.Scoped);

Hope this helps somebody. 希望这有助于某人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM