Prevent Concurrent Logins From A Single User Account In .NET

If you are reading this then you are probably already aware that, out of the box, there is nothing to stop a user logging in multiple times with the same account details in .NET.

This can be a problem if your users are paying to consume a service from you, as they could share their login details with their friends. Or perhaps you have a process that just doesn’t play nice with multiple logins.

I’ve tried to keep this solution as simple as possible. We store minimal data to check whether a user’s login is current, and use .NET’s built in account controls by overriding part of the AuthorizeAttribute class.

This code works in MVC5, but can be adapted for use in Web Api 2 with minor changes.

Summary

To quickly outline what the code is intended to do: we will assume that all user logins are valid and therefore the most recent login is where the user is, and that all other logins are stale. We do not want to force a user to manually log off previous sessions (which would be very annoying) but should simply log them out, leaving only the current session active.

To do this, we’ll create a database table to hold users’ login details. Each time a user logs in, we’ll check if they are in this table. If they are not, we’ll add them. If they are, we’ll overwrite their existing details with their new login details. This means the table will always contain only the details of their most recent login.

Later on, when we want to check if their login is the most current, we’ll compare their credentials with what we have in the database. If their details match those in the database, it is still their most recent login and they can proceed. If their details have been overwritten due to a more recent login, we know the session is old and can mark it to be logged out.

Login Model

This holds the data we are going to store to keep track of users’ logins. I’ve seen solutions that keep track of every login a user makes, but we really don’t need that much information. We only need to keep two details: the username and some unique identifier for that session. We will only ever store details of their most recent login, so we don’t need to record, for example, whether the session is active or not. Here, I have added a Date field as well, although it is not required.

Repository Class

This class interacts with the Login table we’ll create in our database and only requires two methods. The first, PutOrPostLogin, will either add the user to the table or, if they are already in the table, overwrite the information with their new login details.

The second method, IsLoggedIn, simply checks if the username and unique id that are passed to it exist in our database. If the information is there, we consider it to be their current log in but if it’s not, they need to be logged out.

Account Controller

At some point during the login process, we need to store the user’s login details in our database. Here, in the AccountController, after a successful login, we create a new Login object and assign the users email and current Session ID to it. There are a number of options you can use for a unique identifier here. It’s worth noting that, if you’re going to use the Session ID, you need to include the lineĀ Session["sessionid"] = HttpContext.Session.SessionID; or the Session ID will not persist, and each time you check it you’ll get a different result back. Lines 5-11:

Extend AuthorizeAttribute

The AuthorizeAttribute is used in MVC to check whether users are authorised to access restricted parts of your website. We can extend this class and add in an extra check to see if our user should be allowed to remain logged in.

The method we need to override is AuthorizeCore. In Web Api 2, the method you would need to override is the perhaps more sensibly names IsAuthorized.

So, the first thing we do is call the overridden AuthorizeCore method from the base class, so that the standard authorisation checks can be done. As a safety net, if we encounter a problem and either the Username or Session ID are null or empty, we’ll give them the benefit of the doubt and just return the result from the base class.

Assuming we do have a Username and Session ID, we then just need to call our IsLoggedIn method, which will check if their session is the most recent, i.e. whether it should be considered active, or not. The AuthorizeAttribute will do the rest of the work for us and log out the user if false is returned.

Controllers

Lastly, of course, we just need to add our extended AuthorizeSingleLogin attribute to any classes we want to be protected by login

 

 

John