Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Retrieve a List of Authenticated Users using ASP.NET, AJAX, and FormsAuthentication

0.00/5 (No votes)
26 Feb 2009 1  
This article explains how to retrieve and display a list of all the authenticated users on an ASP.NET WebSite

Introduction

As a Web developer working in a small company, I often encountered the same issue: every time I wanted to release a new version of a Web application, I had to call / send an e-mail to every lovely user in order to ask them to leave the application. The fact is that most of them weren't using it at the moment I was planning to launch the update. At the end, everyone was angry because of the time they lost answering my phone calls, especially those who were in the middle of an exciting Freecell game, or watching their friends' pictures on Facebook.

I decided to put an end to it by developing a small Web page that would tell me who is online. The principle is really simple: on the first load of every page in my WebSite, I check if the current user is authenticated. If this is the case, I check if he's already part of the authenticated users' list (a generic List of string objects). If he's not in the list, I add him into it. When the user navigates to another page, or closes his browser, I remove him from the authenticated users' list. This is done using a little bit of JavaScript with a call to a WebService.

Background

If you're not familiar with FormsAuthentication, you may want to take a look at this article:

This blog explains how to use the ASP.NET Cache and Session State storage:

Finally, this page from MSDN describes and shows a sample of the JavaScript "onbeforeunload" event:

Using the Code

Your Web.config file should include the following sections:

<configuration>
  <system.web>

    <!-- Use FormsAuthentication in the WebSite -->
    <authentication mode="Forms">
      <forms loginUrl="~/Login.aspx" defaultUrl="~/Default.aspx" />
    </authentication>

    <!-- References to the Ajax assemblies -->
        <compilation debug="true">
      <assemblies>
        <add assembly="System.Web.Extensions, Version=1.0.61025.0, 
		Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Design, Version=2.0.0.0, Culture=neutral, 
		PublicKeyToken=B03F5F7F11D50A3A" />
        <add assembly="System.Web.Extensions.Design, Version=1.0.61025.0, 
		Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      </assemblies>
    </compilation>

    <!-- This is needed to make the WebService work properly -->
        <httpHandlers>
      <remove verb="*" path="*.asmx"/>
      <add verb="*" path="*.asmx" validate="false" 
	type="System.Web.Script.Services.ScriptHandlerFactory, 
	System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, 
	PublicKeyToken=31bf3856ad364e35" />
      <add verb="*" path="*_AppService.axd" validate="false" 
	type="System.Web.Script.Services.ScriptHandlerFactory, 
	System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, 
	PublicKeyToken=31bf3856ad364e35" />
      <add verb="GET,HEAD" path="ScriptResource.axd" 
	type="System.Web.Handlers.ScriptResourceHandler, 
	System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, 
	PublicKeyToken=31bf3856ad364e35" validate="false" />
    </httpHandlers>

    <!-- This is needed to make Ajax work properly -->
        <httpModules>
      <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, 
	System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, 
	PublicKeyToken=31bf3856ad364e35" />
    </httpModules>

    <!-- This is optional, but if you don't register the 
	System.Web.Extensions library in the Web.config, 
	you'll have to do it in the MasterPage-->
    <pages>
      <controls>
        <add tagPrefix="asp" namespace="System.Web.UI" 
		assembly="System.Web.Extensions, Version=1.0.61025.0, 
		Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
      </controls>
    </pages>
  </system.web>

</configuration>

Login.aspx.cs

This is the code-behind for the login page. Nothing fancy here, just a few lines of code that check if the specified username and password are valid. This is the place to put your authentication logic, but keep in mind that the username is the one that will be displayed in the "authenticated users" page. So you probably don't want to use an id here. If you really can't use the username of the login page (for example, if this username is not relevant), you should store the "real" UserName in a Session variable during your authentication process, then use something like Session["UserName"].ToString() to retrieve it.

/// <summary>
/// Triggers when the page loads
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{
  AuthenticatedUsersLogin.Focus();
}

/// <summary>
/// Triggers when the user logs in
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void AuthenticatedUsersLogin_Authenticate(object sender,
AuthenticateEventArgs e)
{
  //Replace the following code by your own authentication logic
  string userName = AuthenticatedUsersLogin.UserName;
  string password = AuthenticatedUsersLogin.Password;
  if (userName.Length > 0 && password.Length > 0)
	FormsAuthentication.RedirectFromLoginPage(userName, true);
}

BasePage.aspx.cs

All your WebPages must inherit from this class (except for the login page). It stores the authenticated users name in a string List.

/// <summary>
/// Stores the authenticated users in a string List
/// </summary>
public class BasePage : Page
{
    /// <summary>
    /// Triggers every time a page loads
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Page_Load(object sender, EventArgs e)
    {
    //If the page loads for the first time and if the current user is authenticated,
    //we add him to the authenticated users' list
        if (!IsPostBack &&  HttpContext.Current.Request.IsAuthenticated)
	   RegisterUser();
    }

