Geeks With Blogs
Frez Phil Fresle's Developer Blog

This article is largely based on information learned within the book "Professional ASP.NET 3.5 Security, Membership, and Role Management with C# and VB" by Bilal Haidar.

A common question asked regarding ASP.NET is how can you prevent a user logging on more than once at the same time. Unfortunately, the nature of ASP.NET means that you cannot tell if a user is logged in already. Sure you can log the fact a user has accessed your application, but there is no way to tell that they have abandoned their old session, perhaps by closing their browser, and that their new login is therefore valid.

However, if we look at the problem from another angle there is a way to prevent concurrent access by a user. The steps we take are as follows:

  1. When the user logs in we generate a unique guid token to represent their session. We store this token in the authentication cookie, and also in the membership database within the comment property for the user.

  2. Every time the user accesses a page where he requires authenticated access we will check the token stored in the cookie against that stored in the membership database, and as long as they are identical we will allow access. However, if the user has logged in from another computer the token in the membership database will have changed and we will deny access and log them off from their old session.

To implement this in a web site we need to make several changes.

  1. We will make code changes to the LoggedIn event from the login control (or to the custom code we have written once the user has logged in). I place this code in a static method within a custom static class that I then call from the event which makes it easier to reuse in other projects.

  2. We will create a class that implements IHttpModule that will execute code for the OnPostAuthenticate event for all our pages that are authenticated. This separate class also makes it easy to implement in other projects.

  3. We will register the HttpModule in our web.config file.

So, onto the code. First my SingleSessionPreparation class that contains the method CreateAndStoreSessionToken to be called from the LoggedIn event.

 

using System;
using System.Web;
using System.Web.Security;

/// <summary>
/// SingleSessionPreparation is used to help ensure
/// users may only have one session active
/// </summary>
internal static class SingleSessionPreparation
{
    /// <summary>
    /// Called during LoggedIn event. Need to pass username
    /// as login process not fully completed
    /// </summary>
    internal static void CreateAndStoreSessionToken(string userName)
    {
        // Will be using the response object several times
        HttpResponse pageResponse = HttpContext.Current.Response;

        // 'session' token
        Guid sessionToken = System.Guid.NewGuid();

        // Get authentication cookie and ticket
        HttpCookie authenticationCookie =
            pageResponse.Cookies[FormsAuthentication.FormsCookieName];
        FormsAuthenticationTicket authenticationTicket =
            FormsAuthentication.Decrypt(authenticationCookie.Value);

        // Create a new ticket based on the existing one that includes the 'session' token in the userData
        FormsAuthenticationTicket newAuthenticationTicket =
            new FormsAuthenticationTicket(
            authenticationTicket.Version,
            authenticationTicket.Name,
            authenticationTicket.IssueDate,
            authenticationTicket.Expiration,
            authenticationTicket.IsPersistent,
            sessionToken.ToString(),
            authenticationTicket.CookiePath);

        // Store session token in Membership comment
        // You may want to store other information in the comment
        // field, if so, you may have to implement some dilimited
        // structure within it, perhaps xml
        MembershipUser currentUser = Membership.GetUser(userName);
        currentUser.Comment = sessionToken.ToString();
        Membership.UpdateUser(currentUser);

        // Replace the authentication cookie
        pageResponse.Cookies.Remove(FormsAuthentication.FormsCookieName);

        HttpCookie newAuthenticationCookie = new HttpCookie(
            FormsAuthentication.FormsCookieName,
            FormsAuthentication.Encrypt(newAuthenticationTicket));

        newAuthenticationCookie.HttpOnly = authenticationCookie.HttpOnly;
        newAuthenticationCookie.Path = authenticationCookie.Path;
        newAuthenticationCookie.Secure = authenticationCookie.Secure;
        newAuthenticationCookie.Domain = authenticationCookie.Domain;
        newAuthenticationCookie.Expires = authenticationCookie.Expires;

        pageResponse.Cookies.Add(newAuthenticationCookie);
    }
}

 

Then my SingleSessionEnforcement class that will stop the user from logging in multiple times:

using System;
using System.Web;
using System.Web.Security;

