Larry Steinle

July 26, 2014

Mixed Authentication


Web sites are usually created for an intranet user or an internet user. This article explains how to create a single site for both intranet and internet usage while retaining the benefit of single sign-on for internal connections without compromising the security of external connections.

Background

When developing a web site for internal, intranet usage developers prefer to use Windows Authentication Security Mode. Users prefer Windows Authentication because they don’t have to log into the web site. The Windows Authenticated web site automatically knows who the user is and what rights the user has to the system.

However, Windows Authentication does not work externally on the internet. For external, internet connections a developer may choose to use Forms Authentication. Forms Authentication requires the user to provide credentials (usually in the form of a user name and password) to prove to the system they have rights to the web application.

What should you do when you want a web site to support both Windows and Forms Authentication at the same time? Can you create a web site that allows both the convenience of automatic sign-on from an internal connection and secure sign-on authentication from an external connection? Yes!

Two Sites for Mixed Mode Authentication

One popular strategy for supporting Mixed Mode or Single Sign-on Security (SSO) is to create two sites: one site configured with Windows Authentication and another site with Forms Authentication. When the user fails the Windows Authentication request configure IIS to redirect the user to the Forms Authenticated site. When the user passes the Windows Authentication request create a Forms Ticket (using the same cipher key as the Forms Authentication site) and redirect the user to the Forms Authentication site.

Two Sites Emulating Mixed Mode Authentication Process Flow

Two Sites Emulating Mixed Mode Authentication Process Flow

What if you don’t want two sites to support Mixed Mode Authentication?

Such was the challenge I recently encountered. In my search on this very problem I discovered an article from Microsoft written in 2004, Mixing Forms and Windows Security in ASP.Net. I realized it could be done. Unfortunately the article’s instructions were designed for an older version of IIS and involved more configuration changes to IIS than I wanted to make. Further searches revealed there weren’t any articles that pulled all the pieces together for the current VS.Net framework.

This article pulls all the information from my research together into a single resource. You will learn how IIS and various browsers behave when authenticating a user, how to configure a web site for both internal and external use and how to configure your web application to support mixed mode authentication. The code has been tested on both an internal and external connection using Internet Explorer, Safari for Windows, Google Chrome, Mozilla Firefox and Opera browsers.

Browser and IIS Authentication

The IIS Authentication options controls how the site manages security. Anonymous Authentication or Windows Authentication must be enabled. Anonymous Authentication basically disables security. However, when used with Forms Authentication it allows the developer to control which parts of the site have anonymous (or open) access and which parts require a forms authentication ticket with role based permissions for access. At least the login page must be configured for anonymous users otherwise the user can never access the login page to identify themselves.

Windows Authentication requires the computer to be on the same domain as the server. The browser will forward the NTLM or Kerberos data with the request to the server. This information provides proof of identify allowing the programmer to control access using role based permissions.

Unlike Forms Authentication, Anonymous Authentication can’t be used with Windows Authentication. When Anonymous Authentication is used the NTLM/Kerberos data is lost. However, Forms Authentication can be used with Windows Authentication.

ASP.Net Process Flow Differences

It is important to recognize that ASP.Net follows a slightly different process with Forms Authentication and Windows Authentication. The following two diagrams show the process flow from page request to page response for the different security models.

In Forms Authentication IIS validates the identify of the user after calling the IHttpModule.OnAuthenticateRequest and before calling the IHttpModule.OnAuthorizeRequest. If the user has no Forms Authentication Ticket then the server returns a 302 response to the browser redirecting the user to the forms login page.

Forms Authentication Process Flow

Forms Authentication Process Flow

On the other hand, with Windows Authentication IIS validates the identity of the user before calling the IHttpModule.OnAuthenticateRequest. If the user can’t be identified then a 401 challenge response is returned to the browser.

Windows Authentication Process Flow

Windows Authentication Process Flow

Mixed Authentication Strategy

Technically our site is going to be a Forms Authentication site with Windows Authentication enabled to identify the internal user. The site will continue to follow a similar process as the two site example described earlier in this post.

