MSDN Deep Dive: Building Killer ASP.NET Applications…

Lot’s of requests for the code from the Forms Login demo… here it is…


using System;

using System.Text;

using System.Collections;

using System.DirectoryServices;

using System.Web;

namespace FormsAuthAD


public class LDAPAuthentication


private string _path;

private string _filterAttribute;

private string domainusername, password, rootdse, uname;

public LDAPAuthentication()



public LDAPAuthentication(string path)




public bool IsAuthenticated(string domain, string username, string pwd)


string domainAndUsername = domain + @”\” + username;



password = pwd;


DirectoryEntry entry = new DirectoryEntry( _path, domainAndUsername, pwd);



// Bind to the native AdsObject to force authentication.

Object obj = entry.NativeObject;

DirectorySearcher search = new DirectorySearcher(entry);

search.Filter = “(SAMAccountName=” + username + “)”;


SearchResult result = search.FindOne();

if(null == result)


return false;


// Update the new path to the user in the directory

_path = result.Path;

_filterAttribute = (String)result.Properties[“cn”][0];


catch (Exception ex)


throw new Exception(“Error authenticating user. ” + ex.Message);


return true;


public string GetGroups(LDAPAuthentication adAuth)


//does not work for users not part of at least 2 groups

//for example, will not work for users that are only part of the

//domain users group

string resultset=””;

StringBuilder groupNames = new StringBuilder();



//bind to AD on the new path

DirectoryEntry entry2 = new DirectoryEntry( rootdse, domainusername, password);

//create a directorysearcher object off the directoryentry object

DirectorySearcher mysearch = new DirectorySearcher(entry2);

//find the username under the person category

mysearch.Filter= “(&(anr=”+ uname + “)(objectCategory=person))”;

//we want to load the memberOf property


//find an instance

SearchResult result = mysearch.FindOne();

String dn;

int equalsIndex, commaIndex;

int propertyCount = result.Properties[“memberOf”].Count;

//enumerate through the properties loaded

for(int propertyCounter = 0; propertyCounter < propertyCount; propertyCounter++)


dn = (String)result.Properties[“memberOf”][propertyCounter];

equalsIndex = dn.IndexOf(“=”, 1);

commaIndex = dn.IndexOf(“,”, 1);

if(-1 == equalsIndex)


return null;


//create a list seperated by | character

groupNames.Append(dn.Substring((equalsIndex + 1), (commaIndex – equalsIndex) – 1));




catch(Exception ex)


throw new Exception(“Error obtaining group names. ” + ex.Message);


return groupNames.ToString();






<?xml version=”1.0″ encoding=”utf-8″ ?>




<compilation defaultLanguage=”c#” debug=”true” />

<customErrors mode=”RemoteOnly” />

<authentication mode=”Forms”>

<forms loginUrl=”logon.aspx” name=”adAuthCookie” timeout=”60″ path=”/”>




<deny users=”?” />

<allow users=”*” />


<identity impersonate=”true” />

<trace enabled=”false” requestLimit=”10″ pageOutput=”false” traceMode=”SortByTime” localOnly=”true” />

<sessionState mode=”InProc” stateConnectionString=”tcpip=″ sqlConnectionString=”data source=;Trusted_Connection=yes”  cookieless=”false” timeout=”20″ />

<globalization requestEncoding=”utf-8″  responseEncoding=”utf-8″ />


<add verb=”*” path=”*.sqlx” type=”ProAspNet.CS.Ch23.QueryHandler,QueryHandler”/>






private void btnLogon_Click(object sender, System.EventArgs e)


// Path to you LDAP directory server.

// Contact your network administrator to obtain a valid path.

string adPath = “LDAP://,DC=com”;

LDAPAuthentication adAuth = new LDAPAuthentication(adPath);



