I'm trying to limit user login in a web application. Some sources [1] & [2] use same idea by using await _userManager.UpdateSecurityStampAsync (loginUser);
This is the complete code of Login action in AccountController:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login (LoginViewModel model, string returnUrl = null) {
ViewData["ReturnUrl"] = returnUrl;
if ( ModelState.IsValid ) {
var loginUser = await _userManager.FindByEmailAsync(model.Email);
if ( loginUser == null ) {
ModelState.AddModelError (string.Empty, "Invalid username/password.");
return View ();
}
await _userManager.UpdateSecurityStampAsync (loginUser);
var result = await _signInManager.PasswordSignInAsync(loginUser, model.Password, isPersistent: false, lockoutOnFailure: true);
if ( result.Succeeded ) {
_logger.LogInformation ("User logged in.");
return RedirectToLocal (returnUrl);
}
if ( result.RequiresTwoFactor ) {
return RedirectToAction (nameof (LoginWith2fa), new { returnUrl, model.RememberMe });
}
if ( result.IsLockedOut ) {
_logger.LogWarning ("User account locked out.");
return RedirectToAction (nameof (Lockout));
} else {
ModelState.AddModelError (string.Empty, "Invalid login attempt.");
return View (model);
}
}
// If we got this far, something failed, redisplay form
return View (model);
}
Above login action looks so familiar. It's just modified by adding UpdateSecurityStampAsync for updating the 'concurrencyStamp' and 'SecurityStamp' columns in the table (dbo.AspNetUsers) everytime user login before PasswordSignInAsync.
I tried to screen shoot from 2 different browser (1st & 2nd attempt from chrome; 3rd & 4th attempt from firefox) with same login. 2nd & 4th was captured after execute UpdateSecurityStampAsync. 2nd & 3rd had same ConcurrencyStamp and SecurityStamp.
I also modified start.cs in ConfigureServices method:
services.Configure<SecurityStampValidatorOptions> (option =>
option.ValidationInterval = TimeSpan.FromSeconds (0)
);
services.AddAuthentication ()
.Services.ConfigureApplicationCookie (options => {
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes (30);
});
But, after I modified those code above, I still can login, concurrently, from other web browser. I also can login from other machine successfully by using same user name & password.
- well, do I miss something ?
- Or, UpdateSecurityStampAsync cant be used to prevent users login concurrently ?
- Or, .Net Core can't handle this scenario and need 3rd party for user user login management ?
Any help help/suggestion is highly appreciate.