When the same user ID is trying to log in on multiple devices, how do I kill the session on the other device?
I came up with a pretty awesome solution to this. What I've implemented was when user "Bob" logs in from their PC, and then the same user "Bob" logs in from another location, the log-in from the first location (their PC) will be killed while allowing the second log-in to live. Once a user logs in, it inserts a record into a custom table I created called "Logins". Upon a successful log-in, one record will be inserted into this table with values for "UserId, SessionId, and LoggedIn". UserId is pretty self-explanatory, SessionId is the current Session ID (explained below how to get), and LoggedIn is simply a Boolean that's initially set to True upon a successful user log-in. I place this "insert" logic inside my Login method of my AccountController upon successful validation of the user- see below:
Logins login = new Logins();
login.UserId = model.UserName;
login.SessionId = System.Web.HttpContext.Current.Session.SessionID;;
login.LoggedIn = true;
LoginsRepository repo = new LoginsRepository();
repo.InsertOrUpdate(login);
repo.Save();
For my situation, I want to place the check on each of my controllers to see if the currently logged in user is logged in elsewhere, and if so, kill the other session(s). Then, when the killed session tries to navigate anywhere I placed these checks on, it'll log them out and redirect them to the Log-in screen.
I have three main methods that does these checks:
IsYourLoginStillTrue(UserId, SessionId);
IsUserLoggedOnElsewhere(UserId, SessionId);
LogEveryoneElseOut(UserId, SessionId);
Save Session ID to Session["..."]
Before all of this though, I save the SessionID to the Session collection inside the AccountController, inside the Login
([HttpPost]
) method:
if (Membership.ValidateUser(model.UserName, model.Password))
{
Session["sessionid"] = System.Web.HttpContext.Current.Session.SessionID;
...
Controller Code
I then place logic inside my controllers to control the flow of the execution of these three methods. Notice below that if for some reason Session["sessionid"]
is null
, it'll just simply assign it a value of "empty". This is just in case for some reason it comes back as null:
public ActionResult Index()
{
if (Session["sessionid"] == null)
Session["sessionid"] = "empty";
// check to see if your ID in the Logins table has LoggedIn = true - if so, continue, otherwise, redirect to Login page.
if (OperationContext.IsYourLoginStillTrue(System.Web.HttpContext.Current.User.Identity.Name, Session["sessionid"].ToString()))
{
// check to see if your user ID is being used elsewhere under a different session ID
if (!OperationContext.IsUserLoggedOnElsewhere(System.Web.HttpContext.Current.User.Identity.Name, Session["sessionid"].ToString()))
{
return View();
}
else
{
// if it is being used elsewhere, update all their Logins records to LoggedIn = false, except for your session ID
OperationContext.LogEveryoneElseOut(System.Web.HttpContext.Current.User.Identity.Name, Session["sessionid"].ToString());
return View();
}
}
else
{
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account");
}
}
The Three Methods
These are the methods I use to check to see if YOU are still logged in (i.e. make sure you weren't kicked off by another log-in attempt), and if so, check to see if your User ID is logged in somewhere else, and if so, kick them off by simply setting their LoggedIn status to false
in the Logins table.
public static bool IsYourLoginStillTrue(string userId, string sid)
{
CapWorxQuikCapContext context = new CapWorxQuikCapContext();
IEnumerable<Logins> logins = (from i in context.Logins
where i.LoggedIn == true && i.UserId == userId && i.SessionId == sid
select i).AsEnumerable();
return logins.Any();
}
public static bool IsUserLoggedOnElsewhere(string userId, string sid)
{
CapWorxQuikCapContext context = new CapWorxQuikCapContext();
IEnumerable<Logins> logins = (from i in context.Logins
where i.LoggedIn == true && i.UserId == userId && i.SessionId != sid
select i).AsEnumerable();
return logins.Any();
}
public static void LogEveryoneElseOut(string userId, string sid)
{
CapWorxQuikCapContext context = new CapWorxQuikCapContext();
IEnumerable<Logins> logins = (from i in context.Logins
where i.LoggedIn == true && i.UserId == userId && i.SessionId != sid // need to filter by user ID
select i).AsEnumerable();
foreach (Logins item in logins)
{
item.LoggedIn = false;
}
context.SaveChanges();
}
EDIT I just also want to add that this code ignores the capability of the "Remember Me" feature. My requirement didn't involve this feature (in fact, my customer didn't want to use it, for security reasons) so I just left it out. With some additional coding though, I'm pretty certain that this could be taken into consideration.
When the same user ID is trying to log in on multiple devices, how do I kill the session on the other device? Asp.net Identity
The main role of SecurityStamp is to allow this functionality. Change it, and all of the signed in devices will be effectively signed out.
For more information, you can read What is ASP.NET Identity's IUserSecurityStampStore<TUser> interface?
When the same user ID is trying to log in on multiple devices, how do I kill the session on the other device? Asp.net Identity
The main role of SecurityStamp is to allow this functionality. Change it, and all of the signed in devices will be effectively signed out.
For more information, you can read What is ASP.NET Identity's IUserSecurityStampStore<TUser> interface?
How to prevent multiple logins from same user?
Take a look at this it protect you from Cross-Site Request Forgery and you can check if user had logged in.
Try: save csrf token to db, then check if users token same that in db...
If not: unset cookie and session for this user and return him to Sign In page;
If yes: do your stuff
Related Topics
How to Use Use Late Binding to Get Excel Instance
How to Run Visual Studio Without Plugin and All Third Party Feature
How to Implement a Custom Razorviewengine to Find Views in Non-Standard Locations
Where's the Datetime 'Z' Format Specifier
Why Is Array.Length an Int, and Not an Uint
How to Split a String by Strings and Include the Delimiters Using .Net
How to Protect Resources That May Be Used in a Multi-Threaded or Async Environment
"There Was an Error Running the Selected Code Generator" in VS 2013 Scaffolding
"Parameter Not Valid" Exception Loading System.Drawing.Image
Play and Wait for Animation/Animator to Finish Playing
Registering a Custom JSONconverter Globally in JSON.Net
Why Is Ushort + Ushort Equal to Int
Change Sender Address When Sending Mail Through Gmail in C#
Using Itextsharp to Extract and Update Links in an Existing PDF