When a user on the intranet browsers to the site we will want to intercept the user credentials from the Windows Authentication logic creating a Forms Authentication Ticket which will provide common access to the site. Without the Forms Authentication ticket the user will be redirected to the login page even though the Windows Authentication passed.

When a user requests a page from the internet and they have no Forms Authentication Ticket we will create an Anonymous Ticket that gives the user access just to the login page. Without the Anonymous Ticket the Windows Authentication would throw an error and the user wouldn’t be able to reach the login page. When Forms Authentication passes then Windows Authentication is ignored. The Anonymous Ticket is used in place of the Anonymous Authentication behavior.

A few things we need to understand for this logic to work correctly:

  • When is the user navigating to the site from an internal network connection or an external internet connection? Windows Authentication should be guaranteed to work only from internal connections.
  • When is Forms Authentication and Windows Authentication enabled on the site? Ideally we want to disable creating a Forms Ticket when only Windows Authentication is enabled in IIS. And, there is no need to perform Windows Authentication checks when only Forms Authentication is enabled.
  • Does the requesting browser support Windows Authentication?

This logic will be implemented in a module so that we can easily add the mixed authentication support to any web site.

Creating and Configuring a Web Site for Mixed-mode Authentication

To support both Forms and Windows Authentication on the same site configure IIS with both Windows Authentication enabled and Forms Authentication enabled. If your browser has problems authenticating you may need to modify the order the providers are listed for the Windows Authentication option as some browsers do not support the Negotiate model.

Coding a Web Application to Support Mixed-mode Authentication

The code in this article has been tested with all major browsers. Safari for Windows does not support Windows Authentication. However, the system can be coded so that it automatically detects the Safari browser forcing the user into the external internet Forms Authentication logic flow.

For the code in this article to work you will need to include the code from two earlier posts:

MixedAuthenticationModule

This section creates a module to intercept events and call the mixed mode authentication logic. The module guarantees that only authenticated users are permitted to continue to the requested page otherwise it redirects the user to the login page.

using System;
using System.Web;

namespace MixedAuth
{
    public class MixedAuthenticationModule : IHttpModule
    {
        public void Dispose() { }

        public void Init(HttpApplication context)
        {
            context.AuthenticateRequest += new EventHandler(OnAuthenticateRequest);
            context.AuthorizeRequest += new EventHandler(OnAuthorizeRequest);
            context.EndRequest += new EventHandler(OnEndRequest);
        }

        public void OnAuthenticateRequest(Object source, EventArgs e)
        {
            //The AuthenticateRequest event fires after windows authentication and
            //before forms authentication. Here we need to ensure that everything
            //is configured correctly so the system won't throw a 302 error when
            //we are on the log-in page.
            MixedAuthentication.EnforceMixedMode();
        }

        public void OnAuthorizeRequest(Object source, EventArgs e)
        {
            //Ensure that only unauthorized users (anonymous users) are required
            //to log-in and authenticate with the system before giving them access
            //to the web site.
            MixedAuthentication.RedirectToLoginWhenUnauthorized();

            //When forms authentication is enabled even a 401 windows challenge
            //is redirected to the log-in page. Detect when we are being directed
            //to the log-in page even after successfully authenticating the user
            //returning the user to the originally requested page.
            MixedAuthentication.RedirectFromLoginWhenAuthorized();
        }

        public void OnEndRequest(Object source, EventArgs e)
        {
            //This is the only opportunity to intercept a windows challenge response
            //and over-ride it to permit the user access to the form's log-in page.
            if (WebInfo.IsWindowsChallenge && !WebInfo.IsWindowsModeSupported)
                MixedAuthentication.RedirectToLoginPage();
        }
    }
}

MixedAuthentication

This is the heart of the mixed authentication logic. The code handles redirecting unauthenticated users to the login page permitting authenticated users to continue to the requested page.

using System;
using System.Web;
using System.Web.Security;
using System.Security.Principal;

namespace MixedAuth
{
    /// <summary>
    /// Manages mixed-authentication services for Web applications. This class cannot be inherited.
    /// </summary>
    public sealed class MixedAuthentication
    {
        private const string ANONYMOUS_USER_NAME = "Anonymous";

