Reputation: 4383
I have a custom user store in my .NET Core 6 site. The corresponding user table in DB contains a field called Lockout
.
When a user signs in, I need that field to be used to forbid the user to login if that field is true.
The Login
action of the Account Controller has this code:
var result = await _signInManager.PasswordSignInAsync(model.Login, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
var appUser = await _userManager.FindByNameAsync(model.Login);
if (appUser.Roles == null || appUser.Roles.Count == 0)
{
await PerformLogOff();
return Json("ERROR: No tiene autorización para ingresar al sistema.");
}
else
{
returnUrl ??= Request.Path.ToString();
appUser.LastLoggedOn = DateTime.Now;
if (!appUser.FirstLoggedOn.HasValue)
appUser.FirstLoggedOn = appUser.LastLoggedOn;
await _userManager.UpdateAsync(appUser);
return Json(returnUrl);
}
}
else if (result.IsLockedOut)
return Json("ERROR: Su usuario está bloqueado.");
else if (result.RequiresTwoFactor)
return Json("ERROR: El usuario requiere autenticación de doble factor.");
else
return Json("ERROR: Nombre de usuario o contraseña incorrectos.");
When a user is locked out, I need PasswordSignInAsync
to return result.IsLockedOut
.
How can I do it? Should I create a custom SignInManager
? Notice that this has nothing to do with locking a user out when he fails to enter the password several times in login screen.
Thanks Jaime
Upvotes: 1
Views: 1064
Reputation: 2605
You can do it yourself by adding another field (e.g. IsActive
) to your ApplicationUser
class.
Then you can check it before the line _signInManager.PasswordSignInAsync
with some code like this:
var user = await _userManager.Users.Where(s => s.UserName == model.Username).FirstOrDefaultAsync();
if (user != null)
{
if (!user.IsActive)
{
ModelState.AddModelError("", "User has been deactivated"); //Log the failure
return View();
}
}
Upvotes: 3
Reputation: 171
To lockout a user, you have to call the SetLockoutEndDateAsync method in UserManager<> and pass in a future date. The following code locks out a user till the year 3000.
await userManager.SetLockoutEndDateAsync(userToRemove, new DateTimeOffset(3000, 1, 1, 1, 1, 1, TimeSpan.Zero));
Upvotes: 0
Reputation: 1033
If you really want result.IsLockedOut
to be true
then I can see two options:
UserManager
or AspNetUserManager
and override virtual method IsLockedOutAsync
(code) so it will be returning false
based on your Lockout
property.LockoutEnd
field in database to date from the next century e.g.:3000-01-01
when your application logic wants to set it to true
or clear the field to unlock the userFor both options you need to watch out - SignInManager
will only attempt to check lockouts if feature is enabled (code).
To enable it you should make sure that your custom UserStore
implements IUserLockoutStore<TUser>
interface. Simple override of SupportsUserLockout
(code) virtual property to return always true
in your custom UserManager
might not work - because there are few other operations made on UserStore
to count failures etc.
Probably you are not looking for opinion but I feel that your idea from comment to load user in Login
action and check your custom property is the cleanest. It is not only about hacking your solution here - but it is usualy very harmful in development teams to change well documented, battle-tested frameworks like Identity
to behave in a different way.
Upvotes: 0
Reputation: 21883
I have read source code about SignInManager.cs
. And it should support forbid lockout user.
For more details, check the blog below, it contains the code useful to you.
User Lockout with ASP.NET Core Identity
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IactionResult> Login(UserLoginModel userModel, string returnUrl = null)
{
if (!ModelState.IsValid)
{
return View(userModel);
}
var result = await _signInManager.PasswordSignInAsync(userModel.Email, userModel.Password, userModel.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
var forgotPassLink = Url.Action(nameof(ForgotPassword),"Account", new { }, Request.Scheme);
var content = string.Format("Your account is locked out, to reset your password, please click this link: {0}", forgotPassLink);
var message = new Message(new string[] { userModel.Email }, "Locked out account information", content, null);
await _emailSender.SendEmailAsync(message);
ModelState.AddModelError("", "The account is locked out");
return View();
}
else
{
ModelState.AddModelError("", "Invalid Login Attempt");
return View();
}
}
Upvotes: 0