/// <summary>
/// Enforces a single login session
/// Needs an entry in Web.Config, exactly where depends on the version of IIS, but you
/// can safely put it in both places.
/// 1:
///  <system.web>
///     <httpModules>
///      <add name="SingleSessionEnforcement" type="SingleSessionEnforcement" />
///    </httpModules>
///  </system.web>
/// 2:
///  <system.webServer>
///    <modules runAllManagedModulesForAllRequests="true">
///      <add name="SingleSessionEnforcement" type="SingleSessionEnforcement" />
///    </modules>
///  </system.webServer>
/// Also, slidingExpiration for the forms must be set to false, also set a
/// suitable timeout period (in minutes)
///  <authentication mode="Forms">
///   <forms protection="All" slidingExpiration="false" loginUrl="login.aspx" timeout="600" />
///  </authentication>
/// </summary>
public class SingleSessionEnforcement : IHttpModule
{
    public SingleSessionEnforcement()
    {
        // No construction needed
    }

    private void OnPostAuthenticate(Object sender, EventArgs e)
    {
        Guid sessionToken;

        HttpApplication httpApplication = (HttpApplication)sender;
        HttpContext httpContext = httpApplication.Context;

        // Check user's session token
        if (httpContext.User.Identity.IsAuthenticated)
        {
            FormsAuthenticationTicket authenticationTicket =
                ((FormsIdentity)httpContext.User.Identity).Ticket;

            if (authenticationTicket.UserData != "")
            {
                sessionToken = new Guid(authenticationTicket.UserData);
            }
            else
            {
                // No authentication ticket found so logout this user
                // Should never hit this code
                FormsAuthentication.SignOut();
                FormsAuthentication.RedirectToLoginPage();
                return;
            }

            MembershipUser currentUser = Membership.GetUser(authenticationTicket.Name);

            // May want to add a conditional here so we only check
            // if the user needs to be checked. For instance, your business
            // rules for the application may state that users in the Admin
            // role are allowed to have multiple sessions
            Guid storedToken = new Guid(currentUser.Comment);

            if (sessionToken != storedToken)
            {
                // Stored session does not match one in authentication
                // ticket so logout the user
                FormsAuthentication.SignOut();
                FormsAuthentication.RedirectToLoginPage();
            }
        }
    }

    public void Dispose()
    {
        // Nothing to dispose
    }

    public void Init(HttpApplication context)
    {
        context.PostAuthenticateRequest += new EventHandler(OnPostAuthenticate);
    }
}

 

Changes to web.config to ensure that SingleSessionEnforcement is called:

 

  <system.web>
    <httpModules>
      <add name="SingleSessionEnforcement" type="SingleSessionEnforcement" />
    </httpModules>
  </system.web>

  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="SingleSessionEnforcement" type="SingleSessionEnforcement" />
    </modules>
  </system.webServer>

 

And finally, code to our LoggedIn event:

 

protected void LoginUser_LoggedIn(object sender, EventArgs e)
{
    TextBox userNameTextBox = (TextBox)LoginUser.FindControl("UserName");

    SingleSessionPreparation.CreateAndStoreSessionToken(userNameTextBox.Text);
}

 

Posted on Monday, May 17, 2010 5:55 AM | Back to top


Comments on this post: Preventing a User From Having Multiple Concurrent Sessions

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Nice post helps me a lot!! Thanks for sharing this!!
Left by Andre Luiz on May 17, 2010 6:04 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Invalid value for 'encryptedTicket' parameter.
Authenticationcookie.value =null
Left by Kaleem on Jun 02, 2010 10:00 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Are you using Forms Authentication and calling the code in the LoggedIn event?
Left by Phil on Jun 03, 2010 6:50 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Thanks U Posting
But ..ERROR.

Invalid value for 'encryptedTicket' parameter.
Authenticationcookie.value =null
Left by mobilepro on Aug 02, 2010 9:08 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Worked beautifully, thank you kindly for sharing.
Left by Morten on Oct 27, 2010 5:05 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
I'm getting....

CS0122: 'SingleSessionPreparation' is inaccessible due to its protection level

