ResetPasswordAsync returns 'Invalid Token'

2019-07-31 18:14发布

问题:

I have a scheduled WebJob that runs on daily basis and checks the password expiry date for all users in my database. If the password expiry date is today, it will generate a password reset token and send it to the user via email. Then user clicks the url in the email and is brought to a website, where they input the new password.

I managed to generate a token in my WebJob and send it over via email. However, when resetting the password through my Asp.NET Website I get Invalid Token. I cannot figure out why. I assume it must have something to do with the token provider from my WebJob.

1) My Asp.NET website. The custom UserManager:

public class CustomUserManager : UserManager<ApplicationUser> {
    public CustomUserManager(IUserStore<ApplicationUser> store) : base(store) { }          

    public static CustomUserManager Create(IdentityFactoryOptions<CustomUserManager> options, IOwinContext context) {
        var db = context.Get<DataContext>();
        var manager = new CustomUserManager(new UserStore<ApplicationUser>(db));
        // [...]          
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null) {
            manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
        }
        // [...]   
        return manager;
    }
}

Which is used like this:

userManager = HttpContext.GetOwinContext().Get<CustomUserManager>();
// [...]
await userManager.ResetPasswordAsync(model.Id, model.Token, model.ConfirmPassword); // token here is invalid (although the string looks like a proper token)

2) My WebJob function:

public static async void CheckPasswords([QueueTrigger("checkpasswords")] string message) {
    using (var db = new DataContext())
    using (var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(db))) {
        var provider = new DpapiDataProtectionProvider("MyApp");
        userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("PasswordReset"));

        var users = await queryHandler.Run(new UserPasswordExpiryQuery());
        foreach (var user in users) {
            var days = new DateCalculations().DaysFromNow(user.PasswordExpiryDate);
            // if password expired today
            if (days == 0) {
                var token = await userManager.GeneratePasswordResetTokenAsync(user.Id);
                var url = string.Format("{0}/resetpass?user={1}&token={2}", settings.BaseUrl, user.Id, HttpUtility.UrlEncode(token));
                // [...] send email logic here
            }
        }
    }
}

LATER EDIT

I think I might have figured it out. I replaced the token provider in my Asp.NET app:

Old code:

var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null) {
manager.UserTokenProvider =
    new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}

New code:

 var provider = new DpapiDataProtectionProvider("MyApp");
 manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("ASP.NET Identity"));

Will do some further testing later on.

回答1:

It's possible that the logic you are running is running against some sandbox limitation.

If you share your web app name, either directly or indirectly, and the UTC time of one such failure, I could potentially confirm this.