ASP.NET MVC4 – Prevent The Same User ID From Logging In From Multiple Devices
The first thing that comes to my mind is Spotify. The Spotify app will not allow the same user ID to log in from multiple devices, otherwise it pauses the music on the “other” device.
I needed a way to basically do the same thing. I’m writing an app for a friend of mine and he shared with me his concern of people sharing User ID’s. His app is going to be a paid subscription app and wants to prevent the sharing of user ID’s between people- i.e. sharing ID’s on multiple devices. Makes sense, and is perfectly reasonable.
Out of the box, ASP.NET does not support this. By default, the same User ID is able to log in to multiple devices however and whenever they want. The closest you can come to preventing this is to set the SessionState timeout, and that doesn’t even come close to achieving the goal of preventing the same user ID from logging in from multiple devices.
I’ve implemented a custom solution, please see below.
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:
|
1 2 3 4 5 6 7 |
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:
|
1 2 3 |
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:
|
1 2 3 4 |
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:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
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 on multiple devices, and if so, kick them off by simply setting their LoggedIn status to false in the Logins table thereby preventing multiple logins.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public static bool IsYourLoginStillTrue(string userId, string sid) { CapWorxQuikCapContext context = new CapWorxQuikCapContext(); IEnumerablelogins = (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(); IEnumerablelogins = (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(); IEnumerablelogins = (from i in context.Logins where i.LoggedIn == true && i.UserId == userId && i.SessionId != sid select i).AsEnumerable(); foreach (Logins item in logins) { item.LoggedIn = false; } context.SaveChanges(); } |
.NET Tips & Tricks by Mike Marks















