Tuesday, June 5, 2012

C# Validate a username and password against Active Directory?


How can I validate a username and password against Active Directory? I simply want to check if a username and password are correct.



Source: Tips4all

11 comments:

  1. If you work on .NET 3.5, you can use the System.DirectoryServices.AccountManagement namespace and easily verify your credentials:

    // create a "principal context" - e.g. your domain (could be machine, too)
    using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN"))
    {
    // validate the credentials
    bool isValid = pc.ValidateCredentials("myuser", "mypassword");
    }


    It's simple, it's reliable, it's 100% C# managed code on your end - what more can you ask for? :-)

    Read all about it here:


    Managing Directory Security Principals in the .NET Framework 3.5
    MSDN docs on System.DirectoryServices.AccountManagement

    ReplyDelete
  2. We do this on our Intranet

    You have to use System.DirectoryServices;

    Here are the guts of the code

    DirectoryEntry adsEntry = new DirectoryEntry(path, strAccountId, strPassword);
    DirectorySearcher adsSearcher = new DirectorySearcher( adsEntry );
    //adsSearcher.Filter = "(&(objectClass=user)(objectCategory=person))";
    adsSearcher.Filter = "(sAMAccountName=" + strAccountId + ")";

    try
    {
    SearchResult adsSearchResult = adsSearcher.FindOne();
    bSucceeded = true;

    strAuthenticatedBy = "Active Directory";
    strError = "User has been authenticated by Active Directory.";
    adsEntry.Close();
    }
    catch ( Exception ex )
    {
    // Failed to authenticate. Most likely it is caused by unknown user
    // id or bad strPassword.
    strError = ex.Message;
    adsEntry.Close();
    }

    ReplyDelete
  3. very simple solution using DirectoryServices:

    using System.DirectoryServices;

    //srvr = ldap server, e.g. LDAP://domain.com
    //usr = user name
    //pwd = user password
    public bool IsAuthenticated(string srvr, string usr, string pwd)
    {
    bool authenticated = false;

    try
    {
    DirectoryEntry entry = new DirectoryEntry(srvr, usr, pwd);
    object nativeObject = entry.NativeObject;
    authenticated = true;
    }
    catch (DirectoryServicesCOMException cex)
    {
    //not authenticated; reason why is in cex
    }
    catch (Exception ex)
    {
    //not authenticated due to some other exception [this is optional]
    }
    return authenticated;
    }


    the NativeObject access is required to detect a bad user/password

    ReplyDelete
  4. Probably easiest way is to PInvoke LogonUser Win32 API.e.g.


    http://www.pinvoke.net/default.aspx/advapi32/LogonUser.html


    MSDN Reference here...


    http://msdn.microsoft.com/en-us/library/aa378184.aspx


    Definitely want to use logon type

    LOGON32_LOGON_NETWORK (3)


    This creates a lightweight token only - perfect for AuthN checks. (other types can be used to build interactive sessions etc.)

    ReplyDelete
  5. Unfortunately there is no "simple" way to check a users credentials on AD.

    With every method presented so far, you may get a false-negative: A user's creds will be valid, however AD will return false under certain circumstances:

    User is required to Change Password at Next Logon.
    User's password has expired.

    ActiveDirectory will not allow you to use LDAP to determine if a password is invalid due to the fact that a user must change password or if their password has expired.

    To determine password change or password expired, you may call Win32:LogonUser(), and check the windows error code for the following 2 constants:


    ERROR_PASSWORD_MUST_CHANGE = 1907
    ERROR_PASSWORD_EXPIRED = 1330

    ReplyDelete
  6. A full .Net solution is to use the classes from the System.DirectoryServices namespace. They allow to query an AD server directly. Here is a small sample that would do this:

    using (DirectoryEntry entry = new DirectoryEntry())
    {
    entry.Username = "here goes the username you want to validate";
    entry.Password = "here goes the password";

    DirectorySearcher searcher = new DirectorySearcher(entry);

    searcher.Filter = "(objectclass=user)";

    try
    {
    searcher.FindOne();
    }
    catch (COMException ex)
    {
    if (ex.ErrorCode == -2147023570)
    {
    // Login or password is incorrect
    }
    }
    }

    // FindOne() didn't throw, the credentials are correct


    This code directly connects to the AD server, using the credentials provided. If the credentials are invalid, searcher.FindOne() will throw an exception. The ErrorCode is the one corresponding to the "invalid username/password" COM error.

    You don't need to run the code as an AD user. In fact, I succesfully use it to query informations on an AD server, from a client outside the domain !

    ReplyDelete
  7. Try this code
    (NOTE: Reported to not work on windows server 2000)

    #region NTLogonUser
    #region Direct OS LogonUser Code
    [DllImport( "advapi32.dll")]
    private static extern bool LogonUser(String lpszUsername,
    String lpszDomain, String lpszPassword, int dwLogonType,
    int dwLogonProvider, out int phToken);

    [DllImport("Kernel32.dll")]
    private static extern int GetLastError();

    public static bool LogOnXP(String sDomain, String sUser, String sPassword)
    {
    int token1, ret;
    int attmpts = 0;

    bool LoggedOn = false;

    while (!LoggedOn && attmpts < 2)
    {
    LoggedOn= LogonUser(sUser, sDomain, sPassword, 3, 0, out token1);
    if (LoggedOn) return (true);
    else
    {
    switch (ret = GetLastError())
    {
    case (126): ;
    if (attmpts++ > 2)
    throw new LogonException(
    "Specified module could not be found. error code: " +
    ret.ToString());
    break;

    case (1314):
    throw new LogonException(
    "Specified module could not be found. error code: " +
    ret.ToString());

    case (1326):
    // edited out based on comment
    // throw new LogonException(
    // "Unknown user name or bad password.");
    return false;

    default:
    throw new LogonException(
    "Unexpected Logon Failure. Contact Administrator");
    }
    }
    }
    return(false);
    }
    #endregion Direct Logon Code
    #endregion NTLogonUser


    except you'll need to create your own custom exception for "LogonException"

    ReplyDelete
  8. www.c-sharpcorner.com has a nice article on how to do this.

    ReplyDelete
  9. If you are stuck with .NET 2.0 and managed code, here is another way that works whith local and domain accounts:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Security;
    using System.Diagnostics;

    static public bool Validate(string domain, string username, string password)
    {
    try
    {
    Process proc = new Process();
    proc.StartInfo = new ProcessStartInfo()
    {
    FileName = "no_matter.xyz",
    CreateNoWindow = true,
    WindowStyle = ProcessWindowStyle.Hidden,
    WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
    UseShellExecute = false,
    RedirectStandardError = true,
    RedirectStandardOutput = true,
    RedirectStandardInput = true,
    LoadUserProfile = true,
    Domain = String.IsNullOrEmpty(domain) ? "" : domain,
    UserName = username,
    Password = Credentials.ToSecureString(password)
    };
    proc.Start();
    proc.WaitForExit();
    }
    catch (System.ComponentModel.Win32Exception ex)
    {
    switch (ex.NativeErrorCode)
    {
    case 1326: return false;
    case 2: return true;
    default: throw ex;
    }
    }
    catch (Exception ex)
    {
    throw ex;
    }

    return false;
    }

    ReplyDelete
  10. Yet another .NET call to quickly authenticate LDAP credentials:

    using System.DirectoryServices;

    using(var DE = new DirectoryEntry(path, username, password)
    {
    try
    {
    DE.RefreshCache(); // This will force credentials validation
    }
    catch (COMException ex)
    {
    // Validation failed - handle how you want
    }
    }

    ReplyDelete
  11. {
    try
    {
    UserDB user = new UserDB();
    if (user.CheckUserLogIn(txtUserName.Text, txtPassword.Text) == true)
    {
    FormsAuthentication.SetAuthCookie(txtUserName.Text, true);
    Session[“UserID”] = txtUserName.Text;
    Response.Redirect(“SearchFlights.aspx”);
    }
    }
    catch (InValidLoginException ex)
    {
    lblMessage.Visible = true;
    lblMessage.Text = ex.Message;
    }
    }

    ReplyDelete