While this doesn't answer your question regarding the browser shut down, this issue could be solved with the use of the Identity SecurityStamp
.
Upon user login you could force update the SecurityStamp
which would in turn invalidate any other valid cookies and allow the user to only be signed in on one machine at a time.
This answer explains the SecurityStamp
perfectly.
So the primary purpose of the SecurityStamp
is to enable sign out everywhere. The basic idea is that whenever something security related is changed on the user, like a password, it is a good idea to automatically invalidate any existing sign in cookies, so if your password/account was previously compromised, the attacker no longer has access.
In 2.0.0 we added the following configuration to hook the
OnValidateIdentity
method in the CookieMiddleware to look at the
SecurityStamp
and reject cookies when it has changed. It also
automatically refreshes the user's claims from the database every
refreshInterval if the stamp is unchanged (which takes care of things
like changing roles etc)
If that is route you're wanting to go; this answer explains exactly how to accomplish what you're looking for (end result) in good detail.
I will provide the source code from that post for clarity.
Basically your login method would look like this.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// check if username/password pair match.
var loggedinUser = await UserManager.FindAsync(model.Email, model.Password);
if (loggedinUser != null)
{
// change the security stamp only on correct username/password
await UserManager.UpdateSecurityStampAsync(loggedinUser.Id);
}
// do sign-in
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
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("", "Invalid login attempt.");
return View(model);
}
}
And you would update your ConfigureAuth method in Startup.Auth.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
// other stuff
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(0), // <-- Note the timer is set for zero
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});