Notice: This website is an unofficial Microsoft Knowledge Base (hereinafter KB) archive and is intended to provide a reliable access to deleted content from Microsoft KB. All KB articles are owned by Microsoft Corporation. Read full disclaimer for more details.

How to authenticate against the Active Directory by using forms authentication and Visual Basic .NET


View products that this article applies to.

Summary

This step-by-step article describes how an ASP.NET application can use Forms authentication to permit users to authenticate against the Active Directory by using the Lightweight Directory Access Protocol (LDAP).

After the user is authenticated and redirected, you can use the Application_AuthenticateRequest method of the Global.asax file to store a GenericPrincipal object in the HttpContext.User property that flows throughout the request.

Create an ASP.NET Web Application in Visual Basic .NET

Follow these steps to create a new ASP.NET Web application named FormsAuthAd in Visual Basic .NET:
  1. Start Microsoft Visual Studio .NET.
  2. On the File menu, point to New, and then click Project.
  3. Click Visual Basic Projects under Project Types, and then click ASP.NET Web Application under Templates.
  4. In the Location box, type http://<servername>/FormsAuthAd (Replacing http://localhost if you are using the local server (so as to have http://localhost/FormsAuthAd, and then click OK.
  5. Right-click the References node in Solution Explorer, and then click Add Reference.
  6. On the .NET tab in the Add Reference dialog box, click System.DirectoryServices.dll, click Select, and then click OK.

Write the Authentication Code

Follow these steps to create a new class file named LdapAuthentication.vb:
  1. In Solution Explorer, right-click the project node, point to Add, and then click Add New Item.
  2. Click Class under Templates.
  3. Type LdapAuthentication.vb in the Name box, and then click Open.
  4. Replace the existing code in the LdapAuthentication.vb file with the following code:
    Imports System
    Imports System.Text
    Imports System.Collections
    Imports System.DirectoryServices
    
    Namespace FormsAuth
        Public Class LdapAuthentication
    
            Dim _path As String
            Dim _filterAttribute As String
    
            Public Sub New(ByVal path As String)
                _path = path
            End Sub
    
            Public Function IsAuthenticated(ByVal domain As String, ByVal username As String, ByVal pwd As String) As Boolean
    
                Dim domainAndUsername As String = domain & "\" & username
                Dim entry As DirectoryEntry = New DirectoryEntry(_path, domainAndUsername, pwd)
    
                Try
                    'Bind to the native AdsObject to force authentication.			
                    Dim obj As Object = entry.NativeObject
                    Dim search As DirectorySearcher = New DirectorySearcher(entry)
    
                    search.Filter = "(SAMAccountName=" & username & ")"
                    search.PropertiesToLoad.Add("cn")
                    Dim result As SearchResult = search.FindOne()
    
                    If (result Is Nothing) Then
                        Return False
                    End If
    
                    'Update the new path to the user in the directory.
                    _path = result.Path
                    _filterAttribute = CType(result.Properties("cn")(0), String)
    
                Catch ex As Exception
                    Throw New Exception("Error authenticating user. " & ex.Message)
                End Try
    
                Return True
            End Function
    
            Public Function GetGroups() As String
                Dim search As DirectorySearcher = New DirectorySearcher(_path)
                search.Filter = "(cn=" & _filterAttribute & ")"
                search.PropertiesToLoad.Add("memberOf")
                Dim groupNames As StringBuilder = New StringBuilder()
    
                Try
                    Dim result As SearchResult = search.FindOne()
                    Dim propertyCount As Integer = result.Properties("memberOf").Count
    
                    Dim dn As String
                    Dim equalsIndex, commaIndex
    
                    Dim propertyCounter As Integer
    
                    For propertyCounter = 0 To propertyCount - 1
                        dn = CType(result.Properties("memberOf")(propertyCounter), String)
    
                        equalsIndex = dn.IndexOf("=", 1)
                        commaIndex = dn.IndexOf(",", 1)
                        If (equalsIndex = -1) Then
                            Return Nothing
                        End If
    
                        groupNames.Append(dn.Substring((equalsIndex + 1), (commaIndex - equalsIndex) - 1))
                        groupNames.Append("|")
                    Next
    
                Catch ex As Exception
                    Throw New Exception("Error obtaining group names. " & ex.Message)
                End Try
    
                Return groupNames.ToString()
            End Function
        End Class
    End Namespace
    
    
    					