    /// <summary>
    /// Adds the current user's name to the authenticated users' list
    /// </summary>
    protected void RegisterUser()
    {
    //We retrieve the list of the authenticated users
        List<string> authenticatedUsers = AuthenticatedUsersList;

     if (!authenticatedUsers.Contains(UserName))
       {
        //If the current user is not already in the authenticated users' list,
        //we add him to the list
        authenticatedUsers.Add(UserName);
        //Then we save the authenticated users' list
        AuthenticatedUsersList = authenticatedUsers;
    }
    }

    /// <summary>
    /// Gets or sets a list of all the authenticated users
    /// </summary>
    protected List<string> AuthenticatedUsersList
    {
        get
        {
            //If the Cache is empty, we add an empty List in it
            if (Cache["AuthenticatedUsers"] == null)
                Cache["AuthenticatedUsers"] = new List<string>();
            return (List<string>)Cache["AuthenticatedUsers"];
        }
        set { Cache["AuthenticatedUsers"] = value; }
    }

    /// <summary>
    /// Gets the current user's name
    /// </summary>
    protected string UserName
    {
        get
        {
        //If you don't want to use the Identity.Name object, you should store the
        // UserName in a Session variable in the Login page.
        //Then use something like Session["UserName"].ToString() to retrieve it.
            return HttpContext.Current.Request.IsAuthenticated
                ? HttpContext.Current.User.Identity.Name
                : string.Empty;
        }
    }
}

AuthenticatedUsersPage.aspx.cs

This is the page that displays the list of the authenticated users. You just need a label and you're done. Of course you should add some links to the other pages, otherwise you won't be able to go back to your WebSite !

    /// <summary>
    /// Triggers when the page loads
    /// </summary>
    /// <param name="e"></param>
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        //If this is the first time that the page loads, when call the method that
        //will display the authenticated users
        if (!IsPostBack) DisplayAuthenticatedUsers();
    }

    /// <summary>
    /// Displays the authenticated users
    /// </summary>
    private void DisplayAuthenticatedUsers()
    {
        List<string> authenticatedUsersList = AuthenticatedUsersList;
        if (authenticatedUsersList.Count > 0)
        {
            foreach (string user in authenticatedUsersList)
            {
                lblAuthenticatedUsers.Text += string.Format("<li>{0}</li><br />", user);
            }
        }
        else lblAuthenticatedUsers.Text = "No one is authenticated";
    }

AuthenticatedUsersMasterPage.Master

Placing the following code in a MasterPage saves us from the pain of adding it in each of the WebSite's pages. Basically I first added a reference to my WebService between the <asp:ScriptReference> tags. Then I added my JavaScript code that will be triggered during the window.onbeforeunload event. This will call the WebService's UnregisterUser() method, used to remove the current user from the authenticated users' list.

<form id="form1" runat="server">
        <asp:ScriptManager ID="AuthenticatedUsersScriptManager" runat="server">
            <Services>
                <asp:ServiceReference Path="~/AuthenticatedUsersWebService.asmx" />
            </Services>
        </asp:ScriptManager>

        <script type="text/javascript" language="javascript">
            //Occurs when the user leaves the current page, or closes the browser
            window.onbeforeunload = RemoveUser;

            function RemoveUser()
            {
                //We call the UnregisterUser() method located in the WebService
                result = AuthenticatedUsersWebService.UnregisterUser(OnComplete,
				OnTimeOut, OnError);
            }
            function OnComplete(arg) { }
            function OnTimeOut(arg) { }
            function OnError(arg) { }
        </script>

        <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server" />
    </form>

AuthenticatedUsersWebService.asmx

This is the final step: create a WebService that will be called from the JavaScript code, in order to remove the current user from the authenticated users' list. Don't forget to add the WebMethod(EnableSession = true) attribute to the UnregisterUser() method, otherwise it won't be able to reach the Session object.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService()]
public class AuthenticatedUsersWebService : WebService
{
    /// <summary>
    /// Gets the System.Web.Caching.Cache object for the current application domain
    /// </summary>
    private Cache cache = HttpContext.Current.Cache;

    /// <summary>
    /// Removes the current user from the authenticated users' list
    /// </summary>
    [WebMethod(EnableSession = true)]
    public void UnregisterUser()
    {
		string userName = Session["UserName"] != null ? 
			Session["UserName"].ToString() :   string.Empty;
		List<string> authenticatedUsers =   
				cache["AuthenticatedUsers"] != null 
			? (List<string> )cache["AuthenticatedUsers"]
			: new List<string>();
		if (authenticatedUsers.Contains(userName))   
				authenticatedUsers.Remove(userName);
		cache["AuthenticatedUsers"] =   authenticatedUsers;
    }
}

History

  • [02.09.2009] First version
  • [02.19.2009] Fixed typo in AuthenticatedUsersMasterPage.Master
  • [02.25.2009] Fixed a bug in the authorization logic

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here