        /// <summary>
        /// Sets the current security context to an anonymous user and adding it to the 
        /// cookies collection of the response, or to the URL if you are using cookieless
        /// authentication.
        /// </summary>
        public static void SetAnonymous()
        { AuthorizeUser(ANONYMOUS_USER_NAME); }

        /// <summary>
        /// Sets the current security context to the specified user name and adds it to the
        /// cookies collection of the response, or to the URL if you are using cookieless
        /// authentication.
        /// </summary>
        /// <param name="userName">
        /// The name of an authenticated user. This does not have to map to a Windows account.
        /// </param>
        public static void AuthorizeUser(string userName)
        {
            if (userName != WebInfo.UserName)
            {
                //Forms Authentication Uses GenericPrincipal
                //Windows Authentication Uses WindowsPrincipal
                GenericIdentity identity = new GenericIdentity(userName, "Custom");
                WebInfo.User = new GenericPrincipal(identity, null);
            }
            FormsAuthentication.SetAuthCookie(userName, false);
        }

        /// <summary>
        /// Get a value that indicates when the current user is an anonymous user.
        /// </summary>
        public static bool IsAnonymousUser
        {
            get { return WebInfo.UserName == ANONYMOUS_USER_NAME; }
        }

        /// <summary>
        /// Get a value that indicates when the user is authenticated.
        /// </summary>
        /// <remarks>
        /// This method can return false while the WebInfo.IsAuthorized
        /// returns true. WebInfo.IsAuthorized treats Anonymous users as
        /// authorized users where this one does not because a ticket
        /// must be created to ensure the user can access the log-in page
        /// when both forms and windows mode authentication is enabled.
        /// </remarks>
        public static bool IsAuthorized
        {
            get { return WebInfo.IsAuthenticated &amp;amp;amp;&amp;amp;amp; !IsAnonymousUser; }
        }

        /// <summary>
        /// Get a value that indicates when the user is requesting the log-in page.
        /// </summary>
        public static bool IsLoginPageRequest
        {
            get { return WebInfo.Request.RawUrl.ToLower().StartsWith(FormsAuthentication.LoginUrl.ToLower()); }
        }

        /// <summary>
        /// Get the originally requested URL.
        /// </summary>
        /// <remarks>
        /// Return URL is available only from the log-in page after the user is authorized.
        /// </remarks>
        public static string ReturnUrl
        {
            get
            {
                if (!IsLoginPageRequest || !IsAuthorized) return string.Empty;
                return !string.IsNullOrEmpty(WebInfo.QueryString["ReturnUrl"]) ? WebInfo.QueryString["ReturnUrl"] : FormsAuthentication.DefaultUrl;
            }
        }

        /// <summary>
        /// Redirects the authorized user back to the originally requested URL or the default URL.
        /// </summary>
        public static void RedirectFromLoginPage()
        {
            if (IsAuthorized) WebInfo.Response.Redirect(ReturnUrl);
        }

        /// <summary>
        /// Authorizes the specified user redirecting them back to the originally requested URL or the default URL.
        /// </summary>
        /// <param name="userName">
        /// The authenticated user name.
        /// </param>
        public static void RedirectFromLoginPage(string userName)
        {
            AuthorizeUser(userName);
            if (IsAuthorized) WebInfo.Response.Redirect(ReturnUrl);
        }

        /// <summary>
        /// Redirects an authenticated user back to the originally requested URL or the default 
        /// URL when the user is requesting the log-in page and authorized to use the site.
        /// </summary>
        public static void RedirectFromLoginPageWhenAuthorized()
        {
            if (IsLoginPageRequest &amp;amp;amp;&amp;amp;amp; WebInfo.IsFormsModeEnabled &amp;amp;amp;&amp;amp;amp; WebInfo.IsWindowsModeEnabled)
                RedirectFromLoginPage();
        }

        /// <summary>
        /// Redirects the browser to the log-in URL when the user is not authorized.
        /// </summary>
        public static void RedirectToLoginPageWhenUnauthorized()
        {
            if (!IsAuthorized &amp;amp;amp;&amp;amp;amp; !IsLoginPageRequest &amp;amp;amp;&amp;amp;amp; (WebInfo.IsFormsModeEnabled || WebInfo.IsWindowsModeEnabled)) RedirectToLoginPage();
        }