Explanation of the Code

The authentication code accepts a domain, a user name, a password, and a path to the tree in the Active Directory. This code uses the LDAP directory provider.
User Authentication
The code in the Logon.aspx page calls the LdapAuthentication.IsAuthenticated method and passes in the credentials that are collected from the user. Then, a DirectoryEntry object is created with the path to the directory tree, the user name, and the password. The user name must be in the "domain\username" format. The DirectoryEntry object then tries to force the AdsObject binding by obtaining the NativeObject property. If this succeeds, the CN attribute for the user is obtained by creating a DirectorySearcher object and by filtering on the SAMAccountName. After the user is authenticated, the IsAuthenticated method returns true.

Note When you use LDAP to bind to an Active Directory-related object, TCP ports are being used. Increased use of LDAP with the System.DirectoryServices namespace may use all the TCP ports that are available. You may be able to reduce the TCP load by reusing the connection that you used to authenticate your user.
User Groups
To obtain a list of groups that the user belongs to, this code calls the LdapAuthentication.GetGroups method. The LdapAuthentication.GetGroups method obtains a list of security and distribution groups that the user belongs to by creating a DirectorySearcher object and by filtering according to the memberOf attribute. This method returns a list of groups that is separated by pipes (|).

Notice that the LdapAuthentication.GetGroups method manipulates and truncates strings. This reduces the length of the string that is stored in the authentication cookie. If the string is not truncated, the format of each group appears as follows:
CN=...,...,DC=domain,DC=com
				
This can create a very long string. If the length of this string is greater than the length of the cookie, the authentication cookie may not be created. If this string may potentially be greater than the length of the cookie, you may want to store the group information in the ASP.NET Cache object or in a database. Alternatively, you may want to encrypt the group information and store this information in a hidden form field.

Write the Global.asax Code

The code in the Global.asax file provides an Application_AuthenticateRequest event handler. This event handler retrieves the authentication cookie from the Context.Request.Cookies collection, decrypts the cookie, and retrieves the list of groups that will be stored in the FormsAuthenticationTicket.UserData property. The groups appear in a pipe-separated list that is created in the Logon.aspx page.

The code parses the string in a string array to create a GenericPrincipal object. After the GenericPrincipal object is created, this object is placed in the HttpContext.User property.
  1. In Solution Explorer, right-click Global.asax, and then click View Code.
  2. Add the following code at the top of the code, behind the Global.asax.vb file:
    Imports System.Web.Security
    Imports System.Security.Principal
    					
  3. Replace the existing empty event handler for the Application_AuthenticateRequest with the following code:
    Sub Application_AuthenticateRequest(ByVal sender As Object, ByVal e As EventArgs)
            ' Fires upon attempting to authenticate the use
            Dim cookieName As String = FormsAuthentication.FormsCookieName
            Dim authCookie As HttpCookie = Context.Request.Cookies(cookieName)
    
            If (authCookie Is Nothing) Then
                'There is no authentication cookie.
                Return
            End If
    
            Dim authTicket As FormsAuthenticationTicket = Nothing
    
            Try
                authTicket = FormsAuthentication.Decrypt(authCookie.Value)
            Catch ex As Exception
                'Write the exception to the Event Log.
                Return
            End Try
    
            If (authTicket Is Nothing) Then
                'Cookie failed to decrypt.
                Return
            End If
    
            'When the ticket was created, the UserData property was assigned a
            'pipe-delimited string of group names.
            Dim groups As String() = authTicket.UserData.Split(New Char() {"|"})
    
            'Create an Identity.
            Dim id As GenericIdentity = New GenericIdentity(authTicket.Name, "LdapAuthentication")
    
            'This principal flows throughout the request.
            Dim principal As GenericPrincipal = New GenericPrincipal(id, groups)
    
            Context.User = principal
    
        End Sub
    					

