Friday, 9 March 2012

Login failure when accessing Active Directory

I hate wasting time, especially when it’s because of an obvious error that’s staring me in the face but I just can’t see it. Well, it happened today so I thought I’d make a note so it never happens again. Take the following code:

var principalContext = new PrincipalContext(ContextType.Domain, domain);
var validated = principalContext.ValidateCredentials(userName, password);
var userPrincipal = UserPrincipal.FindByIdentity(principalContext, userName);

My problem was that an exception was being thrown on the last line. “How is this possible?” I asked myself. “The call to ValidateCredentials worked so why the error on FindByIdentity?”

Firstly, the exception:

Exception thrown autenticating the user.
System.DirectoryServices.DirectoryServicesCOMException (0x8007052E): Logon failure: unknown user name or bad password.

   at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail)
   at System.DirectoryServices.DirectoryEntry.Bind()
   at System.DirectoryServices.DirectoryEntry.get_AdsObject()
   at System.DirectoryServices.PropertyValueCollection.PopulateList()
   at System.DirectoryServices.PropertyValueCollection..ctor(DirectoryEntry entry, String propertyName)
   at System.DirectoryServices.PropertyCollection.get_Item(String propertyName)
   at System.DirectoryServices.AccountManagement.PrincipalContext.DoLDAPDirectoryInitNoContainer()
   at System.DirectoryServices.AccountManagement.PrincipalContext.DoDomainInit()
   at System.DirectoryServices.AccountManagement.PrincipalContext.Initialize()
   at System.DirectoryServices.AccountManagement.PrincipalContext.get_QueryCtx()
   at System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithTypeHelper(PrincipalContext context, Type principalType, Nullable`1 identityType, String identityValue, DateTime refDate)
   at System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithType(PrincipalContext context, Type principalType, String identityValue)
   at System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(PrincipalContext context, String identityValue)
   at GL.AnglianWater.WIRM.Core.Membership.LdapAuthentication.Authenticate(String userName, String password, String domain, String requiredGroup)

Drilling into the exception details in Visual Studio 2010 yielded a bit more information:

ExtendedError = -2146893044
ExtendedErrorMessage = "8009030C: LdapErr: DSID-0C0904DC, comment: AcceptSecurityContext error, data 52e, v1db1"

After scratching my head for an age the solution turned out to be so obvious as to be embarrassing; simply create the PrincipleContext by passing in the username and password too:

var principalContext = new PrincipalContext(ContextType.Domain, domain, userName, password);
var validated = principalContext.ValidateCredentials(userName, password);
var userPrincipal = UserPrincipal.FindByIdentity(principalContext, userName);

Doh!