The code below works on my development machine when debugging in Visual Studio. The development machine is on a different domain than the staging and production servers but creating the principal context with a username and password seemed to solve the issues I had there.
When run on the staging server ValidateCredentials passes but the FindByIdentity() calls fails with the stack trace below.
The IIS pool is running as ApplicationPoolIdentity but I don't think that should matter as ValidateCredentials works (so contacting the domain controller works) and the PrincipalContext is created with a valid username and password for the domain (so the user the pool runs as shouldn't matter).
I found suggestions to use HostingEnvironment.Impersonate() but I'm pretty sure that's not an option with .Net 5/Core and as I'm passing an explicit username and password to the PrincipalContext it shouldn't be in play anyway.
I found https://stackoverflow.com/a/39118337/ and I can try creating a new app pool and moving the site there but I don't like "black magic" fixes and think it's rather unlikely to work.
using var principalContext = new PrincipalContext(ContextType.Domain, _activeDirectoryConfiguration.ControllerNameOrIp, username, password);
if (!principalContext.ValidateCredentials(username, password))
{
_logger.LogWarning(ErrorMessages.UNABLE_TO_AUTHENTICATE + " {0}", username);
errors.Add("", ErrorMessages.UNABLE_TO_AUTHENTICATE);
return (null, errors);
}
IEnumerable<string> userGroups;
UserPrincipal adUser;
adUser = UserPrincipal.FindByIdentity(principalContext, username);
userGroups = adUser.GetAuthorizationGroups().Select(x => x.Name).ToList();
DirectoryEntry dirEntry = (DirectoryEntry)adUser.GetUnderlyingObject();
string office = dirEntry.Properties["physicalDeliveryOfficeName"].Value.ToString();
Stack Trace:
System.Runtime.InteropServices.COMException (0x80005000): Unknown error (0x80005000)
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, IdentityType identityType, String identityValue)
at System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(PrincipalContext context, IdentityType identityType, String identityValue)
at XXX.ActiveDirectoryService.Authenticate(String username, String password) in XXX\Services\ActiveDirectoryService.cs:line 55
at XXX.AuthService.Authenticate(String username, String password) in XXX\Services\AuthService.cs:line 59