A little late but I'll throw this out there.
If you want to REALLY be able to determine the specific reason that an account is failing authentication (there are many more reasons other than wrong password, expired, lockout, etc.), you can use the windows API LogonUser. Don't be intimidated by it - it is easier than it looks. You simply call LogonUser, and if it fails you look at the Marshal.GetLastWin32Error() which will give you a return code that indicates the (very) specific reason that the logon failed.
However, you're not going to be able to call this in the context of the user you're authenticating; you're going to need a priveleged account - I believe the requirement is SE_TCB_NAME (aka SeTcbPrivilege) - a user account that has the right to 'Act as part of the operating system'.
//Your new authenticate code snippet:
try
{
if (!LogonUser(user, domain, pass, LogonTypes.Network, LogonProviders.Default, out token))
{
errorCode = Marshal.GetLastWin32Error();
success = false;
}
}
catch (Exception)
{
throw;
}
finally
{
CloseHandle(token);
}
success = true;
if it fails, you get one of the return codes (there are more that you can look up, but these are the important ones:
//See http://support.microsoft.com/kb/155012
const int ERROR_PASSWORD_MUST_CHANGE = 1907;
const int ERROR_LOGON_FAILURE = 1326;
const int ERROR_ACCOUNT_RESTRICTION = 1327;
const int ERROR_ACCOUNT_DISABLED = 1331;
const int ERROR_INVALID_LOGON_HOURS = 1328;
const int ERROR_NO_LOGON_SERVERS = 1311;
const int ERROR_INVALID_WORKSTATION = 1329;
const int ERROR_ACCOUNT_LOCKED_OUT = 1909; //It gives this error if the account is locked, REGARDLESS OF WHETHER VALID CREDENTIALS WERE PROVIDED!!!
const int ERROR_ACCOUNT_EXPIRED = 1793;
const int ERROR_PASSWORD_EXPIRED = 1330;
The rest is just copy/paste to get the DLLImports and values to pass in
//here are enums
enum LogonTypes : uint
{
Interactive = 2,
Network =3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkCleartext = 8,
NewCredentials = 9
}
enum LogonProviders : uint
{
Default = 0, // default for platform (use this!)
WinNT35, // sends smoke signals to authority
WinNT40, // uses NTLM
WinNT50 // negotiates Kerb or NTLM
}
//Paste these DLLImports
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonTypes logonType,
LogonProviders logonProvider,
out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);