Single sign on (SSO) for ASP.NET apps in different subdomains

I struggled for many hours to get this working. The linchpin is maintaining sessions across subdomains, which (for Microsoft developers) can only be done if sessions are kept in a database. For this example, I’ll use SQL Server.

Step 0. Prepare your Sql Server database

Make your ASPState database

Step 1. Add new class library to both solutions that you want to link, call it SharedSession.

Inside the library, add a single module called SharedSessionModule.cs:

using System;
using System.Web;
using System.Reflection;
using System.Configuration;

namespace SharedSession
{

	public class SharedSessionModule : IHttpModule
	{
		// Cache settings on memory. 
		protected static string applicationName = ConfigurationManager.AppSettings["ApplicationName"];
		protected static string rootDomain = ConfigurationManager.AppSettings["RootDomain"];

		#region IHttpModule Members
		///  
		/// Initializes a module and prepares it to handle requests. 
		///  
		///  
		/// An System.Web.HttpApplication 
		/// that provides access to the methods, 
		/// properties, and events common to all application objects within  
		/// an ASP.NET application. 
		///  
		public void Init(HttpApplication context)
		{
			// This module requires both Application Name and Root Domain to work. 
			if (string.IsNullOrEmpty(applicationName) ||
				string.IsNullOrEmpty(rootDomain))
			{
				return;
			}

			// Change the Application Name in runtime. 
			FieldInfo runtimeInfo = typeof(HttpRuntime).GetField("_theRuntime",
				BindingFlags.Static | BindingFlags.NonPublic);
			HttpRuntime theRuntime = (HttpRuntime)runtimeInfo.GetValue(null);
			FieldInfo appNameInfo = typeof(HttpRuntime).GetField("_appDomainAppId",
				BindingFlags.Instance | BindingFlags.NonPublic);

			appNameInfo.SetValue(theRuntime, applicationName);

			// Subscribe Events. 
			context.PostRequestHandlerExecute += new EventHandler(context_PostRequestHandlerExecute);
		}

		///  
		/// Disposes of the resources (other than memory) used by the module 
		/// that implements. 
		///  
		public void Dispose()
		{
		}
		#endregion

		///  
		/// Before sending response content to client, change the Cookie to Root Domain 
		/// and store current Session Id. 
		///  
		///  
		/// An instance of System.Web.HttpApplication that provides access to 
		/// the methods, properties, and events common to all application 
		/// objects within an ASP.NET application. 
		///  
		void context_PostRequestHandlerExecute(object sender, EventArgs e)
		{
			HttpApplication context = (HttpApplication)sender;

			try
			{
				if (context.Session != null &&
					//if (context.Session != null &&
					!string.IsNullOrEmpty(context.Session.SessionID))
				{
					// ASP.NET store a Session Id in cookie to specify current Session. 
					// This is a response, so if cookie doesn't exist, it is created!

					HttpCookie cookie = context.Response.Cookies["ASP.NET_SessionId"];
					// Need to store current Session Id during every request. 
					if (cookie != null)
					{
						cookie.Value = context.Session.SessionID;
						// All Applications use one root domain to store this Cookie 
						// So that it can be shared. 
						if (rootDomain != "localhost")
						{
							cookie.Domain = rootDomain;
						}

						// All Virtual Applications and Folders share this Cookie too. 
						cookie.Path = "/";
					}
				}
			}
			catch (Exception ex)
			{
			}
		}
	}


}

Step 2. Make the following modifications to BOTH web.configs

For older versions of IIS:

For newer versions of IIS:

Step 3. There is no Step 3. That’s it!

Leave a Reply

Your email address will not be published. Required fields are marked *