hits counter

Coupling ASP.NET Session State With Forms Authentication

Today I was talking with João about a way to couple the lifetime of the ASP.NET session state with the lifetime of Forms Authentication ticket.

My idea was to store the session ID in the UserData property of the forms authentication ticket upon logon and retrieve it with a custom session ID manager.

The login code would be something like this:

protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
{
bool isPersistent = this.Login1.RememberMeSet;
string username = this.Login1.UserName;
var ticket = new FormsAuthenticationTicket(
0,
username,
DateTime.Now,
DateTime.Now.AddMinutes(2),
isPersistent,
Guid.NewGuid().ToString("N"));

<span style="color: green;">// Encrypt the ticket.
</span><span style="color: blue;">var </span>encryptedTicket = <span style="color: rgb(43, 145, 175);">FormsAuthentication</span>.Encrypt(ticket);

<span style="color: green;">// Create the cookie.
</span><span style="color: blue;">this</span>.Response.Cookies.Add(<span style="color: blue;">new </span><span style="color: rgb(43, 145, 175);">HttpCookie</span>(<span style="color: rgb(43, 145, 175);">FormsAuthentication</span>.FormsCookieName, encryptedTicket));

<span style="color: green;">// Redirect back to original URL.
</span><span style="color: blue;">this</span>.Response.Redirect(<span style="color: rgb(43, 145, 175);">FormsAuthentication</span>.GetRedirectUrl(username, isPersistent));

}

For the purpose of this test I am using a Guid as the session ID.

The session ID manager will return this session ID when queried by the session state HTTP module:

public class SessionIdManager : global::System.Web.SessionState.ISessionIDManager
{
    #region ISessionIDManager Members

    public string CreateSessionID(HttpContext context)
    {
        return GetDummySessionIdOrRedirectToLoginPage(context);
    }

    public string GetSessionID(HttpContext context)
    {
        return GetSessionIdFromFormsIdentity(context);
    }

    public void Initialize()
    {
    }

    public bool InitializeRequest(HttpContext context, bool suppressAutoDetectRedirect, out bool supportSessionIDReissue)
    {
        supportSessionIDReissue = false;
        return GetSessionIdFromFormsIdentity(context) == null;
    }

    public void RemoveSessionID(HttpContext context)
    {
    }

    public void SaveSessionID(HttpContext context, string id, out bool redirected, out bool cookieAdded)
    {
        redirected = false;
        cookieAdded = false;
    }

    public bool Validate(string id)
    {
        return true;
    }

    #endregion

    private static string GetSessionIdFromFormsIdentity(HttpContext context)
    {
        var identity = context.User != null ? context.User.Identity as FormsIdentity : null;

        if ((identity == null) || (identity.Ticket == null) || string.IsNullOrEmpty(identity.Ticket.UserData))
        {
            return GetDummySessionIdOrRedirectToLoginPage(context);
        }
        else
        {
            return identity.Ticket.UserData;
        }
    }

    private static string GetDummySessionIdOrRedirectToLoginPage(HttpContext context)
    {
        if (context.Request.CurrentExecutionFilePath.Equals(FormsAuthentication.DefaultUrl, StringComparison.OrdinalIgnoreCase)
                        || context.Request.CurrentExecutionFilePath.Equals(FormsAuthentication.LoginUrl, StringComparison.OrdinalIgnoreCase))
        {
            return Guid.NewGuid().ToString("N");
        }
        else
        {
            FormsAuthentication.RedirectToLoginPage();
            return null;
        }
    }
}

NOTE: Although this might work, it’s just an intellectual exercise and wasn’t fully tested.

No Comments