MSDN Deep Dive: Building Killer ASP.NET Applications...
Lot's of requests for the code from the Forms Login demo... here it is...
LDAPAuth.cs:
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)
{
_path=path;
}
public bool IsAuthenticated(string domain, string username, string pwd)
{
string domainAndUsername = domain + @"\" + username;
uname=username;
domainusername=domainAndUsername;
password = pwd;
rootdse=_path;
DirectoryEntry entry = new DirectoryEntry( _path, domainAndUsername, pwd);
try
{
// Bind to the native AdsObject to force authentication.
Object obj = entry.NativeObject;
DirectorySearcher search = new DirectorySearcher(entry);
search.Filter = "(SAMAccountName=" + username + ")";
search.PropertiesToLoad.Add("CN");
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();
try
{
//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
mysearch.PropertiesToLoad.Add("memberOf");
//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));
groupNames.Append("|");
}
}
catch(Exception ex)
{
throw new Exception("Error obtaining group names. " + ex.Message);
}
return groupNames.ToString();
}
}
}
web.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation defaultLanguage="c#" debug="true" />
<customErrors mode="RemoteOnly" />
<authentication mode="Forms">
<forms loginUrl="logon.aspx" name="adAuthCookie" timeout="60" path="/">
</forms>
</authentication>
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
<identity impersonate="true" />
<trace enabled="false" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" />
<sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false" timeout="20" />
<globalization requestEncoding="utf-8" responseEncoding="utf-8" />
<httpHandlers>
<add verb="*" path="*.sqlx" type="ProAspNet.CS.Ch23.QueryHandler,QueryHandler"/>
</httpHandlers>
</system.web>
</configuration>
Logon.aspx.cs:
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://mycompany.com/DC=mycompany,DC=com";
LDAPAuthentication adAuth =
new LDAPAuthentication(adPath);
try
{
if(true == adAuth.IsAuthenticated(txtDomainName.Text,
txtUserName.Text,
txtPassword.Text))
{
string groups = adAuth.GetGroups(adAuth);
// Create the authetication ticket
FormsAuthenticationTicket authTicket =
new FormsAuthenticationTicket(1, // version
txtUserName.Text,
DateTime.Now,
DateTime.Now.AddMinutes(60),
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,
encryptedTicket);
// Add the cookie to the outgoing cookies collection.
Response.Cookies.Add(authCookie);
// Redirect the user to the originally requested page
Response.Redirect(FormsAuthentication.GetRedirectUrl(txtUserName.Text,
false));
}
else
{
lblError.Text = "Authentication failed, check username and password. Path is : " + adPath + " ";
}
}
catch(Exception ex)
{
lblError.Text = "Error authenticating. " + ex.Message;
}
}
global.asax.cs:
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if(null == authCookie)
{
// There is no authentication cookie.
return;
}
FormsAuthenticationTicket authTicket =
null;
try
{
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch(Exception ex)
{
// Log exception details (omitted for simplicity)
return;
}
if (null == authTicket)
{
// Cookie failed to decrypt.
return;
}
// 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,
"LdapAuthentication");
// This principal will flow throughout the request.
for (int i=0;i<groups.Length;i++)
{
string cn = "CN=";
groups[i]=groups[i].Substring(3);
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
- Anonymous
April 10, 2004
The comment has been removed - Anonymous
May 14, 2004
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 />";
}
} - Anonymous
May 22, 2004
The comment has been removed - Anonymous
June 13, 2009
PingBack from http://fancyporchswing.info/story.php?id=403 - Anonymous
June 16, 2009
PingBack from http://fixmycrediteasily.info/story.php?id=2796