Modify the Web.config File

In this section, you configure the forms, the authentication, and the authorization elements in the Web.config file. With these changes, only authenticated users can access the application, and unauthenticated requests are redirected to a Logon.aspx page. You can modify this configuration to permit only certain users and groups access to the application.

Replace the existing code in the Web.config file with the following code:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>    
  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="logon.aspx" name="adAuthCookie" timeout="60" path="/" >
      </forms>
    </authentication>	
    <authorization>	
      <deny users="?" />
      <allow users="*" />
    </authorization>	
    <identity impersonate="true" />
  </system.web>
</configuration>
				
Notice the identity impersonate="true" / configuration element. This causes ASP.NET to impersonate the account that is configured as the anonymous account from Microsoft Internet Information Services (IIS). As a result of this configuration, all requests to this application run under the security context of the configured account. The user provides credentials to authenticate against the Active Directory, but the account that accesses the Active Directory is the configured account. For more information, see the REFERENCES section.

Configure IIS for Anonymous Authentication

To configure IIS for anonymous authentication, follow these steps:
  1. In the Internet Information Services (IIS) management console, right-click the Virtual Directory node for "FormsAuthAd".
  2. Click the Properties, and then click the Directory Security Tab.
  3. Click Edit under Anonymous access and authentication control.
  4. Select the Anonymous Access check box.
  5. Make the anonymous account for the application an account that has permission to the Active Directory.
  6. Click to clear the Allow IIS To Control Password check box.
The default IUSR_computername account does not have permission to the Active Directory.

Create the Logon.aspx page

Follow these steps to create a new ASP.NET Web Form named Logon.aspx:
  1. In Solution Explorer, right-click the project node, point to Add, and then click Add Web Form.
  2. Type Logon.aspx in the Name box, and then click Open.
  3. In Solution Explorer, right-click Logon.aspx, and then click View Designer.
  4. Click the HTML tab in the Designer.
  5. Replace the existing code with the following code:
    <%@ Page language="vb" AutoEventWireup="true" %>
    <%@ Import Namespace="FormsAuthAd.FormsAuth" %>
    <html>
    	<body>
    		<form id="Login" method="post" runat="server">
    			<asp:Label ID="Label1" Runat="server">Domain:</asp:Label>
    			<asp:TextBox ID="txtDomain" Runat="server"></asp:TextBox><br>
    			<asp:Label ID="Label2" Runat="server">Username:</asp:Label>
    			<asp:TextBox ID="txtUsername" Runat="server"></asp:TextBox><br>
    			<asp:Label ID="Label3" Runat="server">Password:</asp:Label>
    			<asp:TextBox ID="txtPassword" Runat="server" TextMode="Password"></asp:TextBox><br>
    			<asp:Button ID="btnLogin" Runat="server" Text="Login" OnClick="Login_Click"></asp:Button><br>
    			<asp:Label ID="errorLabel" Runat="server" ForeColor="#ff3300"></asp:Label><br>
    			<asp:CheckBox ID="chkPersist" Runat="server" Text="Persist Cookie" />
    		</form>
    	</body>
    </html>
    <script runat="server">
    sub Login_Click(sender as object,e as EventArgs)
      Dim adPath as String = "LDAP://DC=..,DC=.." 'Path to your LDAP directory server
      Dim adAuth as LdapAuthentication = new LdapAuthentication(adPath)
      try
        if(true = adAuth.IsAuthenticated(txtDomain.Text, txtUsername.Text, txtPassword.Text)) then
          Dim groups as string = adAuth.GetGroups()
    
          'Create the ticket, and add the groups.
          Dim isCookiePersistent as boolean = chkPersist.Checked
          Dim authTicket as FormsAuthenticationTicket = new FormsAuthenticationTicket(1, _
               txtUsername.Text,DateTime.Now, DateTime.Now.AddMinutes(60), isCookiePersistent, groups)
    	
          'Encrypt the ticket.
          Dim encryptedTicket as String = FormsAuthentication.Encrypt(authTicket)
    		
          'Create a cookie, and then add the encrypted ticket to the cookie as data.
          Dim authCookie as HttpCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
    
          if(isCookiePersistent = true) then
    		authCookie.Expires = authTicket.Expiration
          end if				
          'Add the cookie to the outgoing cookies collection.
          Response.Cookies.Add(authCookie)	
    
          'You can redirect now.
          Response.Redirect(FormsAuthentication.GetRedirectUrl(txtUsername.Text, false))
        
        else
          errorLabel.Text = "Authentication did not succeed. Check user name and password."
        end if
     
      catch ex as Exception
        errorLabel.Text = "Error authenticating. " & ex.Message
      end try
    end sub
    </script>
    					
  6. Modify the path in the Logon.aspx page to point to your LDAP Directory server.
