PrincipalContext.ValidateCredentials always returns FALSE

Billy Logan picture Billy Logan · Mar 2, 2010 · Viewed 15.5k times · Source

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?

Answer

RobSiklos picture RobSiklos · Jul 17, 2015

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.