beaucrawford.net

Give me data or give me death

About the author

Author Name is someone.
E-mail me Send mail

Recent comments

Don't show

Authors

Tags

Don't show

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

    © Copyright 2012

    IIS mixed mode authentication for ASP.NET Applications

    The situation: You have a Website that is available to both Intranet users and Internet users.  You want users from the Intranet to be authenticated automatically using their domain credentials and Internet users to be authenticated using basic ASP.NET forms security.

    The problem:  When you configure IIS to allow both Anonymous Access and Integrated Windows Authentication, you cannot get the remote users NTLM name.

    The fix:  Configure IIS to use Anonymous Access for the entire site, with the exception of a single page.  This page will be set to use Integrated Windows Authentication.

    The gotcha is that, when you check "Anonymous Access" in IIS, you cannot get the user's NTLM name (even if you check "Integrated Windows Authentication").  The key is to specify "Integrated Windows Authentication" for exactly one page in your application.  When Intranet users hit this page, we can get their NTLM name out of Request.ServerVariables["LOGON_USER"] and programmatically store that in the FormsAuthentication context.  External Internet users can even hit this page and manually specify their domain credentials if they like, but these users will normally go through the standard login process.

    STEP 1 - Create a new virtual directory in IIS.  In the application root, only check "Anonymous Access".  Do not check "Integrated Windows Authentication".  This should look like:



    STEP 2 - In your Web application, create a new form named "WinLogin.aspx" under the app's root.  Also add a page named "Login.aspx" under the app's root and put an instance of the standard System.Web.UI.WebControls.Login control on it, e.g.:

    <asp:Login id="login" runat="server" />

    STEP 3 - In IIS manager, select WinLogin.aspx and navigate to the "File Security" tab.  Here disable "Anonymous Access" but enable "Integrated Windows Authentication" (this is crucial).   This should look like:


    STEP 4 - In your web application's web.config, add the following:

    <!-- Allow anonymous access to this page. --> 
    
      <location path="Login.aspx">
        <system.web>
          <authorization>
            <allow users="?,*" />
          </authorization>
        </system.web>
      </location> 
    
      <!-- Allow anonymous access to this page.  IIS will prompt external users.  Intranet users will be authenticated automatically when they hit this page --> 
    
      <location path="WinLogin.aspx">
        <system.web>
          <authorization>
            <allow users="?,*" />
          </authorization>
        </system.web>
      </location> 
    
       <system.web> 
    
        <authentication mode="Forms">
          <forms loginUrl="Login.aspx"/>
        </authentication> 
    
        <!-- Require authentication for all other pages --> 
    
        <authorization>
          <deny users="?" />
          <allow users="*" />
        </authorization> 
    
        <!-- custom membership provider to handle forms authentication --> 
    
        <membership defaultProvider="CustomMembershipProvider">
          <providers>
            <clear />
            <add name="CustomMembershipProvider" type="DualAuthenticationExample.CustomMembershipProvider, DualAuthenticationExample" />
          </providers>
        </membership>
    
        <!-- OTHER SETTINGS --> 
    
       </system.web>

    STEP 5 - In ~/Login.aspx add the following code:

    namespace DualAuthenticationExample
    {
        public partial class LoginPage : System.Web.UI.Page
        {
            private bool IsIntranetRequest(string ip)
            {
                // TODO: Check for whatever interanet ip addresses, ranges, etc here... 
    
                return !string.IsNullOrEmpty(ip) && Regex.IsMatch(ip, "^127");
            } 
    
            protected void Page_Load(object sender, EventArgs e)
            {
                if (IsIntranetRequest(Request.ServerVariables["REMOTE_ADDR"]))
                {
                    Response.Redirect("~/WinLogin.aspx");
                }
            }
        }
    } 

    STEP 6 - In ~/WinLogin.aspx add the following code:

    namespace DualAuthenticationExample
    {
        public partial class WinLoginPage : System.Web.UI.Page
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                string user = Request.ServerVariables["LOGON_USER"]; 
    
                if (string.IsNullOrEmpty(user))
                {
                    Response.Redirect("~/Login.aspx");
                }
                else
                {
                    FormsAuthentication.SetAuthCookie(user, false);
                    Response.Redirect("~/Default.aspx");
                }
            }
        }
    }
    So, as specified in the web.config, all users will be bounced to the Login.aspx page.  If the user is on the intranet, they'll get bounced again to the WinLogin.aspx page, which will attempt to authenticate them with their NTLM name.  If that is successful, then we programmatically log them into the FormsAuthentication context.  From here, the user's name will be availble via the standard Page.User.Identity.Name property (as well as HttpContext.Current.User.Identity.Name)

    If you know the IP range(s) for the Intranet requests you can really streamline what I have here and make it seamless to the user.  This is technically not a requirement to get this to work but, without it, external users will be prompted with the authentication box (I believe the behavior for this varies between browsers and also depends on the user's current browser settings).

    Note: in the above example, I use my own custom MembershipProvider.  You can obviously use any type that derives from System.Web.Security.MembershipProvider.


    Categories: C# | ASP.NET
    Posted by Beau on Tuesday, July 15, 2008 6:25 PM
    Permalink | Comments (7) | Post RSSRSS comment feed