Why does the following code lock the account out after one unsuccessful attempt when the policy in AD is set to three attempts? Is there a better way of checking credentials programmatically against AD.
private bool Authenticate(string userName,
string password, string domain)
{
bool authentic = false;
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain,
userName, password);
object nativeObject = entry.NativeObject;
authentic = true;
}
catch (DirectoryServicesCOMException) { }
return authentic;
}
Working fine when the correct credentials are entered...
Thanks,
You might find what you need in this article:
http://www.codeproject.com/Articles/90142/Everything-in-Active-Directory-via-Csharp-NET-3-5-.aspx
specifically the ValidateCredentials method.
Related
What I have is a function that allows a domain user to be authenticated against their LDAP credentials. However, it works long as I hard-code a known password as a raw string... which is a no-no, of course. I wish to pass in a string value received from a TextBox I have set up. Here is the function:
public static bool fnValLDAPCreds()
{
bool validation;
try
{
LdapConnection ADConn = new LdapConnection(new LdapDirectoryIdentifier((string)null, false, false));
NetworkCredential NetCred = new NetworkCredential(Environment.UserName, "Password123", Environment.UserDomainName);
ADConn.Credential = NetCred;
ADConn.AuthType = AuthType.Negotiate;
// the user's authenticated here; creds used to login on the domain controller.
ADConn.Bind(NetCred);
validation = true;
MessageBox.Show("You were successfully authenticated against AD using LDAP!");
}
catch (LdapException)
{
validation = false;
MessageBox.Show("Your login was unsuccesful. Try a different set of credentials.");
}
return validation;
}
What I've tried to do was substitute in a value from my TextBox, but since it lies in the static bool I have not been successful with making any external references to a control in the current context. I'm calling this function in button handler to fire it off. How can I swap in a string DomPassWord variable that gets its value from the textbox I have setup to obtain it?
NetworkCredential NetCred = new NetworkCredential(Environment.UserName, DomPassWord, Environment.UserDomainName); is what I'm striving for, as I can securely match a password in the domain with no hard-coding, using something like DomPassWord = txtUserPW.Text. Tried the SecureString route, but was unsuccessful in that regard as well. Any ideas?
You cannot access text boxes inside a static method, since they aren't static fields (at least it looks like it from what you've written).
But you can simply pass your arguments to your method. Change it to something like this:
public void ButtonClick(object sender, EventArgs args)
{
// bool valid = fnValLDAPCreds(Environment.UserName, "Password123", Environment.UserDomainName);
bool valid = fnValLDAPCreds(txtUserName.Text, txtUserPW.Text, Environment.UserDomainName);
}
public static bool fnValLDAPCreds(string username, string password, string domain)
{
try
{
LdapConnection ADConn = new LdapConnection(new LdapDirectoryIdentifier((string)null, false, false));
NetworkCredential NetCred = new NetworkCredential(username, password, domain);
ADConn.Credential = NetCred;
ADConn.AuthType = AuthType.Negotiate;
// the user's authenticated here; creds used to login on the domain controller.
ADConn.Bind(NetCred);
MessageBox.Show("You were successfully authenticated against AD using LDAP!");
return true;
}
catch (LdapException)
{
MessageBox.Show("Your login was unsuccesful. Try a different set of credentials.");
return false;
}
}
Kinda tangential, but have you thought about AuthType.Ntlm? If all you're doing this for is to make sure user 'Charlie' is actually Charlie by making him type in his password? Then you're on the right track. But if you're trying to connect in to AD using the current user credentials as a way of getting to AD itself? Then you might want to take a look at
ADConn.AuthType = AuthType.Ntlm;
... and letting windows handle this for you (no need to have the user type in a password at all - it'll use their current windows credentials.)
I want to know if there's a way to validate domain credential and make sure we don't use the Cached Domain Credential ?
I use this to validate the credential :
bool valid = false;
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
valid = context.ValidateCredentials( username, password );
}
The problem is when I change the password, the old password is still working.
EDIT : If you force the password to be reset, the cached domain credential will not be use. But between the moment we force the reset, and moment the user reset the password, the old password will still work.
Question already has an answer Why does Active Directory validate last password?
Solution is to use a Kerberos authentication.
The following code shows how you can perform credential validation using only Kerberos. The authentication method at use will not fall back to NTLM in the event of failure.
private const int ERROR_LOGON_FAILURE = 0x31;
private bool ValidateCredentials(string username, string password, string domain)
{
NetworkCredential credentials
= new NetworkCredential(username, password, domain);
LdapDirectoryIdentifier id = new LdapDirectoryIdentifier(domain);
using(LdapConnection connection = new LdapConnection(id, credentials, AuthType.Kerberos))
{
connection.SessionOptions.Sealing = true;
connection.SessionOptions.Signing = true;
try
{
connection.Bind();
}
catch (LdapException lEx)
{
if (ERROR_LOGON_FAILURE == lEx.ErrorCode)
{
return false;
}
throw;
}
return true;
}
you might try something like this
try
{
using (var directoryEntry = new DirectoryEntry(ldapPath, userName, password))
{
var invocation = directoryEntry.NativeObject;
return true;
}
}
catch (Exception ex)
{
return false;
}
I am using the standard Simple Membership model for login via forms in my application. I would like to provide the possibility to login via AD as an alternative.
When logging in via AD, the process should be as follows:
Check that AD authenticates the user, but do not use the information for the principal.
Check if any local user exists with the provided Active Directory username (I have a property on my UserProfile model named ActiveDirectoryID).
If it exists, perform a local login using the local username for this UserProfile.
The problem: I cannot retrieve the local password, so in order to login locally after AD authentication, I need to be able to force the login without the password.
I've considered the following strategies:
Create an extension method for Websecurity to allow Websecurity.Login(string username)
Somehow set the logged in user manually, without implicating Websecurity.
Is this doable / feasible? Is it possible for the framework to create the necessary auth cookie without the plaintext password? And how would I do this?
SOLUTION:
This ended being the correct solution:
public ActionResult ActiveDirectoryLogin(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
try
{
DirectoryEntry entry = new DirectoryEntry("LDAP://DC=MyIntranet,DC=MyCompany,DC=com", model.UserName, model.Password);
object NativeObject = entry.NativeObject;
var internalUser = db.UserProfiles.Where(x => x.ActiveDirectoryID == model.UserName).SingleOrDefault();
if (internalUser != null)
{
FormsAuthentication.SetAuthCookie(internalUser.UserName, model.RememberMe);
return RedirectToLocal(returnUrl);
}
}
catch (DirectoryServicesCOMException)
{
// No user existed with the given credentials
}
catch (InvalidOperationException)
{
// Multiple users existed with the same ActiveDirectoryID in the database. This should never happen!
}
}
return RedirectToAction("Login");
}
This is all that the Websecurity.Login method does:
public static bool Login(string userName, string password, bool persistCookie = false)
{
WebSecurity.VerifyProvider();
bool flag = Membership.ValidateUser(userName, password);
if (flag)
{
FormsAuthentication.SetAuthCookie(userName, persistCookie);
}
return flag;
}
You can write your own method that authenticates against AD and then looks up the user name and the does sets the auth cookie something like:
public static bool MyLogin(string userName, string password, bool persistCookie = false)
{
bool flag = CheckADUser(userName, password);
if (flag)
{
string mappedUsername = GetMappedUser(userName);
if(mappedUsername != "")
{
FormsAuthentication.SetAuthCookie(userName, persistCookie);
}
else
{
flag = false;
}
}
return flag;
}
Hope this helps.
I'm dealing with two domains - one is a trusted domain. There may be a JohnSmith on one domain and another JohnSmith on the other. Both of these people need to log into my application.
My problem: it doesn't matter which domain I pass in - this code returns true! How do I know which JohnSmith is logging in?
static public bool CheckCredentials(
string userName, string password, string domain)
{
using (var context = new PrincipalContext(ContextType.Domain, domain))
{
return context.ValidateCredentials(userName, password);
}
}
The ValidateCredentials works with userPrincipalName you perhaps can try to build the first parameter (username) combining the login and the domain to create the username JohnSmith#dom1.com versus JohnSmith#dom2.com.
You can always retrieve the full DN of the user who has logged in using
UserPrincipal up = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, userName);
up.UserPrincipalName // shows user#domain.com
up.DistinguishedName // shows CN=Surname,OU=group,DC=domain,DC=com
up.SamAccountName // shows login name
Use the up.SamAccountName to subsequent calls to ValidateCredentials including the domain name - you can't have 2 users who log in using the same sAMAccountName after all!
The DistinguishedName will definitely show you which JohnSmith logged in.
Based on JPBlanc's answer, I've re-written my code. I've also added a try/catch in case a bogus domain is passed in.
static public bool CheckCredentials(
string userName, string password, string domain)
{
string userPrincipalName = userName + "#" + domain + ".com";
try
{
using (var context = new PrincipalContext(ContextType.Domain, domain))
{
return context.ValidateCredentials(userPrincipalName, password);
}
}
catch // a bogus domain causes an LDAP error
{
return false;
}
}
The accepted answer will fail with Domains that contain different email addresses within them. Example:
Domain = Company
User1 = employee#department1.com (under company Domain)
User2 = employee2#Department2.com (under company Domain)
The provided answer will return false using:
userName = "employee";
domain = "company";
string userPrincipalName = userName + "#" + domain + ".com";
The correct way to encompass users across domains is:
string userPrincipalName = userName + "#" + domain;
without the .com portion it searches the user AT that domain instead of searching for an email within a global domain.
i need to verify if the password is correct for a user.
i have this code:
private bool checkOldPasswordValid(string password, string username)
{
using (DirectoryEntry entry = new DirectoryEntry("WinNT://" + Environment.MachineName + ",computer"))
{
entry.Username = username;
entry.Password = password;
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(objectclass=user)";
try
{
searcher.FindOne();
}
catch (Exception ex)
{
return false;
}
return true;
}
}
but then directory searcher is not supported with WinNt, so i found another way to loop through all records.
foreach (DirectoryEntry dc in entry.Children)
{
// prints the name
System.Diagnostics.Debug.WriteLine(dc.Name);
}
but this just gets the name and doesnt verify the password.
please help . thanks
To autenticate against LDAP or WinNT, you need no DirectorySearcher. You only need to get the NativeObject from your DirectoryEntry instance. Here's a code sample that might guide you through the way.
public bool Authenticate(string username, string password, string domain) {
bool authenticated = false;
using (DirectoryEntry entry = new DirectoryEntry(#"WinNT://" + domain, username, password) {
try {
object nativeObject = entry.NativeObject;
authenticated = true;
} catch (DirectoryServicesCOMException ex) {
}
}
return authenticated;
}
This code will return either a user is authentic or not. Once you can get the NativeObject property using this DirectoryEntry class instance, this means that the AD (or local computer) used impersonation to get this object. If you get the object without having a thrown exception, this means that the AD (or local computer) was able to authenticate the impersonnated user.
While you can use the currently authenticated user by specifying no username and password, but only the domain (or local computer), by specifying a username and password, you say you want to use impersonnation, so the security infrastructure will use the given username and password to try to retrieve the NativeObject property from this DirectoryEntry class instance.
To authenticate against the AD, just replace the "WinNT://" for "LDAP://".
You can use DirectoryEntry itself.
See the example here: http://support.microsoft.com/kb/316748
Why are you using WinNT:// anyways?