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.
October 30, 2009
Q: I want to add extra data to the forms authenticationcookie issued by FormsAuthentication.RedirectFromLoginPage. Is this possible,and if so, how? Do I need to derive fromSystem.Web.Security.FormsAuthenticationTicket?
A: You unfortunately can't derive fromFormsAuthenticationTicket because it's a sealed class. But you can useFormsAuthenticationTicket's UserData property to encode custom information inauthentication cookies. An authentication cookie is nothing more than an HTTPcookie carrying an instance of FormsAuthenticationTicket as its payload.
The secret to storing user-defined data in formsauthentication cookies is to modify the login page so that instead of issuing anormal authentication ticket, it issues one whose UserData property holds theextra 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 ofFormsAuthenticationTicket containing the user's login name and otherinformation normally found in an authentication ticket. It also initializesUserData with the string "Hello, world." Because UserData is a read-onlyproperty, setting it in FormsAuthenticationTicket's constructor is the only wayto assign it a value.
The remaining statements transform the formsauthentication ticket into a forms authentication cookie. First,FormsAuthentication.Encrypt is called to convert the ticket into a string aswell as possibly hashing and encrypting it. (Encrypt doesn't necessarily do anyhashing or encryption. Whether it hashes the ticket to prevent tampering orencrypts the ticket's content to protect it from prying eyes - or both - iscontrolled by the value of the protection attribute accompanying the element in the section of web.config ormachine.config.) Next, the string returned by FormsAuthentication.Encrypt isplaced inside a cookie, and the cookie is added to the outgoing HTTP response.Finally, Response.Redirect sends the user to the page they sought originallywhen ASP.NET redirected them to the login page. The URL of the original page comesfrom FormsAuthentication.GetRedirectUrl.
Once you've customized a forms authentication cookie withuser data, you need to know how to read it back, too. The following code, whichcould appear in Global.asax or in an ASPX file, extracts the authenticationticket, 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, whoseTicket property (of type FormsAuthenticationTicket) exposes the authenticationticket. Once the ticket is in hand, extracting the user-defined data containedwithin it is a simple matter of reading the ticket's UserData property.
A common use for UserData is to store a list of roles towhich a user belongs when exercising role-based security. You can storeanything you want in UserData, however, provided you can store it as a string.Convert.ToBase64String is a convenient mechanism for converting arbitrarybinary data into text; use Convert.FromBase64String to reverse the process andrecover the original data. Don't forget that the size restrictions browsersimpose on cookie lengths (typically 4K) in turn limit the amount of data youcan encode in UserData. Also, large UserData values decrease effectiveconnection bandwidths by increasing the amount of information transmitted ineach HTTP request.
Q: I'm using a tip from your last column to scroll aDataGrid inside a element. My DataGrid contains a column of Edit buttons, however, and when Iclick on one of those buttons, the DataGrid scrolls back to the top. Can Iprevent the DataGrid from losing its scroll position? I tried enabling smartnavigation with an @ Page directive, but that didn't solve the problem.
A: The fundamental problem is that clicking on one of theDataGrid'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 scrollposition 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");}%> <script language="C#" runat="server">void Page_Load (Object sender, EventArgs e){ if (!IsPostBack) { SqlConnection connection = new SqlConnection ("server=localhost;database=northwind;uid=sa"); try { connection.Open (); SqlCommand command = new SqlCommand ("select productname from products", connection); SqlDataReader reader = command.ExecuteReader (); MyDataGrid.DataSource = reader; MyDataGrid.DataBind (); } finally { connection.Close (); } }}
Figure 1. This page scrolls an editable DataGrid in aregion defined by a
element and preserves the scroll position whenyou click on an Edit button. Note that the onscroll attribute here is brokeninto two lines to fit the width of this magazine column - you must not break itin an actual ASPX file.
The sample I provided in my previous column uses aDataGrid to display the products listed in the "Products" table of SQL Server'sNorthwind database. The DataGrid includes a column of Edit buttons, and itscrolls inside a region defined by a
element (see Figure 2). Notethe onscroll attribute attached to the tag. Scrolling writes thelatest scroll position to a hidden tag named __DIVPOS, causingthe final pre-postback scroll position to be included in the form's postbackdata.
Figure 2. This DataGrid maintains its scroll position when an Editbutton 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 - thisserver-side script outputs client-side script that restores the pre-postbackposition of the
. The result? The DataGrid (actually, the element) retains its scroll position, enhancing the user experienceand helping abstract away those pesky postbacks.
The code in this article isavailable 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 aquestion for this column? Submit queries to [email protected].
Tell us what you think! Please send any comments aboutthis article to [email protected] include the article title and author.
About the Author
You May Also Like