views:

365

answers:

8

How should I design a login system so that each username can only be logged on in one place at a time? I want to keep users from giving their username to someone else to login so they can avoid paying for each user.

If a user is already logged in and tries to log in on another machine should I block the 2nd login (which could be a problem if the user was logged on at work and then tried to get on at home)? Or should I allow the 2nd login and end the 1st login? Or does anyone have a better suggestion?

+2  A: 

Blizzard's World of Warcraft I believe implements this beautifully.

Basically, if you try to sign into the game after already being signed in, the first connection is kicked off.

This basically just entails making the session stored on the database. When you store the session data, store a username too. When a user logs in, delete any session records with that users name, and then create a new one for the person logging in.

I wouldn't suggest blocking 'new' people trying to log in, because users don't want to have to go back to another computer they have (possibly miles away) just because they forgot to log out.


There are also some other things you might have to think of. Things like sessionid hijacking. If a user just puts a cookie on their system (which is always possible) with the right sessionid, it is possible that they could use the same session on multiple computers. In which case you'd probably want to keep an IP field where you keep the data on who is currently logged on.

Chacha102
+1  A: 

Block the first login. If you log in at home, then in work, you don't want to be blocked, since this is a legit method. Always allow the login in the present, and drop the old ones.

erenon
+1  A: 

I would suggest keeping track of whether each user is logged in and allowing the second login to end the first login's session.

Then allow the user whose session has ended to report possible fraudulent activity if they were kicked off in error.

Aaron
+6  A: 

Some Instant Messengers (that can work only with one logged in endpoint) have a nice way of sorting out such conflicts. They show a message like

You are already logged on from <COMPUTERNAME>

(in case of a web app, that would be <IP/Browser>)

and give you a choice between

  • either leaving that logon alive (and not log on from the machine you're on), or
  • ending the existing logon (and logging on on the current machine).

This is technically the most challenging, but definitely the most friendly way - it ensures a user has only one session running, without being too obvious about it. And there is no bad blood with users unable to log in because they forgot to log out at work, etc.

Pekka
If you are storing the session data in a database, it actually doesn't sound too complicated, programming wise.
Chacha102
+1  A: 

A typical approach to this problem is to use an

inactivity time-out period.

This system enforces a maximum number of logins per account, while allowing for the situation mentioned: a user left the office without logging out, and attempts to login from his/her home workstation.

Here are the general lines of such a system

  • Each account is associated with a number of concurrent logins (aka "seats") allowed (it seems the OP wished one and only one, for every account, but this could be more, and vary on an account basis).
  • The license manager logic keeps a list of all accounts/users currently logged-in, along with a time stamp with their "last" activity.
  • Before serving any page, the web application, calls the license manager (LM). The purpose is to allow the LM to update the timestamp of "last" activity, but also to deny the call in case the license was taken (more on this below)
  • Upon each login, the license manager logic verifies that the number of seats taken doesn't exceed the amount specified for the account.
  • If that is not the case, the LM simply adds the current session to the list of active session
  • If that is the case, the LM check for sessions in the list which are older than the time-out period. If one is found, it disables it, and grants access to the new login. If none is found, the login is denied.
  • upon each [explicit] log-out, the LM removes the corresponding session from the lists of active session.

Note that the general principle outlined above can have some variations, in particular:

  • rather than silently and systematically invalidating the [typically oldest] timed-out session, one can inform the user currently attempting to logging about this situation and let him/her decide of the need to "kill" such a session.
  • To avoid burdening the LM with each and every new page request, the web application can keep track on a per-session basis of the time since the session was last "refreshed" in the LM, and only call the LM if such time exceed say 1/3 of the time-out period.

Independently from the LM logic per-se, remember to keep a log of all the LM-related events (logins, logouts, inactive session "kills", refused logins...). Such logs should include the date/time, the IP address and other relevant info, and are useful when resolving issues associated with stolen passwords and such. Such logs also contain invaluable marketing, for example to find all accounts which appear to have too few seats (and could therefore purchase some ugrade), or to find at-risk accounts etc.

A few more considerations

  • make it easy for users to log-out (log-out button/link on most every page, at a fixed location
  • make it easy for users to report conflict / stolen password situation
mjv
How would this solve the problem of multiple users signing in and working at the same time with the same username?
ck
@ck: it would work very much like I outline in my edited response. (Someone impatiently minus-ed me while I was writing this up...) I've implemented several variations on this system over the years, and I can say that it allows a strict enforcement of the number of allowed logins for a given account, while being tolerant of the fact that users do not always log-out.
mjv
A: 

Don't try to do it by counting the number of IP addresses a user has an active session from - some users may be behind load balanced proxies.

The solution is to write your own session handler - probably easiest with a database back end - and only allow one user to have one open session.

You might want to tune the session garbage collection and inactivity. You should also ensure that your system is immune from session fixation attacks.

C.

symcbean
A: 

In terms of security, and this is what you're getting at, it is always a good idea to store session data in a database anyhow. Particularly if you're on a shared server.

In terms of which user to allow and which to knock off that is a matter for you to judge. I suppose you could have some secondary form of identification to make sure they are the real owner of the account. The one who actually signed up to it.

kalpaitch
A: 

I've done this before in a web application that had the same requirement. Here's what I did:

When someone logs in, you generate a GUID and store it in your database, attached to the user. You also store this same GUID in a session cookie.

Every time a logged in user hits any page on your site, you check their cookie GUID and compare it with the GUID that is assigned to them in your database. If these GUIDs don't match, they've logged in on another machine, and you log them out from that session.

This method works really well.

AaronS