        /// <summary>
        /// Redirects the browser to the log-in URL setting the current user to anonymous.
        /// </summary>
        public static void RedirectToLoginPage()
        {
            MixedAuthentication.SetAnonymous();
            if (IsLoginPageRequest &amp;amp;amp;&amp;amp;amp; WebInfo.ResponseStatusCode == 302) return;
            WebInfo.ResponseStatusCode = 302;
            WebInfo.Response.Redirect(FormsAuthentication.LoginUrl);
        }

        /// <summary>
        /// Ensures that Windows Authentication is working correctly when running in Mixed Mode.
        /// </summary>
        /// <remarks>
        /// When running in Windows Mode only or when compiled with CLR 4.0 or greater this code is not required.
        /// </remarks>
        public static void EnforceWindowsMode()
        {
            if (WebInfo.IsWindowsModeSupported &amp;amp;amp;&amp;amp;amp; WebInfo.IsWindowsModeEnabled &amp;amp;amp;&amp;amp;amp; !WebInfo.InSecurityContext &amp;amp;amp;&amp;amp;amp; WebInfo.LogonUser.Length > 0)
                AuthorizeUser(WebInfo.LogonUser);
        }

        /// <summary>
        /// Ensures that forms authentication overrides windows authentication when there are conflicts.
        /// </summary>
        public static void EnforceFormsMode()
        {
            if (!WebInfo.IsFormsModeEnabled) return;

            HttpCookie authCookie = null;
            FormsAuthenticationTicket ticket = null;
            if (WebInfo.InWebContext)
                if (WebInfo.Request.Cookies.Count > 0)
                {
                    //Ensure that we didn't override Windows Authentication (impersonation or login as another user)
                    authCookie = WebInfo.Request.Cookies[FormsAuthentication.FormsCookieName];
                    try
                    {
                        if (authCookie != null || authCookie.Value != null)
                            ticket = FormsAuthentication.Decrypt(authCookie.Value);

                        if (ticket.Name != WebInfo.UserName)
                            MixedAuthentication.AuthorizeUser(ticket.Name);
                    }
                    catch
                    {
                        //If we can't decrypt the cookie it is because we never created the 
                        //cookie to begin with. This can occur when Anonymous Authentication
                        //is enabled and Forms Authentication is disabled.
                    }
                }
                else
                {
                    //Ensure that the forms ticket is created for the current security context.
                    //Failure to perform this check may cause unending loops with browsers like
                    //Chrome, Safari and Mozilla as the log-in page will throw an error because
                    //there isn't even an anonymous user ticket assigned.
                    if (WebInfo.InSecurityContext)
                        AuthorizeUser(WebInfo.UserName);
                    else if (!WebInfo.IsWindowsModeEnabled)
                        AuthorizeUser(ANONYMOUS_USER_NAME);
                }
        }

        /// <summary>
        /// Ensures that Forms and Windows Authentication are correctly applied.
        /// </summary>
        public static void EnforceMixedMode()
        {
            EnforceWindowsMode();
            EnforceFormsMode();
        }
    }
}

Web.Config

Be careful to set the options as shown below in the web.config so that IIS is configured correctly and to enable the mixed authentication module.

<?xml version="1.0"?>
<configuration>
  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="Login.aspx"/>
    </authentication>
  </system.web>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="MixedAuthenticationModule" type="TestAuth.MixedAuthenticationModule"/>
    </modules>
  </system.webServer>
</configuration>

Default.aspx

This page will display security context information about the user so you can see how the different authentication methods affects the type of user created for the site.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="MixedAuth.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head runat="server">
    <title></title>
  </head>
  <body>
    <form id="form1" runat="server">
      <p>
        <asp:Literal ID="UserName" runat="server" />
      </p>
      <asp:Button ID="LogOff" runat="server" OnClick="LogOff_Click" Text="Log Off" />
    </form>
  </body>
