Ayende @ Rahien

It's a girl

Mixed Mode Authentication

I just finished spending half a day implementing support for mixed mode authentication in my application. I am putting it here mostly to remind me how it is done, since it was a pretty involved process.

As usual, the requirement was that most of the users would use Windows Authentication in a single sign on fashion, and some users would get a login screen.

I am using Forms Authentication, and I want to keep is as simple as possible. After some searching, it seems that most of the advice on the web seems to include building two sites, and transfering credentials between the sites.

Perhaps the best places to look for it is this image, that explains how Forms Authentication works, and these two posts from Craig Andera #1, #2. After reading those, I had a much better picture of what I needed to do.

This requires several steps that are supposed to be self coordinated in order for it to work:

  • Setup IIS for Anonmous + Integrated security.
  • In the web.config, specify forms authentication.
  • In the Login Controller, you need to check whatever the user is a candidate or windows authentication. In my case, it is decided according to IP ranges, but your case may be different.
  • If the user can do windows authentication, you need to send back a 401 status code.
  • Here is where it get a bit hairy. You can't do it from the login controller, because the FormsAuthentication Module will intercept that and turn it into a redirect to the login page.
  • You need to create a http module and register it last in the HttpModule sections, and there you can specify the 401 status code safely. I used the HttpContext.Items to transfer that request.
  • After the request has returned, if Windows Authenticaton has been successful, you can access the user name in the ServerVariables["LOGON_USER"].
  • Create a Form Authentication cookie as usual, and carry on with your life.

The main issue here, as far as I am concerned was to make sure that I will do it in a way that is maintainable, there are several disparate actions that are taking place that are all needed to make it work.

It took a while before I could get to grip with what was going on the wire, so here is the code for this:

private void HandleInternalUsersAndSingleSignOn()
{
	//semi-internal users and the like
	if(Context.ClientIpIsIn(Settings.Default.ExcludeFromWindowsAuthetication))
		return;
	string logonUser = Context.GetLogonUser();
	if(string.IsNullOrEmpty(logonUser))
	{
		//Internal installation and an empty user means
		//that we have not yet auttenticated, we register a request
		//to send a 401 (Unauthorized) to the client, so it will provide
		//us with their windows credentials.
		//we have to register a request for 401, which will be handled by the
		//Add401StatusCodeHttpModule on the EndRequest because we are also using
		//FormsAuthentication, and we need to bypass the FormAuthentication interception
		//of 401 status code.
		Context.SetContextVaraible(Constants.Request401StatusCode, true);
		Context.EndResponse();
	}
	// will redirect to destination page if successful
	LoginAuthenticatedUser(logonUser);
}

Comments

Jose
08/31/2007 12:18 AM by
Jose

That is pretty cool man, can you post all the code that you created so other people can use it?

Sergei Shelukhin
09/11/2007 09:07 AM by
Sergei Shelukhin

Hi.

Where do yu attach the HTTP module so that it would override the FormsAuthentiction module? I clear response and send 401 in PreSendRequestHeaders, but it still redirects me to login page.

Sergei Shelukhin
09/11/2007 11:22 AM by
Sergei Shelukhin

Hmm, turns out the problem was different :)

It's that I never actually set the Forms cookie because Session_start doesn't fire again when the user is authenticated after 401 response, so I'm getting redirected to Login page even though 401 part does work.

I guess that could be done on every request (check session variable for being set/set up applicarion specific session stuff if it's not set), buit it's kinda ugly, is there a better solution that you used?

Comments have been closed on this topic.