16

How can I prevent multiple users from being logged in at the same time with a single user ID?

I searched the internet and found some ways, but they do not work in these situations:

  1. If JavaScript in the browser is turned off.
  2. If the user does not click "logout" and instead directly closes the browser.

Suggestions would be appreciated. Thanks.

mfluehr
  • 2,832
  • 2
  • 23
  • 31
haansi
  • 5,470
  • 21
  • 63
  • 91

8 Answers8

21

We implemented a system for this along the following lines:

  • Added a property to the users Profile to hold their session ID.
  • Whenever a user logs in, store their session ID in the Profile.
  • On any page that requires a this level of security, check to see if the session ID stored in the profile matches their session. This check could be performed in a custom AuthorizeRequest event handler, or it could be performed in a Base class that these pages derive from, and if not, redirect them to the login page.

We went for the base class option as we have two levels of authentication:

  1. The user has a cookie token to prove that they have logged in at some point in the past - this is fine for showing them restricted site content.
  2. The user has actually provided their login details this session - this is required when showing them any personal details (email addresses, preferences, saved job searches, etc).

The main issues you'll find with almost any system:

  1. Using the users IP address is unreliable - corporate users, those behind proxies, etc, often share an IP address, so would "appear" to be the same user.
  2. Relying on a user to log out is unreliable - the users computer/browser might crash not giving them the opportunity to log out, the user can/will forget to log out.
  3. Relying on session time-outs is unreliable - if you're not using InProc sessions, the SessionEnd event never fires, if your server crashes the event isn't called, etc.

The issues you'll find with a solution like mine are:

  1. It doesn't stop the second user logging in - instead it will lock out the first user, which should discourage sharing of details in the first place.
  2. If you don't implement this as an AuthorizeRequest handler you have to remember perform the check on the pages that should be locked down.

Responding to comment

In response to your specific queries:

  1. The default Profile Provider stores the data in the same SQL database as the membership provider (the tables are created along with the membership and roles tables). If you were to store it "in the cache" this would need to be the global application cache, along the lines KMan suggests in option 2 - and as pointed out the comments, you'd need to build a time-out for this, and that leads back to the issue of reliably determining this.
  2. The user doesn't log out: This is handled in our system by not locking out future users, but by locking out previously logged in users - so:
    • Alice comes to the site, logs in, starts browsing.
    • Bob comes to the site, and logs in with Alice's details, starts browsing.
    • Alice tries to continue browsing, is locked out, has to log in again.
    • Bob is now locked out.
    • etc.

At its most basic, this won't stop the users sharing their logins, but will cause them annoyance, forcing them to keep logging in. If you need to you can add a delay to the login process - so if a different session id attempts to log into the site within the session time-out (defaults to 20 minutes) or some other time, say based on the average time a user spends on a page, then deny the login attempt.

Community
  • 1
  • 1
Zhaph - Ben Duguid
  • 26,785
  • 5
  • 80
  • 117
  • Hi Zhap, thanks for guiding. I have not used user profils. So there are some relevant quries. 1. Is there some benefit of storing with user profiles. What if I store these in database or cashe etc ? 2. how we will manage if user do not logout ? what if user just close broswer ? thanks – haansi Apr 08 '10 at 11:58
12

There could be several possibilities. A quick response is:

  1. Maintain a flag in database; upon every login/out update the flag. For instance, upon every authentication request you can reject the login request if the flag is already true.

  2. Alternatively, you can maintain a list of users in the Application object and use .Contains to see if it already exists.

--EDIT--

Lets try the database flag option; and assume that you have a method called StillLoggedIn(User) that updates the date/time and flag.

So, when user logs in:

  1. The app is going to authenticate the user and set flag=1, and mark a date/time stamp.
  2. For subsequent requests, the app would call StillLoggedIn(User);

  3. Prepare a windows service that would browse the database from time to time(lets say after 5 minutes if you have 10000 users). The service would compare the database date/time with the current date/time and mark the flag as 0 if the currentTime minus lastUsedTime is greater than, lets say, 5 minutes.

It could be anything besides a database and windows service.

KMån
  • 9,896
  • 2
  • 31
  • 41
  • 6
    You'll need some sort of inactivity timeout mechanism for logged in users who didn't logout properly as well. – Bermo Apr 08 '10 at 11:19
  • hi KMan, Regarding 1. what if a user login and than close the browser instead of signing out ? His entry will remain in db and he will not able to login again. Regarding 2. If I store list of users in Application; what will happen if use close broswer ? If you say after some particular time I should check users; than plz advice how it can be checked ? – haansi Apr 08 '10 at 11:42
  • @Bermo, I know it's a little late in the game, but if you are still monitoring this thread - would removing a user from the list when the `Session_End` event fires in `Global.asax` be sufficient to handle a user not logging out properly? – Abe Miessler Jun 27 '14 at 23:01
