IIS 7.0 Two-Level Authentication with Forms Authentication and Windows Authentication

One of the key improvements granted by the ASP.NET integration in IIS 7.0 is a unified authentication model.  Instead of the two-stage model in previous versions of IIS, where IIS executed its own authentication methods before ASP.NET processing began, in Integrated mode IIS and ASP.NET authentication modules participate in a single authentication process as equals. With this, it becomes very easy to write custom authentication methods using .NET (that previously required ISAPI filters and C++ code), and use these solutions in a way that integrates seamlessly into the IIS security model.

Popular example – everyone’s favorite Forms authentication, backed by a Membership credential store and login controls, being used to secure access to your entire Web site including your images, PHP pages, CGI applications, and so on.
 

The problem: using ASP.NET Forms authentication and IIS Windows authentication in the same application

Unfortunately, one of the limitations of a single-stage authentication model is that it is done in a single stage (imagine that!).  Because of this, certain authentication schemes that relied on the two-stageness of the authentication process used by ASP.NET applications in the past no longer work.

Consider the following example:

You have a login.aspx page which allows your users to log in using Forms authentication.  But, all of your users also have Windows accounts on the server (or Active Directory). For some reason, you want all users to first log in using their Windows credentials, and then log in using their Membership credentials and Forms authentication.  You could do that by enabling Windows authentication and disabling Anonymous authentication in IIS, which would cause the request to be rejected by IIS before it would arrive in ASP.NET, thereby making sure that your users were first authenticated by Windows auth.

This works on IIS 6.0 and on IIS 7.0 in Classic mode. But, in Integrated mode, both Windows and Forms authentication run during the single stage authentication process, which makes it impossible to first authenticate with Windows authentication, and second authenticate with Forms authentication. Additionally, because Forms authentication is enabled for the entire application, there is no way to enable it for a part of your app and not for another – which presents a problem, because Forms authentication’s 302 redirect challenge is incompatible with the 401 “WWW-Authenticate” challenge used by Windows authentication.  Forms auth will always convert unauthorized requests to the application to a 302 redirect, thereby breaking Windows authentication.

Here is how to do it …

After posting the list of ASP.NET breaking changes for IIS 7.0, a number of people contacted me asking for a way to accomplish this.

The answer lies in separating the windows authentication and forms authentication transactions into two separate pages – one page will be the gateway page that requires Windows authentication, and the other page (or pages) will require forms authentication. Luckily, this maps well into the Forms Authentication model of having a separate login page which will become our gateway.

Secondly, using a wrapper module, we will disable Forms authentication for the gateway (login) page. This way, our Windows authentication challenge will work correctly.

Two-Level authentication on IIS 7.0 using Forms Authentication and Windows Authentication

This works as follows (as shown in the diagram above):

1)      Anonymous request to page.aspx (a protected page in your app)

a.       Access is denied (anonymous is disabled, or, authorization rule denies anonymous user)

b.      Forms authentication issues a 302 redirect to login page

2)      Redirected anonymous request to the login page

a.       Access is denied (anonymous is disabled)

b.      Forms authentication is disabled using our wrapper, so it doesn’t issue a 302 redirect

c.       Windows authentication issues a challenge

3)      Request with windows credentials to the login page (this may actually be several requests as part of the NTLM/Kerberous handshake)

a.       Windows authentication authenticates the request

b.      The page either displays a login control for the user to log in using forms, or automatically logs in using forms equivalent of the windows user

c.       Issues a 302 redirect back to the original page

4)      Forms-authenticated request to page.aspx succeeds

Setting it up

Download the attached application for an example of setting it up. You’ll need to:

1. Unlock the <anonymousAuthentication> and <windowsAuthentication> configuration sections before you can use them in web.config:

> %windir%\system32\inetsrv\appcmd unlock config /section:anonymousAuthentication
> %windir%\system32\inetsrv\appcmd unlock config /section:windowsAuthentication

