Introduction
Single sign-on using ASP.NET Forms authentication is not a new topic. If you Google it, you’ll find a fair amount of articles talking about it. However, it does not appear to be easy enough to get a clear picture when you try to implement it. The concepts and implementation details are often scattered around, which requires adequate efforts to put pieces together. In this article, I’ll try to summarize my understandings through literature study as well as my own practice, and hope to make it easy for you if you ever need to implement it.
Cookie and Domain Level
Cookies are restricted to a certain domain level for security reasons. Based on FRC 2901 and 2965, cookies can’t be set to a top-level domain such as .com, .org, .gov, etc. The minimum domain level required is the second. For example, mysite.com (second level) and public.mysite.com (third level) are both fine to set a cookie. The third level domain and beyond are often called “sub domains”. www.myiste.com, public.mysite.com, and art.mysite.com are all sub domains of the second level domain mysite.com. If a domain name ends with a two letter country code, a minimum of third level domain will be required. A cookie set to a second-level domain is visible at all its third-level domains. However, a cookie set to a third-level domain is not visible at its parent second level domain and at other sub domains. If no domain name is specified when a cookie is written, the cookie’s domain attribute defaults to the domain name in which the application resides. For example, if a cookie is set by the page http://public.mysite.com/myapp/defauylt.aspx without specifying a domain attribute, the domain of the cookie will default to public.mysite.com. As a result, the cookie will only be visible at this particular sub domain - public.mysite.com. It is not visible at another sub domain like art.mysite.com or at its second level domain mysite.com.
Forms Authentication Ticket and Cookie
ASP.NET keeps track of a user through a Forms authentication ticket which is a securely encrypted text string. This ticket is passed back and forth between a web server and a client browser to persist the user authentication status. During login, the user’s credentials are first verified against a data store, and then the FormsAuthentication.SetAuthCookie
method (or other methods) is called to log the user into the application. This method call generates a Forms authentication ticket and at the same time creates a Forms authentication cookie to hold the ticket. In other words, upon successful authentication, ASP.NET creates a Forms authentication cookie that has the value of the Forms authentication ticket. Both the ticket and the cookie are securely encrypted by the Framework.
Please note that the authentication ticket is generally saved into a cookie unless the ASP.NET application is specified as “cookieless”, in which case, the ticket will be appended to a URL by the Framework. This is beyond the scope of this article.
Single Sign-on Within a Sub Domain
Since the Forms authentication is done through a Forms authentication ticket, if all applications share one authentication ticket, a user will only be required to log in once, thus, single sign-on. Let’s say we have two applications with these URLs:
- http://public.mysite.com/app1/default1.aspx
- http://public.mysite.com/app2/default2.aspx
As you can see, they are basically two virtual directories under the same sub domain - public.mysite.com. A cookie written by app1 will be visible by app2, and vice versa. However, this does not mean when a user logs into app1, he is automatically logged into app2. The reason is that each ASP.NET application, by default, uses its own encryption keys to create a Forms authentication ticket and cookie. Therefore, the cookie written by app1 can’t be successfully read by app2, even though it is visible to app2.
How can we make the two applications recognize each other’s authentication ticket and cookie? In fact, it is pretty simple in ASP.NET. The only thing that needs to be done is to change the settings in the <machineKey>
element of the web.config file. Here are the default settings for machinKey
:
<machineKey
validationKey="AutoGenerate,IsolateApps"
decryptionKey="AutoGenerate,IsolateApps"
validation="SHA1"
decryption="Auto"
/>
With these settings, the .NET framework uses the automatically generated validationKey
and decrytionKey
to create an authentication ticket and cookie. Each application uses different keys. To make multiple applications to share the same authentication ticket and cookie, we just need to set the validationKey
and decrytionKey
in all the applications to the same values. Similar to the following:
<machineKey validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D7"
decryptionKey="ABAA84D7EC4BB56D75D217CECFFB9628809BDB8BF91CFCD64568A145BE59719F"
validation="SHA1"
decryption="AES"
/>
To generate the keys, the RNGCryptoSErviceProvider
class in the System.Security.Cryptography
namespace is utilized, as shown in the following code sample:
public String CreateKey(int numBytes)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[numBytes];
rng.GetBytes(buff);
return BytesToHexString(buff);
}
private String BytesToHexString(byte[] bytes)
{
StringBuilder hexString = new StringBuilder(64);
for (int counter = 0; counter < bytes.Length; counter++)
{
hexString.Append(String.Format("{0:X2}", bytes[counter]));
}
return hexString.ToString();
}
Single Sign-on Within a Second Level Domain
It is common to see a number of different sub domains from the same organization due to marketing or administrative needs, just like the following samples:
- http://public.mysite.com/app1/default.aspx
- http://art.mysite.com/app2/default.aspx
- http://www.mysite.com/app3/default.aspx
They are all third-level domains (sub domains) under the same second level domain mysite.com. Suppose all applications have the machine key properly set up for single sign-on, as discussed in the last section. However, you’ll find out that single sign-on does not work. A user who logs into app1 at public.mysite.com will still be required to log in at art.mysite.com and at www.mysite.com. This is because the authentication cookie created in app1 during login has, as a default, the domain attribute of public.mysite.com, which makes it not visible at both art.mysite.com and www.mysite.com.
To make the authentication cookie visible at all other sub domains, we need to modify the cookie’s domain attribute to point to its second-level domain – mysite.com. The following code does just that.
This modification makes the single sign-on within the second level domain, mysite.com, a reality.
FormsAuthentication.SetAuthCookie(txtUserName.Text, false);
System.Web.HttpCookie MyCookie =
System.Web.Security.FormsAuthentication.GetAuthCookie(User.Identity.Name.ToString(),
false);
MyCookie.Domain = “mysite.com”;
Response.AppendCookie(MyCookie);
Run Sample Applications
The sample application download (the link at the top of the article) includes two applications: CookieLevel2Domain and CookieSubDomain. As the names implies, the former creates an authentication cookie to a second level domain while the latter to a third level domain. The applications were created in Visual Studio 2008 and Framework 3.5. Since very little coding is involved, you can easily recreate the applications in Visual Studio 2005, if needed. The default.aspx page simulates the authentication process while the GenerateMahineKey.aspx page provides a utility to create the validationKey
and decrytionKey
that are needed in web.config.
To see how the single sign-on works in relation to the domain level, you need to install each application on to a different sub domain, like:
- http://public.mysite.com/CookieLevel2Domain/ProtectedPage.aspx
- http://art.mysite.com/CookieSubDomain/ProtectedPage.aspx
Please change the appSetting
key - Level2DomainName
in the web.config of the first application to the actual second level domain name on your server. After the two applications are set up, browse to the ProtectedPage.aspx in one of them to start an authentication process.
You’ll see that when you log in through the first app, CookieLevel2Domain, you are not required to log in again in the second app, CookieSubDomain. This is because the domain attribute of the Forms authentication cookie is modified to point to the second level domain – mysite.com. However, if you log in through the second app, you’ll be asked to log in again when accessing the first app, since the authentication cookie defaults to the sub domain art.mysite.com and is not visible at the sub domain public.mysite.com.
If you test the two applications on your local development machine, you’ll not have the same experience described above. This is because both applications run at the same domain localhost.
Single Sign-on Cross Domains and Platforms
Single sign-on discussed above is limited to ASP.NET applications within a second level domain. Let’s look at the two ASP.NET applications shown below:
- http://public.mysite.com/app1/default.aspx
- http://art.yoursite.com/app2/default.aspx
The above applications reside at two different second level domains, mysite.com and yoursite.com, respectively. The single sign-on implemented in the above section will not work simply because of the invisibility of the Forms authentication cookie between the two domains.
Furthermore, if two applications run on different platforms, like PHP, CFM, JSP etc., as shown below:
- http://public.mysite.com/app1/default.cfm (Coldfusion app)
- http://art.yoursite.com/app2/default.aspx (ASP.NET app)
the single sign-on solution discussed above becomes not applicable.
To achieve single sign-on under these conditions, we may, sometimes, need to look into commercial solutions, for example, the Microsoft SharePoint Server, Microsoft Passport (is this now the Windows Live?), and others. They can do a good job, but also have some drawbacks, such as cost, complexity, privacy issues, etc. Therefore, we sometimes need to come up with a solution of our own.
I presented an article titled Cross Site Authentication and Data Transfer at aspalliance.com. This article discusses the methodology and implementation of single sign-on cross domains or platforms using ASP.NET Forms authentication in conjunction with ASP.NET web services. It also discusses the methodology of transferring user data to an application located at a different domain or platform during the authentication process. Although being highly simplified, it provides a good starting point for developing your own single sign-on authentication solutions.
References