More Forms and Windows Security in ASP.NET

Ryan Dunn and I have been having a dialog on the ASP.NET Forums about my recent article on Mixing Forms and Windows Security in ASP.NET.  He has another technique that attempts to do something similar here on GotDotNet and he very much disagrees that my solution is sufficient.  Basically, my solution only demos how to combine Forms and Windows Authentication to automatically capture an Intranet user's name.  His method instead combines Forms and Windows Authorization by creating a WindowsPrincipal that roles can be checked against.  I apologize if someone thinks I've misled them since my article did not go all the way and illustrate the combined Authorization also, so I'm attaching the small amount of code, based on Ryan's work, that will create the WindowsPrincipal and complete the example.

Please also note that Ryan's technique is not at all sufficient, and is thus very misleading, since it does not actually do any real Windows Authentication!  It makes an assumption that all users within a certain IP address range are valid users -- which is not at all true if your network allows visitors to plug into the network and access the Intranet.  This means that visitors will automatically get access to your applications that use this technique and don't then check for an additional role.  It will also actually prevent such visitors from ever logging in with the alternative custom login form to prove they have the roles in the custom scenario you worked hard to create.  So, here's the necessary code, not very “clean” since its just a quick example, to complete my technique by creating a real WindowsPrincipal for Windows users:

Change the entirety of WinLogin.aspx's Page_Load method to:

IServiceProvider service = (IServiceProvider) this.Context;
HttpWorkerRequest request = (HttpWorkerRequest) service.GetService(typeof(HttpWorkerRequest));
this.Response.Cookies.Add(new HttpCookie("UserToken", request.GetUserToken().ToString()));
string userName = this.Request.ServerVariables["LOGON_USER"];
FormsAuthentication.RedirectFromLoginPage(userName, false);


Then add the following to the Global.asax's Application_AuthenticateRequest:

else if (this.Request.Cookies["UserToken"] != null) {
    string token = this.Request.Cookies["UserToken"].Value;
    IntPtr userToken = new IntPtr(int.Parse(token));
    WindowsIdentity identity = new WindowsIdentity(userToken,
        "NTLM", WindowsAccountType.Normal, true);
    HttpContext.Current.User = new WindowsPrincipal(identity);
}

You can make this “cleaner” (i.e. more secure) by including the userToken into the FormsAuthentication cookie's UserData so that it gets encrypted, instead of being a separate cookie as I've done here.

7 Comments

  • Thanks for the update to an already nice job.

  • Have you not looked at the code in the download that accompanies the article? If the download isn't working for you then you'll need to contact me directly via email so that I can send you the code.

    Thanks, Paul Wilson

  • Paul,
    I just download the code, compile it and push it on an IIS6 server in domain. I followed the differents configuration instructions. And put the redirect401.htm file in the custom erors of winlogin.aspx.
    From a domain computer, it works fine, i have my domain user. But outside of the domain, the windows authentication dialog shows up. If i hit cancel i am then redirected to the weblogin.aspx.
    So the question is how to get rid of the windows authentication form ? Any idea ?

  • Its a browser configuration option if you have that issue -- its not something you can get rid of just in code. I believe the details involved changing the security zone for that url to be intranet instead of internet, but I can't recall completely. I never experienced this in my tests, nor did the tester at MS, or we would have mentioned it, but since then its been apparent that lots of people do have this issue. Making a client browser change may make this unworkable for most people, and that's fair enough, as this really was just a hack of last resort and not something I wanted to do.

  • Hi Ryan:

    The technique presented in the article does NOT recreate Windows identities, so I believe there is a misunderstanding of intent. Instead, the goal of the article is to show how you can grab the Windows name of the user, if it exists and the browser is setup properly, while still presenting the typical login form otherwise. In all cases though, the result is ASP.NET Forms security since in the end the ASP.NET infrastructure forces you into one and only one model. If you actually need Windows identities then you will not be able to combine it with ASP.NET Forms in the same "app" -- you can however create two different "apps" with the same code base with one configured as ASP.NET Forms and one with Windows identities.

    Thanks, Paul Wilson

  • Hi Paul. I understand your intent in your original article. And it works well which I applaud you for. However, your followup code in this blog post certainly does try to recreate Windows identities for users that previously authenticated via Windows authentication does it not? It is with this followup work that I am having difficulty implementing. You basically store the user's token in a cookie and then use that token to recreate a WindowsPrincipal object in order to populate the Context.Current.User object on subsequent requests. Wasn't the intention there to recreate that user's WindowsIdentity for role checks and try to get the best of both worlds of authentication? I understand it was just a quick example which was probably not thoroughly tested and not included in your original article, which is why I was wondering if you ever used this followup technique in a production environment or ran into the issues I 'm encountering. I apologize if I still misunderstand your intention here. Thanks again.

  • Hi Ryan:

    Ah, my apologies as I totally forgot this post was not about my article in any real sense. So to answer your question, I certainly did not ever use this sample code beyond what was most likely a minimal test, so there may very well be issues that I did not ever notice.

    Also, as I've said many times in emails, and I'm pretty sure a couple of places publicly over time too, I was never really happy with any of this. I was forced to find a combined solution by a manager that refused to listen to my protests. Those reasons included that these types of hacks are very brittle -- they require odd IIS settings and even specific browser settings. The alternatives of just accepting either everyone login, or two separate "apps" with the same codebase that are configured differently, are more than adequate in my opinion. So why did I write an article that I didn't really like? Because in the process of doing what I was made to do, I discovered that many others were in similar situations, so I thought it would make a good article. Anyhow, while I did quite a bit of work and testing to get the main concept in the article, I've never spent any time on this since then -- other than responding to emails which tend to either be "this works great" or "this doesn't work". :)

    Good luck, Paul Wilson

Comments have been disabled for this content.