2. Register the forms authentication wrapper configuration section in your web.config:

<!-- FormsAuthsModule configuration section -->

<configSections>

  <section name="formsAuthenticationWrapper"  

           type="Mvolo.Modules.FormsAuthConfigurationSection" />

</configSections>

3. Replace the built-in Forms Authentication module with the wrapper:

<system.webServer>

  <!-- Replace the built-in FormsAuthenticationModule with the FormsAuthModule wrapper -->

  <modules>

    <remove name="FormsAuthentication" />

    <add name="FormsAuthentication" type="Mvolo.Modules.FormsAuthModule" />

  </modules>

</system.webServer>

4. Set the required settings for the gateway page:

<!-- Disable Forms Authentication for this URL -->

<location path="login.aspx">

  <!-- Disable Forms Authentication -->

  <formsAuthenticationWrapper enabled="false" />

  <system.webServer>

    <security>

      <!-- Enable IIS Windows authentication for the login page -->

      <authentication>

        <windowsAuthentication enabled="true" />

        <anonymousAuthentication enabled="false" />

      </authentication>

    </security>

  </system.webServer>

</location>

That should do it.

Some caveats:

- The wrapper uses reflection to invoke the real forms authentication module. This means that it must either run in applications in Full trust, or be in the GAC.
- This is for Integrated mode applications on IIS 7.0 only. Previous versions of IIS or Classic mode applications dont require this as they use two-phase authentication.

Downloads:

1)      Sample application and FormsAuthModule wrapper v1.0.

2)      Source code for FormsAuthModule wrapper v1.0.

NOTE: Released under Microsoft Permissive License, and supported exclusively through this blog.

Thanks,

Mike

Published 11 February 08 02:28 by Mike Volodarsky

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# IIS 7.0 Server-Side : Breaking Changes for ASP.NET 2.0 applications running in Integrated mode on IIS 7.0 said on February 11, 2008 3:16 PM:

PingBack from http://mvolo.com/blogs/serverside/archive/2007/12/08/IIS-7.0-Breaking-Changes-ASP.NET-2.0-applications-Integrated-mode.aspx

# MVolo's Blog said on February 11, 2008 3:52 PM:

The integration of IIS and ASP.NET authentication stages in Integrated mode applications brings a lot

# Claudiov said on February 13, 2008 4:37 AM:
Hi Mike, I have followed your instructions but I receive HTTP 503, the service is unavailable, I'm I missing something?...
# Mike Volodarsky said on February 13, 2008 12:49 PM:

Hi Claudiov,

Please follow http://mvolo.com/blogs/serverside/archive/2006/10/19/Where-did-my-IIS7-server-go_3F00_-Troubleshooting-_2200_service-unavailable_2200_-errors.aspx and see whether you are getting an error message in the event log.

Thanks,

Mike

# Steve said on March 14, 2008 7:55 AM:
will this approach work with web services? Is it possilbe to use one url which allows integrated authentication + user name password to be passed via soap header properties. The if the caller is not a windows user, use passed in credentials?
# Mike Volodarsky said on March 14, 2008 2:03 PM:

Hi Steve,

You wouldnt use this exact approach, but something similar.  If you can extract the credentials in AuthenticateRequest, you can run after the WindowsAuthenticationModule and authenticate as that user. Otherwise, let the request go forward and be rejected with the NTLM / Negotiate challenge to authenticate with Windows credentials.

If you cannot extract credentials until later when WCF has processed the request/SOAP payload, then just authenticate as special "interim" user in AuthenticateRequest to avoid the request being rejected, then in your web service either authenticate with the SOAP credentials or reject the request with 401 to allow Windows authentication to take place.

Just a note: this information is intended for WCF web services hosted in IIS 7.0 running in Integrated mode.

Thanks,

Mike

# AndrewHa said on May 21, 2008 7:30 PM:

Mike what if you wanted to add to this module for x509cert authentication and SecureID. What happens if your internal users don't have passwords and you don't want to distribute another ID. So your users that may use windows auth when they are logged onto your network are hitting the same site from the internet and have the same certificate they would use when they log onto their workstations. Or some of your users have secureID cards.

# Mike Volodarsky said on May 23, 2008 1:21 PM:

Hi Andrew,

Theoretically, you would configure the the required authentication (cert auth or secureId) for the gateway page, and flow their authenticated identity to the forms ticket the same way I do it here using Windows Auth.

The way you determine the identity in the gateway is completely up to you, so it should support any authentication protocol you'd like to use. As long as you then take that identity and issue a forms auth ticket to represent it.

Keep in mind though that Forms Authentication is a ticket-based scheme, which has inherent security limitations.  Using it to represent a stronger authentication scheme (like x509) is essentially downgrading the security of that scheme - if someone manages to exploit the forms auth ticket.

For more info on ticket security, search "client ticket security" in my old article, http://msdn.microsoft.com/en-us/magazine/cc163702.aspx.

Thanks,

Mike

# Benny_NET said on July 22, 2008 3:48 AM:

[原文:http://mvolo.com/blogs/serverside/archive/2007/12/08/IIS-7.0-Breaking-Changes-ASP.NET-2.0-applic...

# Neeraj Tomar said on August 13, 2008 1:53 PM:
Hi Mike, My project requires windows Authentication and if user do not provide correct credentials or don't have valid credentials, then to display login page and use ADAM (Form authentication). I tried your sample application. Challenge response dialog appears but if i cancel it then HTTP Error 401.2 appears. I need to display login page instead. Let me know how can i override 401.2 Unauthorized error page with my login page. Your help is highly appreciated. Regards, Neeraj Tomar

Leave a Comment

(required) 
(optional)
(required) 
Enter the code you see below


About Mike Volodarsky

For the past 5 years, I was the core Program Manager for Microsoft ASP.NET 2.0 and IIS 7.0 products. I drove the design and development of the IIS 7.0 web server core, the IIS FastCGI support, the AppCmd command line tool, the ASP.NET Integrated pipeline, and other special projects around server security, performance, and scalability. Now, I am working on my own on cutting edge web server tech on top of the Microsoft IIS platform, and continue blogging about it here.

About me



For the past 5 years, I was the core server Program Manager for the IIS 7.0 and ASP.NET 2.0 products at Microsoft.
Now, I work on advanced web server tech using IIS 7.0, .NET, and Windows Server 2008 and write about it in this blog.

View Michael Volodarsky's profile on LinkedIn

Writings



TechNet Magazine
>Top 10 Performance Improvements in IIS 7.0

MSDN Magazine
>IIS 7.0: Build Web Server Solutions with End-To-End Extensibility
>IIS 7.0: Enhance Your Apps with the Integrated ASP.NET Pipeline
>IIS 7.0: Explore The Web Server For Windows Vista And Beyond
>Design and Deploy Secure Web Apps with ASP.NET 2.0 and IIS 6.0
>Fast, Scalable, and Secure Session State Management for Your Web Applications


Tools and Modules

LeechGuard
IconHandler 2.0
DirectoryListing
HttpRedirection
IIS Auth for Wordpress
iisschema.exe
PortCheck.exe v2.0

Popular Posts

- ASP.NET 2.0 Breaking Changes on IIS 7.0
- Develop IIS7 modules and handlers with .NET
- Troubleshoot IIS7 errors like a pro
- Troubleshooting 503 / "service unavailable" errors
- Troubleshooting "server not found" errors
- Create IIS7 sites, applications, and virtual directories
- Run Ruby on Rails with IIS FastCGI
- VS Debugging of ASP.NET applications on Windows Vista
- Stop hot-linking with IIS and ASP.NET

Tags

Search

Go

This Blog

Archives

Good IIS Blogs

Disclaimer

These postings are provided as is with no warranties, and confer no rights. The views expressed in this blog are entirely my own.

Syndication