Single Sign-On Across Multiple Domains

I'm trying to develop an easy to use HttpModule for enabling Single Sign-On across multiple domains. My idea was to have a single login page on one domain and then a HttpModule that checked for an authentication cookie on each domain and if there wasn't one already enabled it would redirect to the single login site and check if you were authenticated there. If you were the site would then forward you back with a query string so that you can create the cookie on that side.

Heres the code i've attempted:

HTTP MODULE

using System;
using System.Web;
using System.Web.Security;
using System.Configuration;
namespace SSOLibrary
{
/// <summary>
/// Summary description for SSOHttpModule.
/// </summary>
public class SSOHttpModule : IHttpModule
{
public SSOHttpModule() { /* nothing to construct */ }
#region IHttpModule Members

public void Init(HttpApplication context)
{
// subscribe to the begin request event
context.BeginRequest += new EventHandler(context_BeginRequest);
}

public void Dispose() { /* Nothing to dispose of */ }

#endregion

private void context_BeginRequest(object sender, EventArgs e)
{
string authDomain = ConfigurationSettings.AppSettings["authDomain"].ToString() + "?returnUrl=" + HttpContext.Current.Request.Url;
// see if the user is logged in
HttpCookie c = HttpContext.Current.Request.Cookies[ConfigurationSettings.AppSettings["cookieName"]];
if (c != null && c.HasKeys) // the cookie exists!
{
string cookie = HttpContext.Current.Server.UrlDecode(c.Value);
FormsAuthenticationTicket fat = FormsAuthentication.Decrypt(cookie);
return; // cookie decrypts successfully, continue processing the page
}

// the authentication cookie doesn't exist - ask AuthDomain if the user is logged in there
if(HttpContext.Current.Request.UrlReferrer == null)
HttpContext.Current.Response.Redirect(authDomain);

UriBuilder uri = new UriBuilder(HttpContext.Current.Request.UrlReferrer);
UriBuilder safeUri = new UriBuilder(ConfigurationSettings.AppSettings["authDomain"]);
if (uri.Host != safeUri.Host || uri.Path != safeUri.Path) // prevent infinite loop
{
HttpContext.Current.Response.Redirect(authDomain);
}
else
{
// we are here because the request we are processing is actually a response from AuthDomain
if (HttpContext.Current.Request.QueryString["ssoauth"] == null)
{
// AuthDomain does not have the authentication cookie
return; // continue normally, this user is not logged-in
}
else
{
// user is logged in to AuthDomain and we got his name!
string userName = (string)HttpContext.Current.Request.QueryString["ssoauth"];
// let's create a cookie with the same name
FormsAuthenticationTicket fat = new FormsAuthenticationTicket(1, userName, DateTime.Now, DateTime.Now.AddHours(1), true, "");
HttpCookie cookie = new HttpCookie(ConfigurationSettings.AppSettings["cookieName"]);
cookie.Value = FormsAuthentication.Encrypt(fat);
cookie.Expires = fat.Expiration;
HttpContext.Current.Response.Cookies.Add(cookie);
}
}
}
}
}

--
AUTH DOMAIN
--
private void Page_Load(object sender, System.EventArgs e)
{
// This is our caller, need to redirect back to it
UriBuilder uri = new UriBuilder(Request.UrlReferrer);
HttpCookie c = HttpContext.Current.Request.Cookies[ConfigurationSettings.AppSettings["cookieName"]];
if (c != null && c.HasKeys) // the cookie exists!
{
string cookie = HttpContext.Current.Server.UrlDecode(c.Value);
FormsAuthenticationTicket fat = FormsAuthentication.Decrypt(cookie);

uri.Query = uri.Query + "&ssoauth=" + fat.Name; // add logged-in user name to the query
}
Response.Redirect(uri.ToString()); // redirect back to the caller
}

The problem is that URLReferer is always null. Does anyone have a better way of doing this?

