How to setup password expiration using ASP.NET Identity Framework How to setup password expiration using ASP.NET Identity Framework asp.net asp.net

How to setup password expiration using ASP.NET Identity Framework


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    }}


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);    }