The Logon.aspx page is a page that collects the information from the user and call methods on the LdapAuthentication class. After the code authenticates the user and obtains a list of groups, the code does the following in this order:
  • creates a FormsAuthenticationTicket object;
  • encrypts the ticket;
  • adds the encrypted ticket to a cookie;
  • adds the cookie to the HttpResponse.Cookies collection;
  • redirects the request to the URL that was originally requested.

Modify the WebForm1.aspx Page

The WebForm1.aspx page is the page that is requested originally. When the user requests this page, the request is redirected to the Logon.aspx page. After the request is authenticated, the request is redirected to the WebForm1.aspx page.
  1. In Solution Explorer, right-click WebForm1.aspx, and then click View Designer.
  2. Click the HTML tab in the Designer.
  3. Replace the existing code with the following code:
    <%@ Page language="vb" AutoEventWireup="true" %>
    <%@ Import Namespace="System.Security.Principal" %>
    <html>
    	<body>
    		<form id="Form1" method="post" runat="server">
    			<asp:Label ID="lblName" Runat="server" /><br>
    			<asp:Label ID="lblAuthType" Runat="server" />
    		</form>
    	</body>
    </html>
    <script runat="server">
    sub Page_Load(sender as object, e as EventArgs)
      lblName.Text = "Hello " + Context.User.Identity.Name & "."
      lblAuthType.Text = "You were authenticated using " &   Context.User.Identity.AuthenticationType & "."
    end sub
    </script>
    					
  4. Save all files, and then compile the project.
  5. Request the WebForm1.aspx page. Notice that you are redirected to Logon.aspx.
  6. Type the logon credentials, and then click Submit. When you are redirected to WebForm1.aspx, notice that your user name appears and that LdapAuthentication is the authentication type for the Context.User.Identity.AuthenticationType property.
Note Microsoft recommends that you use Secure Sockets Layer (SSL) encryption when you use Forms authentication. This is because the user is identified based on the authentication cookie, and SSL encryption on this application prevents anyone from compromising the authentication cookie and any other valuable information that is being transmitted.

↑ Back to the top


References

For more information, click the following article numbers to view the articles in the Microsoft Knowledge Base:
306590 ASP.NET security overview
317012 Process and request identity in ASP.NET
306238 How to implement role-based security with forms-based authentication in your ASP.NET application by using Visual Basic .NET
313091 How to create keys by using Visual Basic .NET for use in Forms authentication
313116 Forms authentication requests are not directed to loginUrl page

↑ Back to the top


Keywords: KB326340, kbwebforms, kbsecurity, kbhowtomaster, kbcookie, kbconfig

↑ Back to the top

Article Info
Article ID : 326340
Revision : 9
Created on : 3/22/2007
Published on : 3/22/2007
Exists online : False
Views : 662