Support Forms and Windows authentication on the same ASP.NET Application.

It is quite common request to support both Forms and Windows authentication on the same Web Application.
The best solution that I found is described by  Richard Dudley at posts Using Forms Authentication with Windows Authentication (“Mixed Mode”) and How I Made Windows Authentication and Forms Authentication Work Together.

I want to post some details of my implementation of this approach. 

There is a snippet from my WebLogin.ASPX code Page_Load method:

        If Not IsPostBack Then

            Dim bUseWindowsAuthentication As Boolean = Application(“UseWindowsAuthentication”)

            If bUseWindowsAuthentication Then

                ‘ Redirect to winlogin to try WindowsAuthentication if possible

                ‘http://aspadvice.com/blogs/rjdudley/archive/2005/03/10/2561.aspx

                ‘http://aspadvice.com/blogs/rjdudley/archive/2005/03/10/2562.aspx (How I Made Windows Authentication and Forms Authentication Work Together )

            ‘//only try to force windows if starting with LAN ip address

                ‘//change this logic as your like using a Regex

                Dim r As New Regex(ConfigurationSettings.AppSettings(“lanIPmask”))

                If r.IsMatch(Me.Request.UserHostAddress) = True Or HttpRequestHelper.IsBrowserOnServer Then

                    If Me.Request.QueryString(“WinLoginError”) = Nothing Then ‘avoid indefinite loop

                        Dim sQueryString As String = Me.Request.QueryString.ToString()

                        DebugHelper.TracedLine(“sQueryString =” & sQueryString)

                        Response.Redirect(“Authent/WinLogin.aspx?” & sQueryString)

                    Else

                        lblError.Text = Me.Request.QueryString(“WinLoginError”)

                    End If

 

                End If

            End If

The snippet from Authent/WinLogin.aspx page

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        ‘http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/MixedSecurity.asp

        DebugHelper.PrintAspNetIdentities()

        Dim userName As String = Me.Request.ServerVariables(“LOGON_USER”) ‘usually with domain name

        userName = StringHelper.RightAfter(userName, “”) ‘Strip domain if applicable

        CreateFormsAuthenticationTicket(userName)

    End Sub

    Function CreateFormsAuthenticationTicket(ByVal userIdentity As String) As Boolean

        Dim oLogin As New FSWeb.LoginHelper

        Dim bPersistCookie As Boolean = False ‘ = Persist.Checked

        ‘Set Current.Session[“PatronID”] if successful

        Dim bRet As Boolean = False

        If Not DataHelper.IsNullOrEmpty(userIdentity) Then

            bRet = oLogin.LoadUserFromDatabase(userIdentity)

        End If

        If bRet = True Then

            ‘Save WindowsIdentity -from http://weblogs.asp.net/pwilson/archive/2004/02/02/66155.aspx

            Dim sUserToken As String = LoginHelper.GetUserTokenAsString(Me.Context)

            Dim userToken As New IntPtr(Integer.Parse(sUserToken))

            Dim identity As New WindowsIdentity(userToken, “NTLM”, WindowsAccountType.Normal, True)

            Dim principal1 As New WindowsPrincipal(identity)

            bRet = oLogin.SaveWindowsPrincipalInSession(principal1)

        End If

        If bRet = True Then

            oLogin.CreateTicket()

            If FSWeb.Login.RedirectBack(Me) = True Then Exit Function ‘old way of redirect

            ‘ Redirect back to original URL.

            WebFormsHelper.SmartRedirect(Page, FormsAuthentication.GetRedirectUrl(oLogin.PatronID, bPersistCookie))

        Else ‘windows account not found in the database- so use normal forms authentication

            Dim sQueryString As String = HttpHelper.QueryStringCollectionToString(Me.Request.QueryString)

            ‘Expected that oLogin.ErrorMessage is meaningful, but if not, ensure it’s not empty

            sQueryString = HttpHelper.AddQueryStringParameter(sQueryString, “WinLoginError”, Nz(oLogin.ErrorMessage, “Error during windows Authentication”))

            Dim sUrl As String = ResolveUrl(“~login.aspx?” & sQueryString)

            WebFormsHelper.SmartRedirect(Page, sUrl)

        End If

        Return bRet

    End Function

 

To setup this functionality it is required to specify lanIPmask in Web.Config and set authentication on AuthentWinLogin.aspx page to be Windows Integrated only.

 

Procedure to specify lanIPmask:

  1. Open file web.config in your web application folder the text editor(e.g. Notepad)
  2. Search for a line
    <add key=”lanIPmask value=””/>
  3. Change the value to appropriate, e.g. “192.168.d{1,3}.d{1,3}” if your LAN IP addresses are in range “192.168.0.0”- “192.168.255.255”
    For details how mask can be specified see

http://www.regular-expressions.info/examples.html

 

  1. Save the changed file.

 

Procedure to set  AuthentWinLogin.aspx page to be Windows Integrated only.  

1.      On the web server right-click the My Computer icon and click Manage.

2.      Expand the Services and Applications node in the MMC.

3.      Select Internet Information Services.

  1. Navigate to YorApplicationVirtualDirectoryAuthent Folder(Assume that the application is installed in the default virtual folder).
  2. Right-mouse click WinLogin.aspx, then Click Properties, and then click the File Security tab.
  3. Under Authentication and access control, click Edit.
  4. In the Enable Anonymous Access group, clear the Enable anonymous access check box.
  5. Ensure that Integrated Windows Authentication check box is ticked
  6. Click OK to close the dialog.
    UPDATE- See the post Programmatically set IIS Authentication for a page. to set authentication for WinLogin.aspx automatically from the installer.

 It is also required to  change Internet Explorer clients setting to trust your website  as described at

 http://groups.google.com.au/group/microsoft.public.inetserver.iis/msg/4e6c0dda9313f23a?hl=en&

I want to highlight that site should be added to Local Intranet , not to Trusted Sites.

 

 

UPDATE: Jorge Loco requested to post code of helper classes that are referred by these snippets.

 See My DataHelper class.  (including Nz method), My DebugHelper and TraceHelper classesMy HttpRequestHelper class, My StringHelper class.

 

To resolve ‘WindowsIdentity’ and  ‘WindowsPrincipal’ add

imports System.Security.Principal.
 

FSWeb.LoginHelper, FSWeb.Login and HttpHelper classes contain propriatary information and can’t be published. You should replace them with your own code.

UPDATE: there is excellent Michael Morozov‘s post Single Sign-On for everyone ,which suggest easier solution for mixed-mode authentication (Forms and Windows).I haven’t tried, but it looks promising. 

 

Advertisements