How to setup password expiration using ASP.NET Identity Framework

后端 未结 2 1885
天涯浪人
天涯浪人 2020-12-14 08:27

I have a ASP.NET project using Identity. For Identity Configuration regarding passwords, the PasswordValidator is being used. How do I expand the enforcement of

相关标签:
2条回答
  • 2020-12-14 09:10

    There is no such functionality builtin ASP.NET Identity 2. Easiest is to add a field on the user like LastPasswordChangedDate. And then check this field during each Authorization.

    public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
    {
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            var user = await GetUser(context.UserName, context.Password);
            if(user.LastPasswordChangedDate.AddDays(20) < DateTime.Now)
               // user needs to change password
    
        }
    }
    
    0 讨论(0)
  • 2020-12-14 09:18

    Adding on to @Rikard's answer...

    I added LastPasswordChangedDate to my ApplicationUser model, like so:

        public class ApplicationUser : IdentityUser
        {
            public DateTime LastPasswordChangedDate { get; set; }
    
            public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
            {
                // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
                var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
                // Add custom user claims here
                return userIdentity;
            }
        }
    

    Add a static configuration setting to AccountController (you'll need this later on in Login():

    private static readonly int PasswordExpireDays = Convert.ToInt32(ConfigurationManager.AppSettings["PasswordExpireDays"]);
    

    Then, during Login, check to see if the user should reset password. This only checks after a successful login, as to not bug user too much.

            [HttpPost]
            [AllowAnonymous]
            [ValidateAntiForgeryToken]
            public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
            {
                if (!ModelState.IsValid)
                {
                    return View(model);
                }
    
                // This doesn't count login failures towards account lockout
                // To enable password failures to trigger account lockout, change to shouldLockout: true
                var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
                switch (result)
                {
                    case SignInStatus.Success:
                        var user = await UserManager.FindByNameAsync(model.Email);
                        if (user.LastPasswordChangedDate.AddDays(PasswordExpireDays) < DateTime.UtcNow)
                        {
                            return RedirectToAction("ChangePassword", "Manage");
                        }
                        else
                        {
                            return RedirectToLocal(returnUrl);
                        }
                    case SignInStatus.LockedOut:
                        return View("Lockout");
                    case SignInStatus.RequiresVerification:
                        return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
                    case SignInStatus.Failure:
                    default:
                        ModelState.AddModelError("", "Error: Invalid username or password");
                        return View(model);
                }
            }
    

    Make sure to update the LastPasswordChangedDate when the user successfully updates password, in ManageController, ChangePassword action:

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> ChangePassword(ChangePasswordViewModel model)
        {
            if (!ModelState.IsValid)
            {
                return View(model);
            }
            var result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
            if (result.Succeeded)
            {
                var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
                if (user != null)
                {
                    user.LastPasswordChangedDate = DateTime.UtcNow;
                    await UserManager.UpdateAsync(user);
    
                    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
                }
                return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
            }
            AddErrors(result);
            return View(model);
        }
    
    0 讨论(0)
提交回复
热议问题