views:

328

answers:

5

Looking for a robust and efficient implementation where I can restrict a user to only log in to my web app from a single computer.

If a the same user is already logged in and another person tries to log in from another computer then I should either have the option to

  1. end the session of the currently logged in user, or
  2. show a message to the new person trying to log in with the same user account.

Currently authentication is done using Forms Authentication with custom membership and role providers.

Our server is showing it's age so I'm looking for something that uses the least processing power and hopefully does very few db reads (if at all needed). My initial naive implementation is to store the IP (in db? app state?) on a successful login and then check on each page request or each other log in attempt depending on scenario. Would like to hear of better ideas =)

A: 

You need to register a Dictionary containing the IP address as value, and the UserId as the index in the Application context like this:

In the Global.asax file, initialize the values at Application_Start:

protected void Application_Start(object sender, EventArgs e)
{
    Application["OpenSessions"] = new Dictionary<string, string>();
}

When authenticating a user, just make a lookup on the dictionary to check if the currently logging user is already in the list, using another computer. If so, display an error message. If the user wants to connect, he has to go back to the previous computer, authenticate again, and disconnect. For this, don't forget to create a link to let the user Disconnect from any computer, and remove it's entry at this point.

You should also intercept Session_End events to remove this entry.

Sébastien Ros
A: 

If I understand your question correctly, you wish to make sure each user can only be logged on once at a given time. As far as I know the ASP.NET Membership provider only stores the last activity date and the last login date. You could keep a list of user id's that are logged in and display your message when a new user tries to login as a user that is already in that list. A complication would be that you need to remove the user from this 'LoggedOn' list when he logs out. You can perhaps use session_end in Global.asax

edosoft
How do you handle the unreliability of session_end firing?
fung
By adding a timer-like solution to periodically clean the cache. On second thought use the ASP.NET cache to store the list of userid's
edosoft
+2  A: 

You can do it this way:

  • Store the current Session Id (HttpContext.Current.Session.SessionID) in the Application object, along with a time stamp.
  • At the next request (e.g. in Global.asax), check if the current session is the same as before and if less than 20 minutes have passed. If the session is the same, let them work normally. If they are different, only let them work if 20 minutes have passed. Do update the Application object.

This will allow one user at a time, on one computer at a time. It is probably not 100% safe, but it is a very viable way to do it.

Sklivvz
+1  A: 

Hi,

Earlier i got a similar situation, and followed the below appraoch

  • Along with login name maintain a session id and timestamp in each request.
  • And allow the user to gain access only if both login & session id combination are same.
  • If the combination differs,you can either

    • log off the first logged in user (by showing notification to them
      saying the some other user logged into your
      account ) 0r
    • log off the newly enterd user saying already this account is in use

you can use timestamp of the request to validate the session timeouts..

Cheers

Ramesh Vel

Ramesh Vel
my answer looks same as skilwz response... :(.. his answer dint shown up when i started typing...
Ramesh Vel
A: 

Hi It's the way I've implemented this (I've added these lines to onload of my Page's base class):

  bool authenticated = UserIsAuthenticated();

  if (Session["~~~loginprocessed"] == null)
  {
    if (authenticated)
    {
      // user just logged in:
      if (Application[Page.User.Identity.Name] != null)
        Application.Remove(Page.User.Identity.Name);
      Application.Add(Page.User.Identity.Name, Session.SessionID);

      Session.Add("~~~loginprocessed", true);
    }
  }
  else
  {
    if (!authenticated)
    {
      Session.Remove("~~~loginprocessed");
    }
  }

  if (authenticated)
  {
    if ((Application[Page.User.Identity.Name] == null) || (Application[Page.User.Identity.Name].ToString() != Session.SessionID))
    {
      System.Web.Security.FormsAuthentication.SignOut();

      // show some message to user which will tell he has signed out because of login from somewhere else!
    }
  }
KiNG