</html>
using System;
using System.Threading;
using System.Web;
using System.Web.Security;

namespace MixedAuth
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            UserName.Text = string.Empty;
            UserName.Text += "<br/>Raw Url = " + Request.RawUrl;
            UserName.Text += "<br/>Login Url = " + FormsAuthentication.LoginUrl;
            UserName.Text += "<br/>Logon User = " + Request.ServerVariables["LOGON_USER"];
            UserName.Text += "<br/>Auth User = " + Request.ServerVariables["AUTH_USER"];
            UserName.Text += "<br/>Remote User = " + Request.ServerVariables["REMOTE_USER"];
            UserName.Text += "<br/>Unmapped Remote User = " + Request.ServerVariables["UNMAPPED_REMOTE_USER"];
            UserName.Text += "<br/>User Identity = " + User.Identity.Name;
            UserName.Text += "<br/>Thread Identity = " + Thread.CurrentPrincipal.Identity.Name;
            UserName.Text += "<br/>Is Authenticated = " + User.Identity.IsAuthenticated;
            UserName.Text += "<br/>Authentication Type = " + User.Identity.AuthenticationType;
            UserName.Text += "<br/>Principal Type = " + User.GetType().FullName;
            UserName.Text += "<br/>Forms Mode Enabled = " + WebInfo.IsFormsModeEnabled;
            UserName.Text += "<br/>Windows Mode Enabled = " + WebInfo.IsWindowsModeEnabled;
            UserName.Text += "<br/>Windows Mode Supported = " + WebInfo.IsWindowsModeSupported;

            if (Request.ServerVariables["LOGON_USER"] != User.Identity.Name)
                UserName.Text += @"<p styl='margin-left:25%;margin-right:25%;'>
  Notice that Logon User contains a different name from the other user name properties. 
  This is because Logon User retains the original NTLM account name for the client while 
  the other user fields store the user account from the authentication filter (in this 
  case the forms ticket). This can be useful when you need to track when a user is logged 
  into the web site from another user's machine.
</p>";
        }

        protected void LogOff_Click(object sender, EventArgs e)
        {
            MixedAuthentication.RedirectToLoginPage();
        }
    }
}

Login.aspx

Of course we need a login page for the external internet users and for internal users who need to login from another users computer.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="MixedAuth.Login" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head runat="server">
    <title>User Login</title>
  </head>
  <body>
    <form id="form1" runat="server">
      <table>
        <tr>
          <th>User Name:</th>
          <td>
            <asp:TextBox ID="UserName" runat="server" />
          </td>
        </tr>
        <tr>
          <th>Password:</th>
          <td>
            <asp:TextBox ID="Password" runat="server" TextMode="Password" />
          </td>
        </tr>
        <tr>
          <td colspan="2" style="text-align:right;">
            <asp:Button ID="AuthenticateUser" runat="server" Text="Login" OnClick="AuthenticateUser_Click" />
          </td>
        </tr>
      </table>
      <asp:Literal ID="UserInfo" runat="server" />
    </form>
  </body>
</html>
using System;

namespace MixedAuth
{
    public partial class Login : System.Web.UI.Page
    {
        protected void AuthenticateUser_Click(object sender, EventArgs e)
        {
            if (CredentialsAreValid(UserName.Text, Password.Text))
                MixedAuthentication.RedirectFromLoginPage(UserName.Text);
        }

        private bool CredentialsAreValid(string userName, string password)
        {
            //YOUR CODE GOES HERE OR USE A MembershipProvider
            return true;
        }
    }
}

Mozilla Windows Authentication Support

Out of the box Mozilla does not support Windows Authentication. An add-in has been created for Mozilla that simplifies configuring Mozilla for Windows Authentication. Simply download and install the Integrated Authentication for Firefox extension and follow the directions to configure the browser for your local environment.

Summary

Now you should understand the behavior differences between Forms and Windows Authentication, and learned how to support mixed authentication in your ASP.Net web sites!

Happy coding!

Advertisement