What would be left out to cause this?
Left by Andrew on Jan 08, 2011 12:36 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
LoggedIn event is not firing
Left by Mushtaq on Mar 16, 2011 7:01 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Plz help me .

multiple login not alloaw oa a single machine.
plz help me
mohit.chauhan8@gmail.com
Left by Mohit Chauhan on Apr 02, 2011 7:31 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
instead of userName pass the ip address,

FormsAuthentication.Authenticate(Request.ServerVariables("REMOTE_ADDR"))

singleSessionPreparation.CreateAndStoreSessionToken(Request.ServerVariables("REMOTE_ADDR"))

on class SingleSessionEnforcement
replace Membership.GetUser for your own method to retrive de guid.id

I hope this help.
Left by AnaO on Apr 27, 2011 3:51 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Thanks a lot!!, i used that in vbnet without membership and only one user for ip address.
Also implement a cache for httpContext to save total of open windows with diferent sesion for ip address, that cache is only for ips open more than one time. I do that to reduce the calling of database only when are more open windows or tabs in the machine. If the user open a tab and this tab have same session i block de user for enter the site, that control is on login page.

Private Sub OnPostAuthenticate(ByVal sender As Object, ByVal e As EventArgs)
Dim sessionToken As String
Dim httpApplication As HttpApplication = CType(sender, HttpApplication)
Dim httpContext As HttpContext = httpApplication.Context
Dim totalCachePorIp As Integer = 1 'total de caches por nombre de ip, indica cantidad de ventas de ie abiertas
If httpContext.Current.Cache.Get(httpContext.Current.Request.ServerVariables("REMOTE_ADDR")) Is Nothing Then
Exit Sub
Else
totalCachePorIp = httpContext.Current.Cache.Get(httpContext.Current.Request.ServerVariables("REMOTE_ADDR"))
End If
'// Check user's session token
If httpContext.User.Identity.IsAuthenticated Then
Dim authenticationTicket As FormsAuthenticationTicket = CType(httpContext.User.Identity, FormsIdentity).Ticket
If authenticationTicket.UserData <> "" Then
sessionToken = New String(authenticationTicket.UserData)
Else
' // No authentication ticket found so logout this user
' // Should never hit this code
FormsAuthentication.SignOut()
FormsAuthentication.RedirectToLoginPage()
Exit Sub
End If
Dim clSdata As New clsDataSource.FastConnection(httpContext.Server.MapPath(System.Configuration.ConfigurationManager.AppSettings("ConnectionOtorgamiento")))
Dim storedToken As String = clSdata.RetornaUnValor("select Sessionid from Db_Log_Ctrl_Sesion where Id_Maquina = '" & httpContext.Current.Request.ServerVariables("REMOTE_ADDR") & "' and Activa = 1")
'// May want to add a conditional here so we only check
'// if the user needs to be checked. For instance, your business
'// rules for the application may state that users in the Admin
'// role are allowed to have multiple sessions
If sessionToken <> storedToken Then
' // Stored session does not match one in authentication
' // ticket so logout the user
totalCachePorIp += 1
If totalCachePorIp = 1 Then
System.Web.HttpContext.Current.Cache.Remove(httpContext.Current.Request.ServerVariables("REMOTE_ADDR"))
Else
System.Web.HttpContext.Current.Cache.Insert(httpContext.Current.Request.ServerVariables("REMOTE_ADDR"), totalCachePorIp - 1, Nothing, DateTime.MaxValue, TimeSpan.FromMinutes(20))
End If
FormsAuthentication.SignOut()
FormsAuthentication.RedirectToLoginPage()
End If
End If
End Sub

I would like to post my code and write in spanish, of course if the author let me doit.

Left by AnaO on Apr 27, 2011 8:42 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Worked for me, thanks. I received the same error as Andrews and had to change my class from internal to public, internal static void to public static void. Not exactly sure of the ramifications. Great solution for the last user to login wins scenario, still looking for one to prevent the second attempt from gaining access.
Left by LostArk on Jun 16, 2011 6:34 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
It Will Give Error:- encryptedTicket is NULL
Left by sandip on Mar 05, 2012 12:54 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Thanks for sharing this articale But i have one problem am not using form auth method to login
the system throw exception when i loing here is my loign button click pls help:

