1

I am trying to limit users from being logged in on more than one machine at a time. To do this I am changing the value of a field in my database to 1 upon login and resetting it back to 0 in the LogOff() method of AccountController.

I would like the user to be automatically logged out when the browser is closed so I set inPersistent = false at logging in like so:

await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

The problem is that when the browser is closed the LogOff() method isn't called and I am unable to reset my value to 0. Since the field has not been reset to 0, that user won't be able to log in again.

How can I update my database and reset that field to 0 when the browser is closed?

matt.
  • 2,355
  • 5
  • 32
  • 43
Awais Mahmood
  • 1,308
  • 4
  • 21
  • 51

4 Answers4

3

This is a bad way of developing things. Why won't you use slidingExpiration on cookies to invalidate a cookie.

If you want to check for unique users, you should check for the last used session and invalidate the session used before.

Is that a good alternative?

NicoJuicy
  • 3,435
  • 4
  • 40
  • 66
  • Yeah, it sounds good. But wouldn't the sliding expiration works when Formas Authentication mode is "FORMS", I have got it on net that Forms authentication is obsolete so, In my config file I have set . ?? – Awais Mahmood Oct 19 '15 at 14:51
  • Sliding expiration can be manually implemented. It just means that when the client hits the server ( eg. catch this with a http module or with a Attribute on your controller), then get the ExpirationValue of the cookie and then add some time to it. I personally have a project which stores every session and every page. When a user has the same UserAgent and IP as a previous session, i invalidate the previous session so the user gets logged out ( just mentioning, you can adjust it to your needs) – NicoJuicy Oct 19 '15 at 22:50
0

Look for onbeforeunload and make an asynchronous call to your server endpoint to do execute the necessary code.

window.onbeforeunload = function(event) {
  alert("browser closing")
  // to do : Call server 
}
Shyju
  • 214,206
  • 104
  • 411
  • 497
0

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))
    }
}); 
Community
  • 1
  • 1
matt.
  • 2,355
  • 5
  • 32
  • 43
0

Use JavaScript event as below:

window.onbeforeunload = function (e) {
    e = e || window.event;

    // For IE and Firefox prior to version 4
    if (e) {
       // write code here
    }
};
Pang
  • 9,564
  • 146
  • 81
  • 122