47 Comments »

  1. Both these methods are not defined.
    MixedAuthentication.RedirectToLoginWhenUnauthorized();
    MixedAuthentication.RedirectFromLoginWhenAuthorized();

    I changed it to be:
    MixedAuthentication.RedirectToLoginPageWhenUnauthorized();
    MixedAuthentication.RedirectFromLoginPageWhenAuthorized();

    I am still having issues tho. It seems to be stuck in a loop. The windows user Id is not being passed through even though I have windows auth enabled.

    Comment by Dave — December 18, 2014 @ 7:16 am | Reply

    • IIS is a bit finicky. You have to pay close attention not only to the IIS Settings but also web.config. Can you provide additional details about how these are configured?

      Comment by Larry Steinle — December 18, 2014 @ 9:45 am | Reply

      • IIS:
        Windows Authentication Enabled.
        Basic Authentication Enabled.
        Digest Authentication Enabled.
        Forms Authentication Disabled. (Toggled on and off just to check)
        Impersonation Disabled.

        Web Config:

        // Using routes

        The http handler is being called but the user is always Anonymous.

        Comment by Dave — December 18, 2014 @ 10:45 am

      • I would start by disabling everything except windows. This code was tested to support forms and windows. Forms will show as disabled in IIS.

        Comment by Larry Steinle — December 18, 2014 @ 7:24 pm

      • <authentication mode="Forms">
              <forms loginUrl="Login"/>
            </authentication>
        

        Comment by Dave — December 18, 2014 @ 10:47 am

      • <system.webServer>
            <modules runAllManagedModulesForAllRequests="true">
              <add name="MixedAuthenticationModule" type="PSFinancials.Asp.Authentication.MixedAuthenticationModule" />
            </modules>
          </system.webServer>
        

        Comment by Dave — December 18, 2014 @ 10:49 am

  2. I am getting a “ASP.NET Ajax client-side framework failed to load.” when enabling the MixedAuthenticationModule : IHttpModule. Will filtering fix the issue?
    Thx

    Comment by Bb — February 17, 2015 @ 11:49 pm | Reply

    • The system is very finicky. You have to configure it exactly. See other comments for details.

      Comment by Larry Steinle — February 27, 2015 @ 11:26 am | Reply

      • What is your application pool Managed Pipeline mode set to? On my configuration, it works on Pipeline Mode = classic but I get the error on Integrated. I prefer to use Integrated and may have to tweak the config file. thx–

        Comment by Bb — May 19, 2015 @ 6:25 pm

      • It works with integrated but the site has to be configured in a certain way and the web.config in a certain way. It is a bit tricky but it does work.

        Comment by Larry Steinle — June 1, 2015 @ 9:11 am

      • I ended up switching to Integrated and adjusted some config file settings (old, pre-integrated ones) and now it is working fine. I also had to adjust the MixedAuthenticationModule.OnAuthorizeRequest/OnAuthorizeRequest to allow certain files required by the login page to be bypassed (temporary fix until I get a better solution). Thanks for sharing the code…

        Comment by Bb — June 3, 2015 @ 11:25 pm

  3. Hi, Larry can you please tell me what is “WebInfo” and where i can set or get this.
    Many Thanks.

    Comment by Omprakash Yadav — July 16, 2015 @ 9:11 am | Reply

    • For the code in this article to work you will need to include the code from two earlier posts:

      Web Information post, and
      Big Brother, Little Brother or No Bother? article to distinguish between internal and external connections.

      Comment by Larry Steinle — July 16, 2015 @ 9:36 am | Reply

  4. Will this technique work for MVC 5 sites? Will it work for owin in place of forms?

    Comment by RJJ — August 10, 2015 @ 1:07 am | Reply

    • I haven’t tested it but don’t see why it wouldn’t. The basic underlying mechanism is the same.

      Comment by Larry Steinle — August 12, 2015 @ 7:20 am | Reply

  5. Hi Larry,

    Thank you so much for this great article. My question is how to make this mixed authentication work with windows authentication having kerberos provider and delegation activated.
    The application pool is in integrated mode and it uses a service account kerb_user. the web application connects to an authentication service to authenticate the domain username connected in the client windows session that tries to connect to the website using its kerberos token. So I need the impersonation activated and windows authentication with the kerberos activated.

    Your help will be precious for me.

    Regards

    Comment by dev-ms — August 24, 2015 @ 10:05 am | Reply

  6. Hi guys. I followed the steps exactly. Windows authentication (IE, Chrome) works as expected. When i try the same using Mozilla, instead of showing the Login page, i get the browser popup for credentials, and if i click cancel, i get the HTTP Error 401.2 – Unauthorized. You are not authorized to view this page due to invalid authentication headers. What am i doing wrong?

    Comment by asc — September 29, 2015 @ 1:26 pm | Reply

    • Mozilla requires a plugin as it doesn’t support windows authentication by default.

      Comment by Larry Steinle — September 29, 2015 @ 10:14 pm | Reply

  7. Hi guys, Windows Authentication (IE, Chrome) work as expected. I can’t figure out why Mozilla shows the broser credentials popup instead of being redirected to the Login page. After i click cancel, i get a HTTP Error 401.2 – Unauthorized. You are not authorized to view this page due to invalid authentication headers.

    What i tried: Since forms need to be used with anonymous authentication, i assume visiting Login page is forbidden. This is why i am trying to add a location exception for Login.aspx in web.config, but with no success.

    Any suggestions?

    Comment by alinserbaneci — September 29, 2015 @ 1:30 pm | Reply

    • Hello again. Everything was working correctly. The “weird” (but totaly normal actually) behavior was triggered by the fact that i did not had Mozilla configured to accept cookies, which , obviously, needs to be enabled. Thx!

      Comment by alinserbaneci — September 30, 2015 @ 8:26 am | Reply

  8. Hi Larry,

    Thanks for the nice article. But i had no success.

    I followed the same steps but when i run in local IIS express am getting into redirect loop.

    So I tried hosting in IIS with only windows authentication enabled but when i accesses the site as anonymous it is asking for browser credentials popup.

    For some days am trying the same scenario but no luck

    Comment by Venkatesh — January 28, 2016 @ 10:38 am | Reply

    • I never tried it with express. The normal IOS configuration is very sensitive. Even when code is perfect you have to be careful with how you configure IIS and the web site itself. Send me your configuration settings and I’ll see if anything jumps out of you want.

      Comment by Larry Steinle — January 28, 2016 @ 8:02 pm | Reply

      • Sorry for delay reply.

        I have enabled only Windows Authentication, Once the site is accessed inside domain am getting the logged in domain username that’s perfect, but when i access the same site out side the network it is giving browser credential pop up, which i don’t want instead need to land in some page at least some where i need to know that the request is from external user.

        Below are ideal scenario am trying.

        1. We need to host only one site in IIS with both Anonymous and Windows mode enabled.
        2. When the site is accessed within network then i should be able to get domain logged in user name. (No browser credential pop up should come less priority).
        3. When the site is accessed outside network it should land in login page or any page.(No browser credential pop up should come).

        We already have OAuth 2.0 in place for external users and for internal users some way i need to identify the logged in user.

        Much appreciate you help.

        Comment by Venkatesh — February 4, 2016 @ 6:50 am

      • What I can tell you is that the web.config and IIS settings are very finicky. You have to follow the directions in the article perfectly. As well I only tested with Windows authentication and forms security. I never tried with oAuth. It is possible that OAauth is not playing well with this hack. Another option is to have a gateway site that detects if you are internal or external and it sends the user to the appropriate subsite where you can apply standard security settings. Then it is still one site.

        Comment by Larry Steinle — February 7, 2016 @ 4:39 pm

  9. I think I almost have this working in Classic mode. The last remaining problem is when I test on two PCs in the same domain I am getting the Windows Security login popup window when I launch my web site and I’m not sure why. If I turn on Anonymous authentication for the web site then the pop up goes away but of course I can’t get the Windows user name then.

    Comment by Joe — March 3, 2016 @ 9:53 pm | Reply

    • As I’ve said before web.Config and IIS settings are very finicky. I did my best to document what worked for my scenario. Just have to be patient and a very good investigator. Of course I was also on an older server and it may behave differently on the new operating systems.

      Comment by Larry Steinle — March 3, 2016 @ 9:56 pm | Reply

  10. Why would I be prompted by Windows for credentials when I try to access my web application? Client is a PC that is not on the server’s domain. I thought the MixedAuthenticationModule intercepted and prevented this challenge response?

    Comment by Joe — March 4, 2016 @ 1:24 pm | Reply

    • Depends on the browser. Some browsers require additional configuration to work correctly. Use IE and if it works then that’s the issue.

      Comment by Larry Steinle — March 4, 2016 @ 1:26 pm | Reply

  11. In MixedAuth.EnforceWindowsMode() the comment says this method is not necessary if compiled with CLR 4.0 or later. Why is that?

    Comment by Joe — March 4, 2016 @ 1:36 pm | Reply

    • I remember a behavior change in credential management but fail to recall exact reason.

      Comment by Larry Steinle — March 4, 2016 @ 1:45 pm | Reply

  12. Will this code prevent a Windows Security login popup when the user is connecting from an intranet address? In other words, I want to always go to the login web page and never want to see the Windows Security popup asking for a user name and password.

    Comment by Brian D. — March 5, 2016 @ 5:33 pm | Reply

    • The intent is to avoid a login page except when required because credentials are missing or invalid. Windows authentication should create credentials and bypass login page.

      Comment by Larry Steinle — March 5, 2016 @ 5:35 pm | Reply

      • My situation is the client could be on a different domain from the server (or not in a domain at all) but both are on an intranet network so the credentials WILL be missing so I always just want to go to my forms login page. I thought maybe it would just be a matter of changing IsInternalAddress() to always return true but I haven’t gotten it working yet. Not sure I tried it in integrated pipeline mode yet.

        Comment by Brian D. — March 6, 2016 @ 12:35 pm

      • Honestly I’d have to setup a server and replicate tor challenges to reproduce. It sounds like you are trying to do something different. The whole premise behind the code is to avoid Windows forms unless absolutely necessary. So there are probably a couple code paths that you need to consider which is why changing one routines behavior isn’t helping. Step thru debugging us your friend.

        Comment by Larry Steinle — March 6, 2016 @ 2:36 pm

  13. To be more specific, if a client is on the intranet but is NOT on the same domain as the server, it is always popping up the Windows login box. I instead want it to always go to the forms login page. Can I accomplish that by simple taking out EnforceWindowsMode? I still want the logged-on Windows user name (in cases where there is one) to be available to the forms login page though.

    Comment by Brian D. — March 5, 2016 @ 6:05 pm | Reply

    • That’s how it was intended to work and did work. I do not know if the newest Windows server has changed behaviors since I wrote the article.

      Comment by Larry Steinle — March 5, 2016 @ 6:07 pm | Reply

      • Does the web app.’s app. pool have to be running in integrated pipeline mode to prevent a Windows login popup? I think what’s happening is in Classic mode the Windows authentication is taking place before your http module runs. Does that make sense?

        Sorry for the all the questions and I appreciate that you responding to a four year old post! I’ve spent hours researching mixed auth. and your article and code is about the most clear I’ve found. The Microsoft MADAM module is way more complicated.

        Comment by Brian D. — March 6, 2016 @ 12:31 pm

  14. What is the role ok WebInfo class and where it is.

    Comment by rathul — June 2, 2016 @ 3:30 am | Reply

  15. Everything is good except:
    get { return WebInfo.IsAuthenticated & amp; amp; amp; & amp; amp; !IsAnonymousUser; }
    All the amp have the red underline, “The name amp does not exist in the current context”
    I assume this is some type of escape sequence required by the other functions. I’m not experienced in C#, when I need to write something I usually use VB so it’s probably something I don’t understand. If you can set me on the right path that would be great.

    Comment by Paul Quinn — June 21, 2017 @ 2:21 pm | Reply

    • It is the word press editor messing with stuff. It is an HTML escape code. Research how to escape a ampersand in HTML. It isn’t c#. Every time I try to fix it the editor breaks something else.

      Comment by Larry Steinle — June 21, 2017 @ 6:31 pm | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply to Larry Steinle Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: