Mixed Authentication- ASP.NET Forms and IIS 6.0 Windows Integrated
Recently I was working on a website that needed to leverage both Integrated Windows and Forms Authentication. The idea is that this website has some users that are on Active Directory and should be able to gain access without being challenged from credentials when they are logged into the domain. Additionally, these users should be allowed to enter their AD credentials and access the site if they access the site remotely (while not logged in to AD). This is all handled by IIS Integrated Windows Authentication. However, for our situation we also have users who are not in AD at all and need to access the site. We preferred to not create AD accounts for these users just to permit access to the site. The solution we wanted was try windows integrated, if it fails, use forms authentication. This problem will go away with the integrated pipeline of IIS 7.0 (more on this with a later post). But, in IIS 6.0 this problem proved very tough to solve.
After doing some research, we found this article. http://msdn2.microsoft.com/en-us/library/ms972958.aspx. This was a great article and provided the basic approach to the solution.
To summarize
- Set site to use Forms Authentication
- Set web.config authorization to deny anonymous users
- You need to set one page in your web application to use Windows Integrated Authentication (this was a revelation to me, setting one page to different IIS authentication than others in same app). In this case we call it WinLogin.aspx
- In this page, capture the Request Variable "LOGON_USER" and then call FormsAuthentication.RedirectFromLoginPage.
- This allows you to mimick a forms logon for the windows user.
- Set Forms loginUrl to page from step 3
- Handle Windows Integrated failures. Add a custom handler for 401 errors, and in the static HTM page. Use a javascript function to forward to the real ASP.NET forms login page (WebLogin.aspx).
.gif)
6. Update web.config to allow anonymous access of WebLogin.aspx.
7. Redirect to original requested URL
.gif)
This was very helpful and got us a solution that solved most problems. However, we quickly ran into one problem that we couldn't live with. For IE users who did not have our site as a "Trusted Site", Windows Integrated would popup a login dialog prompting for credentials prior to our custom 404 provider. So, our non AD users would have to press cancel on this to get to our login form. Just not acceptable.
Some more research brought us to another great post that ultimately gave us they framework for our solution. The article that we used to help us solve this problem is here http://glazkov.com/blog/credentials-screening/.
We were not able to get his exact implementation working, and ended up changing the code quite a bit. But, the essence of the approach is exactly as described in that post (and the subsequent updates).
The short explanation of how this works is:
- Create an HTTPModule to handle incoming requests
- In this module, emit a javascript function to try to load the page that is setup as requiring Windows Integrated.
- Back in the module, check the LOGON_USER server variable. (If javascript succeeded in accessing that file, this variable will be populated)
- Continue with redirect
The key to the solution that solves the original problem is the javascript load of the page set to require Windows Integrated Authentication. What this accomplishes is pre-empting the load of this page that IIS will do and allows our code (in the handler) to respond the results of the check prior to IIS potentially popping up a dialog.
The last thing we needed to do was simply added AD integration into the weblogin form. So, we could still allow AD users who access the site without being logged into the domain use the domain credentials. I will discuss this in my next post. The code I am posting with this blog does not have the AD integration on the login page itself. (As it will take more work for me to factor that out into a digestable snippet).
You can download a sample here.