3

This is the way i do.

Once user login, check the flag and sessionID from DB, If found logged in with same account and different sessionID(Compare newly generated current sessionID and sessionID from DB), alert user something like "System detected you did not log out from your last login, Click<"OK"> to log out last login and create new session." If OK, just replace the old SessionId with current SessionID in DB.

One more thing is to check current sessionID and sessionID from DB in everypage, if it not same, logout and redirect to login page.

No matter user did not logout properly or just close browser, user has chance to logout theirself and no need to wait until session end from IIS. It will prevent multiple login with same account at the same time.

Hope this help, thanks...

Soe Aung
  • 1
  • 1
2

What we did is we used a combination of Session state and Application state to prevent duplicate login.

When a user logs-in his userId is retrieved from the asp.net membership database and saved in the Application state as a unique object.

Since Application state is global to the application and not specific to a user session, we can check whether the userId is already saved in Application state. If it is already saved we can notify the user and stop the login. When the session expires (on Session_End within the Global.asax file), that Application state object for that particular user can be removed from Application state so that he can log in again after his session has expired.

Here's the code: In Login.aspx.cs:

protected void OnLoggingIn(object sender, LoginCancelEventArgs e)
    {
        // Accesses the database to get the logged-in user.
        MembershipUser userInfo = Membership.GetUser(LoginUser.UserName);
        UserMan.UserID = userInfo.ProviderUserKey.ToString();

        if (Application[UserMan.UserID] != null)
        {
            if (Convert.ToString(Application[UserMan.UserID]) == UserMan.UserID)
            {
                e.Cancel = true;
            }
            else
            {
                // Save the user id retrieved from membership database to application state.
                Application[UserMan.UserID] = UserMan.UserID;
            }
        }
        else
        {
            Application[UserMan.UserID] = UserMan.UserID;                
        }
    }

And in Global.asax:

void Session_End(object sender, EventArgs e)
    {
        // Code that runs when a session ends. 
        // Note: The Session_End event is raised only when the sessionstate mode
        // is set to InProc in the Web.config file. If session mode is set to StateServer 
        // or SQLServer, the event is not raised.

        if (Application[UserMan.UserID] != null)
        {
            if (Convert.ToString(Application[UserMan.UserID]) == UserMan.UserID)
            {                    
                Application.Contents.Remove(UserMan.UserID);
            }
        }
    }

Although this solution seems a little messy, it works quite well without too much coding and fewer database hits.

1

There is no one answer here as it depends on how you are authenticating users. However, the basic logic would be simple - when a user logs in, checked their username or ID against a list of already logged in users and, if there is a match, do not authenticate them (and instead give them some kind of message explaining why they cannot log in).

Obviously the exact way you do this depends on how you are authenticating users and storing user details - you will need to supply more details if you want more help.

Dan Diplo
  • 25,076
  • 4
  • 67
  • 89
  • Hi Dan Diplo, I am authenticating users from database using id & password. Than I place userId in session. It is not issue to maintain list of currently connected users; issue is how to track them if they close browser and do not logout. What if client side scriptign is disable on visiters browers ? – haansi Apr 08 '10 at 11:45
  • 1
    You could maybe set a short session timeout and then handle the Session_End event (in global.ascx) to log out users when the session times out. – Dan Diplo Apr 08 '10 at 13:03
1

I looked at the membership table and didnt see any column like "IsLoggedIn" so membership API does not meet this requirement.

May be you can use Asp.net Cache system and flag user as "LoggedIn". With this way you can check for extra logins.

Efe Kaptan
  • 465
  • 2
  • 11
  • hi Efe, Lis of current users can be maintained. Issue is how to keep track of users. What if they close browser and do not logout. What if client side scriptign is disable on visiters browers ? – haansi Apr 08 '10 at 11:47
0

Zhaph - Ben Duguid, Little note Profile can not be accessed in AuthenticateRequest as it is not created until AcquireSessionState app event so if they were to use such an approach (instead of page approach) They would have to handle AcquireRequestState or PostAcquireRequestState

0

When member loged in. setup a Random int ex. randNo, save cache[UserName]=randNo, session[UserName]=randNo. When member access any page we check: cache[UserName]==Session[UserName] is ok, else this user will be Log Out. (Mean: login first, logout first)

NamPT
  • 49
  • 5