I have an MVC application that needs to login and verify a user against Active Directory. I am using the PrincipalContext.ValidateCredentials
method but always get a authentication of false
.
Connecting to the Server is fine. The problem seems to occur in the ValidateCredentials
.
Here is my code:
public static bool IsAuthenticated(string domain, string username, string pwd) {
bool IsAuthenticated = false;
try {
PrincipalContext insPrincipalContext =
new PrincipalContext(ContextType.Domain, domain, "DC=c1w,DC=com");
username = "c1w\\" + username;
IsAuthenticated = insPrincipalContext.ValidateCredentials(username, pwd);
}
catch (Exception ex)
{
// Rethrow this exception
ExceptionPolicy.HandleException(ex, "Exception Policy");
}
return IsAuthenticated;
}
Anyone know why this would be happening?
Here's how ValidateCredentials(string, string)
works: First, it tries to authenticate with the Negotiate
, Signing
, and Sealing
context options. If this fails, it tries again with SimpleBind
and SecureSocketLayer
.
The problem is that the NT4 (AKA "legacy", AKA "down-level name") format (DOMAIN\UserName
, or more correctly, NetBiosName\SamAccountName
) doesn't work with Negotiate. But it does work with SimpleBind.
So what's probably happening when calling the 2-parameter ValidateCredentials()
method, is that it first fails using Negotiate because it doesn't like the NT4 format, and then fails again when using simple bind.
During my own testing, I've found that the reason why it fails even after falling back to using simple bind is that it's not only using SimpleBind. It's using SimpleBind
plus SecureSocketLayer
. This means that it will still fail if the Active Directory server isn't set up correctly to use SSL (a common scenario for test environments).
As was mentioned in one of the comments, you NEVER, NEVER want to use SimpleBind
by itself (without SecureSocketLayer
), otherwise your passwords are sent over the network in plain text.
In the wild, I've seen that some Active Directory systems don't allow the use of simple binds at all, so you must make it work with Negotiate.
I've found 2 ways to deal with this problem:
1) If everything is happening on the same domain, you should be able to call ValidateCredentials
with only the username (SAM account name), leaving out the "DOMAIN\" part. Then, it will work properly the first time with Negotiate.
2) If the domain part is important because there may be multiple domains involved (i.e. Domain1\UserA
and Domain2\UserA
are different people), then it gets a bit more complicated. In this case what I ended up doing was translating the NT4 name (DOMAIN\User) to "user principal name" format (e.g. [email protected]
). There are a couple different ways to do this. The easiest is probably to use the 3-parameter overload of UserPrincipal.FindByIdentity()
, and then grab the value of the UserPrincipalName
property on the result. Another way would be to use a DirectorySearcher
and query LDAP://domain
for the userPrincipalName
property of the user with the matching sAMAccountName
value. Note: this solution will only work if all the domains involved are in the same forest.