Add Custom Info to Authentication Cookies

Learn how to add data to a forms authentication cookie, as well as how to prevent DataGrid from losing its scroll position.

Q: I want to add extra data to the forms authentication cookie issued by FormsAuthentication.RedirectFromLoginPage. Is this possible, and if so, how? Do I need to derive from System.Web.Security.FormsAuthenticationTicket? 

A: You unfortunately can't derive from FormsAuthenticationTicket because it's a sealed class. But you can use FormsAuthenticationTicket's UserData property to encode custom information in authentication cookies. An authentication cookie is nothing more than an HTTP cookie carrying an instance of FormsAuthenticationTicket as its payload.

The secret to storing user-defined data in forms authentication cookies is to modify the login page so that instead of issuing a normal authentication ticket, it issues one whose UserData property holds the extra data. That means replacing code like this:

FormsAuthentication.RedirectFromLoginPage (UserName,

    false);

with code like this:

FormsAuthenticationTicket ticket =

    new FormsAuthenticationTicket (

        1,                             // Version number

        UserName,                      // Username

        DateTime.Now,                  // Issue date

        DateTime.Now.AddMinutes (30), // Expiration date

        false,                         // Persistent?

        "Hello, world"                 // User data

    );

 

string eticket = FormsAuthentication.Encrypt (ticket);

HttpCookie cookie = new HttpCookie

     (FormsAuthentication.FormsCookieName, eticket);

Response.Cookies.Add (cookie);

 

string url =

    FormsAuthentication.GetRedirectUrl (UserName, false);

Response.Redirect (url);

The first statement creates an instance of FormsAuthenticationTicket containing the user's login name and other information normally found in an authentication ticket. It also initializes UserData with the string "Hello, world." Because UserData is a read-only property, setting it in FormsAuthenticationTicket's constructor is the only way to assign it a value.

The remaining statements transform the forms authentication ticket into a forms authentication cookie. First, FormsAuthentication.Encrypt is called to convert the ticket into a string as well as possibly hashing and encrypting it. (Encrypt doesn't necessarily do any hashing or encryption. Whether it hashes the ticket to prevent tampering or encrypts the ticket's content to protect it from prying eyes - or both - is controlled by the value of the protection attribute accompanying the element in the section of web.config or machine.config.) Next, the string returned by FormsAuthentication.Encrypt is placed inside a cookie, and the cookie is added to the outgoing HTTP response. Finally, Response.Redirect sends the user to the page they sought originally when ASP.NET redirected them to the login page. The URL of the original page comes from FormsAuthentication.GetRedirectUrl.

Once you've customized a forms authentication cookie with user data, you need to know how to read it back, too. The following code, which could appear in Global.asax or in an ASPX file, extracts the authentication ticket, if present, from the current request and reads its UserData property:

if (HttpContext.Current.User != null &&

    HttpContext.Current.User.Identity is FormsIdentity) {

    FormsAuthenticationTicket ticket = ((FormsIdentity)

        HttpContext.Current.User.Identity).Ticket;

    if (ticket != null) {

        string UserData = ticket.UserData;

          .

          .

          .

    }

}

If forms authentication is being used, HttpContext.Current.User.Identity refers to an instance of FormsIdentity, whose Ticket property (of type FormsAuthenticationTicket) exposes the authentication ticket. Once the ticket is in hand, extracting the user-defined data contained within it is a simple matter of reading the ticket's UserData property.

A common use for UserData is to store a list of roles to which a user belongs when exercising role-based security. You can store anything you want in UserData, however, provided you can store it as a string. Convert.ToBase64String is a convenient mechanism for converting arbitrary binary data into text; use Convert.FromBase64String to reverse the process and recover the original data. Don't forget that the size restrictions browsers impose on cookie lengths (typically 4K) in turn limit the amount of data you can encode in UserData. Also, large UserData values decrease effective connection bandwidths by increasing the amount of information transmitted in each HTTP request.

 

Q: I'm using a tip from your last column to scroll a DataGrid inside a

element. My DataGrid contains a column of Edit buttons, however, and when I click on one of those buttons, the DataGrid scrolls back to the top. Can I prevent the DataGrid from losing its scroll position? I tried enabling smart navigation with an @ Page directive, but that didn't solve the problem.

A: The fundamental problem is that clicking on one of the DataGrid's Edit buttons causes a postback to occur and returns brand-new HTML. The solution is to inject some client-side script that tracks the scroll position before a postback occurs and restores it afterward (see Figure 1).

<%@ Page Language="C#" %>

<%@ Import Namespace="System.Data.SqlClient" %>

 



  

    
<% if (Request["__DIVPOS"] != null && Request["__DIVPOS"] != String.Empty) { int pos = Convert.ToInt32 (Request["__DIVPOS"]); Response.Write ("\r\n"); } %>

Figure 1. This page scrolls an editable DataGrid in a region defined by a

element and preserves the scroll position when you click on an Edit button. Note that the onscroll attribute here is broken into two lines to fit the width of this magazine column - you must not break it in an actual ASPX file.

The sample I provided in my previous column uses a DataGrid to display the products listed in the "Products" table of SQL Server's Northwind database. The DataGrid includes a column of Edit buttons, and it scrolls inside a region defined by a

element (see Figure 2). Note the onscroll attribute attached to the
tag. Scrolling writes the latest scroll position to a hidden tag named __DIVPOS, causing the final pre-postback scroll position to be included in the form's postback data.


Figure 2. This DataGrid maintains its scroll position when an Edit button is clicked on, despite the fact that the button click causes a postback.

The other half of the equation is the C# code in the <%...%> block. If the HTTP request includes a non-null __DIVPOS parameter - that is, if the page is being fetched as a result of a postback - this server-side script outputs client-side script that restores the pre-postback position of the

. The result? The DataGrid (actually, the
element) retains its scroll position, enhancing the user experience and helping abstract away those pesky postbacks.

The code in this article is available for download.

 

Jeff Prosise is author of several books, including Programming Microsoft .NET (Microsoft Press). He also is a co-founder of Wintellect (http://www.wintellect.com), a software consulting and education firm that specializes in .NET. Got a question for this column? Submit queries to [email protected].

Tell us what you think! Please send any comments about this article to [email protected]. Please include the article title and author.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish