Forms authentication and role-based security

In a recent post about Forms Authentication and Roles that showed the way to improve performance when you have to fetch the roles info on each page hit, some people note that the proposed solution suffered from the cookie size limit and the approach was the same as stated by an MSDN samples (here and here). Let’s make a further insight of these two observations. The cookie limit of 4K is certainly an issue here when you have userData of size greater than 1K (actually 1200 bytes) because the FormsAuthentication API performs several operations that will expand your data size before it gets ready for packing inside a cookie.This operations like encoding in Hexa (size x 2) and the addition of some random entropy material, expands the userData size in a non linear way that it quickly reach this 4K limit. Unfortunately you won't notice what is really happening hence no exception is thrown. The only strange behavior is that Forms seems not to be working as expected. So beware of you userData size! (up to 1200 bytes length) (I know, it should have been documented).With that said, you still have room to store up to 50 roles of 23 bytes length each (character separator included). That’s a pretty good number of roles (IMO, if you have near that number of roles, perhaps you should review you authorization design model). If your needs of role storage exceeds this size, you should better use the traditional approach of an external DB store (mainly for performance and scalability reasons) and place on the “userData” parameter, some kind of ID that will give you a direct access to your role info. Another scenario might be a hybrid approach where you have some users that has just a few roles (under 50) and others users that has more than this limit (more than 50 roles!!, mmm… you should think about it). Here you might use a combination of the two models presented above.  Let’s see now a brief comparison between the MSDN samples and the suggested approach. The authentication and roles fetching are almost the same in both techniques. However, the main differences are in the cookie and the FormsAuthenticationTicket creation.Here we have this sample: (note: This code will be usually placed after the authentication and roles fetching code section.          We assume that the variable ‘roles’ is a string array with all the user roles) 
 // Create the authentication ticketFormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(                                    1,                          //version                                    txtUserName.Text,           // user name                                    DateTime.Now,               // creation                                    DateTime.Now.AddMinutes(60),//Expiration                                    false,                      //Persistent                                    String.Join( "|", roles));  // User data // Now encrypt the ticket.string encryptedTicket = FormsAuthentication.Encrypt(authTicket);// Create a cookie and add the encrypted ticket to the cookie as data.HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName,                                                               encryptedTicket); // Add the cookie to the outgoing cookies collection.Response.Cookies.Add(authCookie); // Redirect the user to the originally requested pageResponse.Redirect(FormsAuthentication.GetRedirectUrl(txtUserName.Text, false)); 
 Compare with the proposed approach: 
    // Get the cookie created by the FormsAuthentication API  // Notice that this cookie will have all the attributes according to     // the ones in the config file setting.         HttpCookie cookie = FormsAuthentication.GetAuthCookie( UserId.Text, false );  FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);   // Store roles inside the Forms Ticket with all the attributes aligned with   // the config Forms section.  FormsAuthenticationTicket newticket = new FormsAuthenticationTicket(                                                              ticket.Version,                                                               ticket.Name,                                                                  ticket.IssueDate,                                                             ticket.Expiration,                                                            ticket.IsPersistent,                                                          String.Join( "|", roles),                                                      ticket.CookiePath);             // add the encrypted ticket to the cookie as data.                                           cookie.Value = FormsAuthentication.Encrypt(newticket);  // Update the outgoing cookies collection.  Context.Response.Cookies.Set(cookie);   // Redirect the user to the originally requested page  Response.Redirect( FormsAuthentication.GetRedirectUrl( newticket.Name,                                                     newticket.IsPersistent ) ); 
  The main difference is that our approach takes all the Forms data (cookie and Ticket) from the already created Forms cookie and Forms ticket so these two objects will be already filled with the setting of the Forms config section. This is crucial in order to get an easy configuration management from one place (the web.config file). The MSDN sample hardcode many of these settings so you won’t be able to control the Forms configuration like the proposed solution do.If you have a config section like the one below, with the MSDN sample you will loose many of the configured settings, specially those associated with the cookie (notice the requireSSL attribute ). 
<configuration>      <system.web>     <authentication mode="Forms">            <forms loginUrl="Secure\login.aspx"                  protection="All"                                requireSSL="true"                               timeout="10"                                    name="FormsAuthCookie"                            path="/FormsAuth"                               slidingExpiration="true" />        </authentication>   </system.web>  </configuration>
 So with the proposed solution you will benefit from the performance gains by the roles caching inside the FormsAuthentication cookie (if you hopefully have less than 50 roles) and the configuration management from the web.config file only and not from “magic” values hardcoded inside your authentication method.    

11 Comments

  • I generally use server-side caching techniques for user roles (typically Session if it's InProc, or Cache can be used too) which avoids the performance problems without artificial restrictions on the number of roles and avoids the overhead of a large cookie.

  • You may use whatever server side caching technique you want but you should watch for NLB (farm usage) and scalability issues that might arise. In this scenario, if you hit a server that does not have its roles cached yet, you will need to fetch these roles from their repository and perhaps that involves another authentication but you probably won't have the user credentials available so this may poses a new issue to solve.

  • Good point about needing user credentials to retrieve roles in some scenarios: if this is your situation then a cookie is probably the only viable solution. Apart from the issues you mention, server-side caching means you would need to be able to regenerate the data following e.g. session timeout or cache expiry.



    But in my case I've done several apps which use a database for authentication and the app can easily retrieve the list of roles without needing credentials (using the user name from the FormsAuthentication cookie as a key). The cost of retrieving roles is enough to be worth avoiding on every page hit, but not so high that the web farm scenario you mention is an issue.



    When deciding to cache roles or any other data, the same tradeoffs between cookie, session (InProc, StateServer or SqlServer), cache, database, no caching apply. The important thing is to choose the best caching technique based on your target application and environment.

  • Forms auth is going to use a cookie anyway, so you're avoiding nothing by trying to manage the roles on the server or look them up in the database every hit. All this approach does is piggybacks the user roles onto that cookie. This approach is great!

  • Is there any possibility to grant role based page access or directory access in forms authentication?

  • hiiiiiii
    i'm 0 experience with web sites development but now i have to implement and develop forms authentication with role-based secuiry ... please all help me

    enji_ma@hotmail.com

  • Hi, a solution that I use to cope with the cookie size limitations is to handle roles in a bitwise manner (1,2,4,8). So the whole bunch of roles a user is attached to, is just an integer number. I'm using sql server and instead of using a table that holds user roles, the roles of a user are stored in a column in the user table row. My roles are predefined and role creation, deletion etc is not an option in this app.


  • Depends on how you get the roles you can probably improve it in the following way. If roles string created from the database then in this case you can use no the name but numeric ID of each role. In this case instead of 50 roles of 23 bytes length each you might have up to 300 roles when id is less than 1000-9999 or more if id is less than 999

  • Silly typo: when id is less than 1000 then you might have up to 300 roles (XXX plus "|") if id is between 1000-9999 then up to 240 (also quite many)

  • Interesting post. appreciate your point of view.

Comments have been disabled for this content.