I’ve spent a few hours trying to figure out why my code does not work, and I have not found any explanations to the issue, so might just write it down here.
In ASP.NET Identity there is a concept for user locking out. You can specify how many attempts user can gets before the lock-out kicks in and for how long the lockout is enabled. This is a widely known, from all the articles online. But what these articles don’t say is that users can be opted-in and opted-out from this process.
If you look on IdentityUser
class, there are 2 fields that relate to lockout: LockoutEnabled
and LockoutEndDateUtc
. My first reaction was that LockoutEnabled
says that user is locked-out and LockoutEndDateUtc
is time when the lockout expires.
Turned out that I was wrong. LockoutEnabled
is a flag saying that user can (or can not) be locked in principle. I.e. opt-in flag for locking. So if we have an admin user who we don’t want to lockout ever, we’ll set this flag to be false
. And for the rest of user-base this should be set to true
.
To check if user is locked-out use UserManager.IsLockedOutAsync(user.Id)
. Function UserManager.GetLockoutEnabledAsync()
checks if user is opted in for lock-out, it does not check if user is actually locked-out.
And fields on IdentityUser
– don’t use them to detect is user is locked out, they are lies! Use UserManager functions to detect user state.
Hope this will save some people a head-banging, cause that caused me some stress!
Update 27 Aug 2014
To enable locking-out UserManager should have these flags set:
//lockout users for 10 minutes
UserManager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(10);
// takes 6 incorrect attempts to lockout
this.MaxFailedAccessAttemptsBeforeLockout = 6;
// when new user is created, they will be "lockable".
this.UserLockoutEnabledByDefault = true;
To lockout user you can do this:
user.LockoutEnabled = true;
user.LockoutEndDateUtc = DateTime.UtcNow.AddMinutes(42);
await userManager.UpdateAsync(user);
This will set the user to be locked out for 42 minutes from now. However, this is not recommended way to lock-out user, this is what the framework does in the background. There are ways users can lock themselves out.
If you are updated to AspNet Identity 2.1 (which has been released recently). You should use SignInManager
:
var signInManager = new SignInManager(userManager, authenticationManager);
var signInStatus = await signInManager.PasswordSignInAsync(username, password, isPersistent, shouldLockout);
If shouldLockout
is true, user is getting locked out after a number of failed attempts to login.
PasswordSignInAsync
method does the following steps:
- checks if user with given username exists;
- then checks if this user is not locked out;
- if password is correct, redirects logic to 2FA (if it is enabled);
- if
shouldLockout
is true, then on incorrect password increases number of failed log-ins on user record.
If you have not updated and still using Identity v2.0 do the following. On every failed attempt to login, call the following
await userManager.AccessFailedAsync(user.Id);
IdentityUser
class have property AccessFailedCount
. The method above increases count on this property. If count is greater than UserManager.MaxFailedAccessAttemptsBeforeLockout
, then user is getting locked out: user.LockoutEndDateUtc
is set to a date in the future and resets AccessFailedCount
to zero.
When user is signed-in successfully, you need to reset AccessFailedCount
to zero. Do it by calling:
await userManager.ResetAccessFailedCountAsync(user.Id);