C# – How to validate domain credentials

authenticationcSecuritywindows

I want to validate a set of credentials against the domain controller. e.g.:

Username: STACKOVERFLOW\joel
Password: splotchy

Method 1. Query Active Directory with Impersonation

A lot of people suggest querying the Active Directory for something. If an exception is thrown, then you know the credentials are not valid – as is suggested in this stackoverflow question.

There are some serious drawbacks to this approach however:

  1. You are not only authenticating a domain account, but you are also doing an implicit authorization check. That is, you are reading properties from the AD using an impersonation token. What if the otherwise valid account has no rights to read from the AD? By default all users have read access, but domain policies can be set to disable access permissions for restricted accounts (and or groups).

  2. Binding against the AD has a serious overhead, the AD schema cache has to be loaded at the client (ADSI cache in the ADSI provider used by DirectoryServices). This is both network, and AD server, resource consuming – and is too expensive for a simple operation like authenticating a user account.

  3. You're relying on an exception failure for a non-exceptional case, and assuming that means invalid username and password. Other problems (e.g. network failure, AD connectivity failure, memory allocation error, etc) are then mis-intrepreted as authentication failure.

Method 2. LogonUser Win32 API

Others have suggested using the LogonUser() API function. This sounds nice, but unfortunately the calling user sometimes needs a permission usually only given to the operating system itself:

The process calling LogonUser requires
the SE_TCB_NAME privilege. If the
calling process does not have this
privilege, LogonUser fails and
GetLastError returns
ERROR_PRIVILEGE_NOT_HELD.

In some
cases, the process that calls
LogonUser must also have the
SE_CHANGE_NOTIFY_NAME privilege
enabled; otherwise, LogonUser fails
and GetLastError returns
ERROR_ACCESS_DENIED. This privilege is
not required for the local system
account or accounts that are members
of the administrators group. By
default, SE_CHANGE_NOTIFY_NAME is
enabled for all users, but some
administrators may disable it for
everyone.

Handing out the "Act as a part of the operating system" privilege is not something you want to do willy-nilly – as Microsoft points out in a knowledge base article:

…the process that is calling
LogonUser must have the SE_TCB_NAME
privilege (in User Manager, this is
the "Act as part of the Operating
System
" right). The SE_TCB_NAME
privilege is very powerful and
should not be granted to any arbitrary user just so that they can
run an application
that needs to
validate credentials.

Additionally, a call to LogonUser() will fail if a blank password is specified.


What is the proper way to authenticate a set of domain credentials?


I happen to be calling from managed code, but this is a a general Windows question. It can be assumed that the customers have the .NET Framework 2.0 installed.

Best Answer

C# in .NET 3.5 using System.DirectoryServices.AccountManagement.

 bool valid = false;
 using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
 {
     valid = context.ValidateCredentials( username, password );
 }

This will validate against the current domain. Check out the parameterized PrincipalContext constructor for other options.