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

    Comments

    erich us

    Wednesday, September 03, 2008 6:34 PM

    Building a page that's available both internally and externally is a very bad idea IMHO.

    Beau us

    Friday, September 19, 2008 5:09 PM

    erich,

    Care to elaborate on your reasons why?

    yash sg

    Tuesday, September 30, 2008 4:34 AM

    Request.ServerVariables["LOCAL_ADDR"] Always return 127.0.0.1 For my Network can i do a Mixed Mode Authentiocqation using this.
    My approch is
    string serverIP = Request.ServerVariables["LOCAL_ADDR"];
    string userIP = Request.UserHostAddress;
    if (userIP.Equals(serverIP))
    {

    Server.Transfer("Winlogin.aspx");
    }

    Can you Please Help me as Both UserIp and Server Ip Return same IP address
    127.0.0.1 Is it Correct or some Error

    Beau us

    Tuesday, September 30, 2008 10:53 AM

    127.0.0.1 is the local loopback address, which returns the server address on which the request came in. You got that because you used LOCAL_ADDR. Try using REMOTE_ADDR instead to determine if the remote user is on the local network (and not the Internet).

    Tushar us

    Friday, January 09, 2009 2:38 AM

    i need to know active directories are normally behind the firewall. Intranet users could access the website and get authenticated thru ADS. For Internet users to access the same website they have to come thru a live ip i.e a different url.

    When internet users get to see a login form, and put there NTML credentials, will it get authenticated against an Active Directory. Isn't exposing ADS to outside world a security threat!!

    Please clarify.

    Busby SEO Test us

    Friday, January 16, 2009 1:42 AM

    IIS mixed mode authentication for ASP.NET Applications thanks for the info.

    Dave gb

    Thursday, October 01, 2009 5:36 AM

    I had this working great on IIS6, but can't get it working on IIS7. Is it possible to modify this approach for IIS7?