When submitting valid credentials to Active Directory via a ASP.NET web form, the following error message is returned: "The specified directory service attribute or value does not exist."
Code for LDAP Authentication:
using System;
using System.Text;
using System.Collections;
using System.DirectoryServices;
namespace FormsAuth
{ public class LdapAuthentication { private string _path; private string _filterAttribute; public LdapAuthentication(string path) { _path = path; } public bool IsAuthenticated(string domain, string username, string pwd) { string domainAndUsername = "CBHC" + @"\" + username; 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() { DirectorySearcher search = new DirectorySearcher(_path); search.Filter = "(cn=" + _filterAttribute + ")"; search.PropertiesToLoad.Add("memberOf"); StringBuilder groupNames = new StringBuilder(); try { SearchResult result = search.FindOne(); int propertyCount = result.Properties["memberOf"].Count; string dn; int equalsIndex, commaIndex; 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; } 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(); } }
}Code on login page:
<script runat=server>
void Login_Click(object sender, EventArgs e)
{ string adPath = "LDAP://server/DC=domain,DC=loc"; //Path to your LDAP directory server LdapAuthentication adAuth = new LdapAuthentication(adPath); try { if(true == adAuth.IsAuthenticated("CBHC",txtUsername.Text, txtPassword.Text)) { string groups = adAuth.GetGroups(); //Create the ticket, and add the groups. bool isCookiePersistent = chkPersist.Checked; FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, txtUsername.Text,DateTime.Now, DateTime.Now.AddMinutes(60), isCookiePersistent, groups); //Encrypt the ticket. string encryptedTicket = FormsAuthentication.Encrypt(authTicket); //Create a cookie, and then add the encrypted ticket to the cookie as data. HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket); if(true == isCookiePersistent) authCookie.Expires = authTicket.Expiration; //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."; } } catch(Exception ex) { errorLabel.Text = "Error authenticating. " + ex.Message; }
}
</script>Authentication settings on Web.config for form on IIS:
<authentication mode="Windows"> <forms loginUrl="logon.aspx" name="adAuthCookie" timeout="10" path="/" /> </authentication> <authorization> <deny users="?" /> <allow users="*" /> </authorization> <identity impersonate="true" />Word of note: This does not occur when running the site in Debug; in that situation, it authenticates perfectly and moves on to the Default page. It only occurs when contacting the page live on the IIS server.
31 Answer
I've hit an issue like this once. It may be because you can't retrieve LDAP NativeObject property for authentication. If the exception is thrown right after the object obj = entry.NativeObject; call, check if user has permissions on the domain.
Either debug through your code to see if it is indeed the NativeObject binding that is failing. Or Put a try/catch block around the binding in your IsAuthenticated() function like below. You should see the custom error thrown if it's caused by the issue I'm describing.
try
{ //Bind to the native AdsObject to force authentication. Object obj = entry.NativeObject;
}
catch (System.Runtime.InteropServices.COMException e)
{ if (e.ErrorCode == -2147016694) // -2147016694 - The specified directory service attribute or value does not exist. { throw new Exception("Can't retrieve LDAP NativeObject property"); } throw;
} 6