Monday, March 16, 2009

Authenticating Active Directory

Ok, you first must know that Microsoft added a specific namespace for working with ActiveDirectory in .NET 3.5. It is called System.DirectoryServices.AccountManagement. The principal objects in the Account Management API include computer, group and user objects and it provides a means for applications to extend the object model to include custom schema object types.

Now that this is out of the way, I want to take my simple Authenticate, ListUser and ReadUser methods (working fine with the old DirectoryEntry method) and translate them into this new way of doing things.

I did some code and I got this error message: "80075000". That is the actual error Message property! Even more remarkable, a google for 80075000 resulted in only 246 results!! none of them explaining what I did wrong. Apparently, I had sent a string in the format "LDAP://ComputerName" as the computer name. Maybe it helps someone.

But this didn't solve it. I changed it with just the computer name, with or without a "\\" prefix, and I got a more clear text, but just as vague UnauthorizedAccessException: "General access denied error".

I am still working on it, but damn, how can a programmer not think about the error message he passes to other programmers?!

Ok, made it work, here is the code for the user authentication in an Active Directory domain.
Classic DirectoryEntry:

bool authentic = false;
var entry = new DirectoryEntry("LDAP://ComputerName",
"Domain\\username", "password");
object nativeObject = entry.NativeObject;
authentic = true;
catch (DirectoryServicesCOMException ex){}
return authentic;
As you can see, not the most elegant approach.

The .NET 3.5 way:

using (var context = new PrincipalContext(ContextType.Domain,"ComputerName"))
return context.ValidateCredentials("username","password");
Much better, isn't it? Pay attention that in the first case you need the domain in the username and in the second you need it not to be part of the username!

There is a third way (System.DirectoryServices.Protocols.LdapConnection), but seems too complicated to address right now.


Paulo Santos said...

I know it's an old post, but did you manage to get it working?

Because I just got this error and when I googled it I just got, only your website popped up.

Siderite said...

The solution is there. Just use the .NET 3.5 way. If you are not using .Net 3.5, the solution is above, but ... why are you not using .Net 3.5? :)

Paulo Santos said...

I am using 3.5.

The problem in my case, and to register for posterity, is that I was passing an empty string instead of null in the domain name while acessing the local computer.

The way that the PrincipalContext constructor works it requires a NULL. Anything else, even an empty string, will be treated as the computer or domain name.

Siderite said...

Thank you for taking the time to write the solution to your problem, as well. People rarely do that...