if(true == adAuth.IsAuthenticated(txtDomainName.Text,




string groups = adAuth.GetGroups(adAuth);

// Create the authetication ticket

FormsAuthenticationTicket authTicket =

new FormsAuthenticationTicket(1, // version




false, groups);

// Now encrypt the ticket.

string encryptedTicket = FormsAuthentication.Encrypt(authTicket);

// Create a cookie and add the encrypted ticket to the

// cookie as data.

HttpCookie authCookie =

new HttpCookie(FormsAuthentication.FormsCookieName,


// Add the cookie to the outgoing cookies collection.


// Redirect the user to the originally requested page





lblError.Text = “Authentication failed, check username and password. Path is : ” + adPath + ” “;



catch(Exception ex)


lblError.Text = “Error authenticating. ” + ex.Message;





// Extract the forms authentication cookie

string cookieName = FormsAuthentication.FormsCookieName;

HttpCookie authCookie = Context.Request.Cookies[cookieName];

if(null == authCookie)


// There is no authentication cookie.



FormsAuthenticationTicket authTicket = null;



authTicket = FormsAuthentication.Decrypt(authCookie.Value);


catch(Exception ex)


// Log exception details (omitted for simplicity)



if (null == authTicket)


// Cookie failed to decrypt.



// When the ticket was created, the UserData property was assigned a

// pipe delimited string of group names.

String[] groups = authTicket.UserData.Split(new char[]{‘|’});

// Create an Identity object

GenericIdentity id = new GenericIdentity(authTicket.Name,


// This principal will flow throughout the request.

for (int i=0;i<groups.Length;i++)


string cn = “CN=”;


groups[i]=groups[i].Substring(0, groups[i].IndexOf(“,”));


GenericPrincipal principal = new GenericPrincipal(id, groups);

// Attach the new principal object to the current HttpContext object

Context.User = principal;


Comments (5)

  1. rumen stankov (a) telerik com says:

    Thanks a lot for the code! I’ve spent a few minutes trying to crack this exact problem yesterday but had hard times even to get started.

    Is this the only way to proceed:

    mysearch.Filter= "(&(anr="+ uname + ")(objectCategory=person))";

    .NET style, I would have expected strongly typed properties for filter, i.e. mysearch.Fiter.Category = Category.Person, so I can get my job done without learning ActiveDirectory format strings.

    Thanks for the info once again.

  2. Can a similar technique to authenticate users be done (and/or read/write their user properties) without AD or AD/AM on a Windows 2000 Server?

    The following code will fail when the properties are enumerated even if the username and password given are those of an administrator:

    string path = @"WinNT://" + SystemInformation.ComputerName;

    string su = "admin";

    string pass = "adminpass";

    DirectoryEntry objDirEnt = new DirectoryEntry(path,su,pass);

    this.lblMessage2.Text = objDirEnt.Name + " = " + objDirEnt.Path + " = " + objDirEnt.SchemaClassName;

    foreach(String Key in objDirEnt.Properties.PropertyNames) {

    this.lblMessage2.Text += Key+"<br />";

    foreach(Object objValue in objDirEnt.Properties[Key]) {

    this.lblMessage2.Text += objValue+"<br />";



  3. Russel Krause says:

    I found a solution to my previous posting.

    We are building a ASP.NET website to manage adding/updating/deleting Windows/SQL/IIs users on-line. I needed a way to have the ASP.NET process "impersonate" an administrator to perform many of the the Windows operations. I found a solution provided by Robert Chartier of Santra Technology ( as described in his paper at which I have repeated below (with full credit to Robert Chartier). Email me

    Rob uses two methods from InteropServices advapi32.dll two define two of his own working methods impersonateValidUser() and undoImpersonation() which you can find in his referenced article:

    #region setup impersonation via interop

    //need to import from COM via InteropServices to do the impersonation when saving the details

    public const int LOGON32_LOGON_INTERACTIVE = 2;

    public const int LOGON32_PROVIDER_DEFAULT = 0;

    System.Security.Principal.WindowsImpersonationContext impersonationContext;

    [DllImport("advapi32.dll", CharSet=CharSet.Auto)]public static extern int LogonUser(String lpszUserName,

    String lpszDomain,String lpszPassword,int dwLogonType,int dwLogonProvider,ref IntPtr phToken);

    [DllImport("advapi32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto, SetLastError=true)]public

    extern static int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);


    And impersonates the administrator temporarily while making the prtected AD calls:

    if(impersonateValidUser(this.LoginUsername, this.DomainName, this.loginPassword)) {

    //Insert your code that runs under the security context of a specific user here.

    //don’t forget to undo the impersonation


    } else {

    //Your impersonation failed. Therefore, include a fail-safe mechanism here.


    Happy trails!