if (DBAccess.IsValidUser(txtUserName.Text, txtPassword.Text, chkStatus.Checked.ToString()))
{




SingleSessionPreparation.CreateAndStoreSessionToken(txtUserName.Text);


Session["UserName"] = txtUserName.Text.Trim();
//Session["userPass"] = txtPassword.Text;

Session["True"] = chkStatus.Checked.ToString();


if (chkStatus.Checked)
{
Response.Redirect("UserArea/UserArea.aspx");
}
else
Response.Redirect("client_login.aspx");
}
else
{
lblError.Visible = true;
lblError.Text = "Invalid UserName / Password";
lblError.BackColor = Color.Red;
lblError.ForeColor = Color.White;
}
Left by Noor on Mar 18, 2012 12:49 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Sir, here is my error details for the above code when i try to loing
encryptedTicket parameter
Left by Noor on Mar 18, 2012 12:51 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Invalid value for 'encryptedTicket' parameter.
How to solve this issue..?
Left by LIN on Aug 16, 2012 12:54 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Trying to call singlesessionenforcement at global.asax as below

void Application_OnPostAuthenticateRequest(Object sender, EventArgs e)
{
SingleSessionEnforcement ForceSingelSession = new SingleSessionEnforcement();
ForceSingelSession.OnPostAuthenticate(sender, e);
}


Is it the right way ? Please help.
Left by siba on Apr 29, 2013 8:29 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Invalid value for 'encryptedTicket' parameter.
How to solve this issue..?
Left by Nikunj Balar on Sep 03, 2013 5:30 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Invalid value for 'encryptedTicket' parameter.
How to solve this ??? please reply me fast
Left by raju on Sep 19, 2013 12:03 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
To fix error: Invalid value for 'encryptedTicket' parameter.

Add:

System.Web.Security.FormsAuthentication.SetAuthCookie(UserName, True)


Before:

// Get authentication cookie and ticket
HttpCookie authenticationCookie =
pageResponse.Cookies[FormsAuthentication.FormsCookieName];
FormsAuthenticationTicket authenticationTicket =
FormsAuthentication.Decrypt(authenticationCookie.Value);


Left by El Bananero on Mar 21, 2014 7:21 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Great job here!
Left by David Jiboye on Mar 26, 2014 1:16 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
getting Invalid value for 'encryptedTicket' parameter error even adding
System.Web.Security.FormsAuthentication.SetAuthCookie(UserName, True)
Left by s on Apr 01, 2014 2:24 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.

Parser Error Message: Could not load type 'SingleSessionEnforcement'. (C:\Documents and Settings\palak\my documents\visual studio 2010\Projects\PalakDemo\PalakDemo\web.config line 63)

Source Error:


Line 61:
Line 62: <httpModules>
Line 63: <add name="SingleSessionEnforcement" type="SingleSessionEnforcement" />
Line 64: </httpModules>
Line 65: </system.web>




this error occured so what can i do please help me.
Left by Palak on Apr 04, 2014 6:49 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Hello sir

I am getting this error "Could not load type 'Single Session Enforcement'."
Left by sunil on May 28, 2014 5:23 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
How to remove sessionID when session expires.
Left by Khang Vi on Aug 05, 2014 4:09 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
MembershipUser currentUser =Membership.GetUser(userName);
currentUser.Comment = sessionToken.ToString();

In this line currentUser is always returns null value...
What may be the reason???
Left by Athul on Jun 02, 2015 7:32 AM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Could not load type 'SingleSessionEnforcement'.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Web.HttpException: Could not load type 'SingleSessionEnforcement'.
Left by Syed on Jun 06, 2015 4:53 PM

# re: Preventing a User From Having Multiple Concurrent Sessions
Requesting Gravatar...
Great work , just copy pasted and it works:)
Left by Ehsan on Jun 11, 2015 6:08 AM

Your comment:
 (will show your gravatar)


Copyright © Frez | Powered by: GeeksWithBlogs.net | Join free