Reputation: 223
I would like to find out if there is any .Net way to validate an Active Directory user credential even if the user's password is expired or the user has "user must change password at next logon" set. I have tried PrincipalContext.ValidateCredential and this returns false for my user. I also tried Ldap Bind and that does not work either. My purpose is to authenticate the user and then prompt him with a change password dialog if his password is expired or he has to change passwored at next login.
Upvotes: 3
Views: 12036
Reputation: 24449
Inspired by Pedro's answer, here's another simpler way to temporarily "unexpire" the user's password, so that ValidateCredentials
can be used:
var user = UserPrincipal.FindByIdentity(PrincipalContext, username);
user.PasswordNeverExpires = true;
user.Save();
Now this works:
var canLogin = PrincipalContext.ValidateCredentials(username, password);
After verification, the flag can be reset:
user.PasswordNeverExpires = false;
user.Save();
Upvotes: 1
Reputation: 148
I have found absolutely NO WAY to validate a user if the "User must change password at next log on" flag is set.
This is the solution I came up with. Sorry it is in VB, it is inherited code :)
' Authenticate Using the Administrator Account Dim domainAndUserName As String = tmpDomain + "\" + tmpUser Dim entry As DirectoryEntry = New DirectoryEntry(sLDAP_PATH, m_AD_BIND_USERNAME, m_AD_BIND_PASSWORD) Dim ds As DirectorySearcher = New DirectorySearcher(entry) ds.Filter = "(&(objectClass=user)(anr=" + tmpUser + "))" ' Lookup the User Dim user As SearchResult = ds.FindOne() ' Check the User Status ' If the "User must change password at next logon" flag is set ' then we need to clear it before we can test the users credentials If Convert.ToInt64(user.Properties("pwdLastSet").Item(0)) = 0 Then Dim deUser As DirectoryEntry = user.GetDirectoryEntry() deUser.Properties("pwdLastSet")(0) = -1 deUser.CommitChanges() Dim isValidCreds As Boolean = True ' Create Directory Entry for User entry = New DirectoryEntry(sLDAP_PATH, domainAndUserName, Password) ' Attempt to create NativeObject based on credentials ' If it fails the credentials are invalid Try Dim obj As Object = entry.NativeObject() Catch ex As Exception isValidCreds = False End Try ' Reset the flag deUser.Properties("pwdLastSet")(0) = 0 deUser.CommitChanges() ' If the credentials are valid, return '1907' ' if the credentials aren't valid, return '1326' If isValidCreds Then Throw New Exception("1907") ' Password Expired Else Throw New Exception("1326") ' Invalid Login End If ElseIf Convert.ToInt64(user.Properties("userAccountControl").Item(0)) = 514 Then Throw New Exception("1331") ' Account Disabled ElseIf user.Properties("lockoutTime").Count > 0 AndAlso Convert.ToInt64(user.Properties("lockouttime").Item(0)) > 0 Then Throw New Exception("1909") ' Account Locked Out End If
Only thing to be aware of is there is a potential for an issue due to slow replication, i.e., you may set the flag but since the update hasn't replicated to all of your DCs when you attempt to create the 'NativeObject' it may still fail.
Upvotes: 0
Reputation: 1344
We have several AD controllers in our setup and the PrincipalContext.ValidateCredentials method would always return false on the AD controllers on Windows 2003 servers on users with the "user must change password at next logon" checkbox checked.
But on the ones on Windows 2008 R2 servers, it would return true if the creds were valid even if the checkbox was checked.
So I just made sure my code was hitting one of the windows 2008 R2 servers and that did the trick.
I did work on a solution for the 2003 servers (before I realized things would just work on the other ones). Here is the code:
var adContext = new PrincipalContext(ContextType.Domain, adLocation, adContainer, adAdminUsername, adAdminPassword);
var initialValidation = adContext.ValidateCredentials(username, password);
Console.WriteLine("Initial validation returned: " + initialValidation);
if (!initialValidation)
{
// maybe validation failed because "user must change password at next logon".
// let's see if that is the case.
var user = UserPrincipal.FindByIdentity(adContext, username);
if (user.LastPasswordSet == null)
{
// the user must change his password at next logon. So this might be
// why validation returned false
// uncheck the "change password" checkbox and attempt validation again
var deUser = user.GetUnderlyingObject() as DirectoryEntry;
var property = deUser.Properties["pwdLastSet"];
property.Value = -1;
deUser.CommitChanges();
// property was unset, retry validation
adContext.ValidateCredentials(username, password);
Console.WriteLine("Secondary validation returned: " + adContext.ValidateCredentials(username, password));
// re check the checkbox
property.Value = 0;
deUser.CommitChanges();
}
}
Upvotes: 7
Reputation: 2982
This article might help. You can use DirectorySearcher
to find out if a user exists and what the password status is.
Upvotes: 0