[4062 byte] By [sontek] at [2008-2-18]
# 1
The reason the URL referrer was null was because of security settings

on my browser. So using URLReferrer was not a realiable way of tracking

which domain sent the request so I decided to use a returnURL query

string. I also decided instead of returning the username in a query

string that I would instead build a token system. When the user logs

in it will generate a token for them and insert it into the database

with an expiration time and then the SingleSignOn.aspx page will send

the token to the query string and the Domain site needing

authentication will build the cookie by the token.

Heres the code:

HTTP MODULE

using System;

using System.Web;

using System.Web.Security;

using System.Configuration;

namespace SSOLibrary

{

/// <summary>

/// Summary description for SSOHttpModule.

/// </summary>

public class SSOHttpModule : IHttpModule

{

public SSOHttpModule() { /* nothing to construct */ }

#region IHttpModule Members

public void Init(HttpApplication context)

{

// subscribe to the begin request event

context.BeginRequest += new EventHandler(context_BeginRequest);

}

public void Dispose() { /* Nothing to dispose of */ }

#endregion

private void context_BeginRequest(object sender, EventArgs e)

{

string authDomain =

ConfigurationSettings.AppSettings["authDomain"].ToString() +

"?returnUrl=" + HttpContext.Current.Request.Url;

// see if the user is logged in

HttpCookie c = HttpContext.Current.Request.Cookies[ConfigurationSettings.AppSettings["cookieName"]];

if (c != null) // the cookie exists!

{

string tempCookie = HttpContext.Current.Server.UrlDecode(c.Value);

FormsAuthenticationTicket tempFat = FormsAuthentication.Decrypt(tempCookie);

// cookie decrypts successfully, continue processing the page

return;

}

// the authentication cookie doesn't exist - ask AuthDomain if the user is logged in there

if(HttpContext.Current.Request.QueryString["token"] == null)

HttpContext.Current.Response.Redirect(authDomain);


// user is logged in to AuthDomain and we got his name!

string token = (string)HttpContext.Current.Request.QueryString["token"];


if(token != String.Empty)

{

string userName = TokenFacade.ValidateToken(token);

if(userName != String.Empty)

{

// let's create a cookie with the same name

FormsAuthenticationTicket fat = new

FormsAuthenticationTicket(1, userName, DateTime.Now,

DateTime.Now.AddHours(1), true, "");

HttpCookie cookie = new HttpCookie(ConfigurationSettings.AppSettings["cookieName"]);

cookie.Value = FormsAuthentication.Encrypt(fat);

cookie.Expires = fat.Expiration;

HttpContext.Current.Response.Cookies.Add(cookie);

}

}

}

}

}
-

AUTH DOMAIN

private void Page_Load(object sender, System.EventArgs e)

{

string query = "token=";

// This is our caller, need to redirect back to it

if(Request.QueryString["returnURL"] != null)

{

UriBuilder uri = new UriBuilder(Request.QueryString["returnURL"]);

HttpCookie c = HttpContext.Current.Request.Cookies[ConfigurationSettings.AppSettings["cookieName"]];

if (c != null) // the cookie exists!

{

string cookie = HttpContext.Current.Server.UrlDecode(c.Value);

FormsAuthenticationTicket fat = FormsAuthentication.Decrypt(cookie);

string token = SSOLibrary.TokenFacade.GetTokenByUserName(fat.Name);

query += token;

}

uri.Query = query;

Response.Redirect(uri.ToString()); // redirect back to the caller

}

}

sontek at 2007-8-30 > top of Msdn Tech,Feedback for forums and MSDN websites,Off-Topic Posts (Do Not Post Here)...
# 2
The code you'v written leadas to a forever loop! From httpModule to login.aspx and vice versa. Also, you have mentioned about another class: SingleSignOn.aspx. Is it login.aspx or another class?
Farshad at 2007-8-30 > top of Msdn Tech,Feedback for forums and MSDN websites,Off-Topic Posts (Do Not Post Here)...