Czy zweryfikować nazwę użytkownika i hasło do Active Directory?

526

Jak mogę zweryfikować nazwę użytkownika i hasło do Active Directory? Chcę po prostu sprawdzić, czy nazwa użytkownika i hasło są prawidłowe.

Marc
źródło

Odpowiedzi:

642

Jeśli pracujesz na .NET 3.5 lub nowszym, możesz użyć System.DirectoryServices.AccountManagementprzestrzeni nazw i łatwo zweryfikować swoje poświadczenia:

// 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");
}

To proste, niezawodne, to kod zarządzany w 100% C # po twojej stronie - o co więcej można prosić? :-)

Przeczytaj o tym tutaj:

Aktualizacja:

Jak opisano w tym drugim pytaniu SO (i jego odpowiedziach) , istnieje problem z tym wywołaniem, które prawdopodobnie powraca w Trueprzypadku starych haseł użytkownika. Bądź świadomy tego zachowania i nie zdziw się, jeśli tak się stanie :-) (dzięki @MikeGledhill za zwrócenie na to uwagi!)

marc_s
źródło
36
W mojej domenie musiałem określić pc.ValidateCredentials („mój użytkownik”, „moje hasło”, ContextOptions.Negotiate) lub otrzymałem System.DirectoryServices.Protocols.DirectoryOperationException: Serwer nie może obsłużyć żądań katalogu.
Alex Peck
12
Jeśli hasło wygasło lub konta zostały wyłączone, ValidateCredentials zwróci false. Niestety, nie mówi ci, dlaczego zwrócił fałsz (szkoda, ponieważ oznacza, że ​​nie mogę zrobić czegoś sensownego, jak przekierowanie użytkownika w celu zmiany hasła).
Chris J
64
Uważaj również na konto „Gość” - jeśli konto Gość na poziomie domeny jest włączone, ValidateCredentials zwraca true, jeśli dasz mu nieistniejącego użytkownika. W rezultacie możesz zadzwonić, UserPrinciple.FindByIdentityaby sprawdzić, czy przekazany identyfikator użytkownika istnieje wcześniej.
Chris J
7
@AlexPeck: powodem, dla którego musiałeś to zrobić (tak jak ja), było to, że .NET domyślnie korzysta z następujących technologii: LDAP + SSL, Kerberos, a następnie RPC. Podejrzewam, że RPC jest wyłączony w twojej sieci (dobrze!), A Kerberos tak naprawdę nie jest używany przez .NET, chyba że wyraźnie to określisz ContextOptions.Negotiate.
Brett Veenstra
5
Należy pamiętać, że jeśli użytkownik ZMIENI swoje hasło do usługi Active Directory, ten fragment kodu będzie nadal szczęśliwie uwierzytelniał użytkownika przy użyciu starego hasła AD. Tak, naprawdę. Przeczytaj tutaj: stackoverflow.com/questions/8949501/...
Mike Gledhill
70

Robimy to w naszym intranecie

Musisz użyć System.DirectoryServices;

Oto wnętrzności kodu

using (DirectoryEntry adsEntry = new DirectoryEntry(path, strAccountId, strPassword))
{
    using (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.";
        }
        catch (Exception ex)
        {
            // Failed to authenticate. Most likely it is caused by unknown user
            // id or bad strPassword.
            strError = ex.Message;
        }
        finally
        {
            adsEntry.Close();
        }
    }
}
Posiłek Filanderer
źródło
9
Co umieszczasz na „ścieżce”? Nazwa domeny? Nazwa serwera? Ścieżka LDAP do domeny? Ścieżka LDAP do serwera?
Ian Boyd,
3
Odpowiedź 1: Nie, uruchamiamy go jako usługę internetową, dzięki czemu można go wywoływać z wielu lokalizacji w głównej aplikacji internetowej. Odpowiedź 2: Ścieżka zawiera informacje o LDAP ... LDAP: // DC = nazwa_domeny1, DC = nazwa_domeny2, DC = com
DiningPhilanderer
3
Wydaje się, że może to pozwolić na wstrzyknięcie LDAP. Możesz chcieć uciec lub usunąć nawias w strAccountId
Brain2000
Czy to oznacza, że strPasswordjest przechowywany w LDAP jako zwykły tekst?
Matt Kocaj,
15
Nigdy nie powinno być potrzeby jawnie wywołać Close()na usingzmiennej.
Nyerguds
62

W kilku przedstawionych tutaj rozwiązaniach brakuje możliwości rozróżnienia niewłaściwego użytkownika / hasła od hasła, które należy zmienić. Można to zrobić w następujący sposób:

using System;
using System.DirectoryServices.Protocols;
using System.Net;

namespace ProtocolTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                LdapConnection connection = new LdapConnection("ldap.fabrikam.com");
                NetworkCredential credential = new NetworkCredential("user", "password");
                connection.Credential = credential;
                connection.Bind();
                Console.WriteLine("logged in");
            }
            catch (LdapException lexc)
            {
                String error = lexc.ServerErrorMessage;
                Console.WriteLine(lexc);
            }
            catch (Exception exc)
            {
                Console.WriteLine(exc);
            }
        }
    }
}

Jeśli hasło użytkownika jest nieprawidłowe lub użytkownik nie istnieje, błąd będzie zawierał

„8009030C: LdapErr: DSID-0C0904DC, komentarz: błąd AcceptSecurityContext, dane 52e, v1db1”,

jeśli hasło użytkownika wymaga zmiany, będzie ono zawierać

„8009030C: LdapErr: DSID-0C0904DC, komentarz: błąd AcceptSecurityContext, dane 773, v1db1”

Wartość lexc.ServerErrorMessagedanych stanowi szesnastkową reprezentację kodu błędu Win32. Są to te same kody błędów, które zostałyby zwrócone przez wywołanie wywołania API Win32 LogonUser. Poniższa lista podsumowuje zakres wspólnych wartości z wartościami szesnastkowymi i dziesiętnymi:

525 user not found ​(1317)
52e invalid credentials ​(1326)
530 not permitted to logon at this time (1328)
531 not permitted to logon at this workstation (1329)
532 password expired ​(1330)
533 account disabled ​(1331) 
701 account expired ​(1793)
773 user must reset password (1907)
775 user account locked (1909)
Søren Mors
źródło
2
Niestety niektóre instalacje AD nie zwracają sub-kodu błędu LDAP, co oznacza, że ​​to rozwiązanie nie będzie działać.
Søren Mors,
4
Nie zapomnij dodać niektórych odniesień do projektu: System.DirectoryServicesorazSystem.DirectoryServices.Protocols
TomXP411
3
Moje pytanie brzmi jednak: jak uzyskać nazwę serwera LDAP? Jeśli piszesz aplikację przenośną, nie możesz oczekiwać, że użytkownik pozna lub będzie musiał podać nazwy serwerów AD w każdej sieci.
TomXP411
1
Mam użytkowników, którzy są ograniczeni do logowania się do określonych stacji roboczych; jak określić stację roboczą, dla której próbuję się zalogować? (stacja robocza 1 zawiodłaby z danymi 531, stacja robocza 2 działałaby dobrze, na przykład)
akohlsmith
1
Czuję się dziwnie, ponieważ nie sądzę, abyś miał wystarczająco dużo kredytu. Jest to w pełni zarządzana metoda bez kłopotów z wywołaniem API Win32 w celu ustalenia, czy „użytkownik musi zresetować hasło”, co wyraźnie nie daje żadnej innej odpowiedzi. Czy w tej metodzie jest jakaś luka, która powoduje jej niską stopę aprecjacji? hmm ...
Lionet Chen
34

bardzo proste rozwiązanie za pomocą 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;
}

Dostęp NativeObject jest wymagany do wykrycia złego użytkownika / hasła

Steven A. Lowe
źródło
4
Ten kod jest zły, ponieważ wykonuje również kontrolę autoryzacji (sprawdź, czy użytkownik może czytać informacje o active directory). Nazwa użytkownika i hasło mogą być prawidłowe, ale użytkownik nie może odczytać informacji - i uzyskać wyjątek. Innymi słowy, możesz mieć prawidłową nazwę użytkownika i hasło, ale nadal możesz uzyskać wyjątek.
Ian Boyd
2
właściwie pytam o natywny odpowiednik PrincipleContext- który istnieje tylko w .NET 3.5. Ale jeśli używasz .NET 3.5 lub nowszej, powinieneś użyćPrincipleContext
Ian Boyd
28

Niestety nie ma „prostego” sposobu sprawdzenia poświadczeń użytkowników w AD.

Przy każdej przedstawionej dotychczas metodzie możesz otrzymać wynik fałszywie ujemny: poświadczenia użytkownika będą ważne, jednak AD zwróci fałsz w pewnych okolicznościach:

  • Użytkownik jest zobowiązany do zmiany hasła przy następnym logowaniu.
  • Hasło użytkownika wygasło.

ActiveDirectory nie pozwoli ci użyć LDAP do ustalenia, czy hasło jest nieprawidłowe, ponieważ użytkownik musi zmienić hasło lub jego hasło wygasło.

Aby ustalić zmianę hasła lub hasło wygasło, możesz wywołać Win32: LogonUser () i sprawdzić kod błędu systemu Windows pod kątem następujących 2 stałych:

  • ERROR_PASSWORD_MUST_CHANGE = 1907
  • ERROR_PASSWORD_EXPIRED = 1330
Alan
źródło
1
Czy mogę zapytać, skąd masz te Devinitions dla Expired i Must_Change ... Nie znalazłem ich nigdzie indziej, ale tutaj :)
mabstrei,
Dzięki. Próbowałem dowiedzieć się, w jaki sposób moja walidacja ciągle zwraca wartość false. Stało się tak, ponieważ użytkownik musi zmienić swoje hasło.
Deise Vicentin
22

Prawdopodobnie najłatwiejszym sposobem jest PInvoke LogonUser Win32 API.eg

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

Dokumentacja MSDN tutaj ...

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

Zdecydowanie chcę użyć typu logowania

LOGON32_LOGON_NETWORK (3)

Tworzy to tylko lekki token - idealny do czeków AuthN. (inne typy mogą być używane do tworzenia interaktywnych sesji itp.)

stephbu
źródło
Jak wskazuje @Alan, LogonUser API ma wiele przydatnych cech poza wywołaniem System.DirectoryServices.
stephbu,
3
@cciotti: Nie, to źle. NAJLEPSZY sposób na poprawne uwierzytelnienie kogoś to użycie LogonUserAPI podczas pisania @stephbu. Wszystkie inne metody opisane w tym poście NIE będą działać w 100%. Tylko uwaga, jednak uważam, że aby połączyć się z LogonUser, musisz dołączyć do domeny.
Alan,
@Alan, aby wygenerować dane uwierzytelniające, musisz mieć możliwość połączenia się z domeną poprzez przekazanie prawidłowego konta domeny. Jestem jednak pewien, że twój komputer niekoniecznie musi być członkiem domeny.
stephbu
2
LogonUserAPI wymaga, aby użytkownik miał do działania jako część systemu operacyjnego privelage; co nie jest czymś, co użytkownicy otrzymują - i czymś, czego nie chcesz przyznawać każdemu użytkownikowi w organizacji. ( msdn.microsoft.com/en-us/library/aa378184(v=vs.85).aspx )
Ian Boyd
1
LogonUser potrzebuje Act tylko jako część systemu operacyjnego Windows 2000 i niższych, zgodnie z support.microsoft.com/kb/180548 ... Wygląda czysto dla Server 2003 i wyższych.
Chris J
18

Pełnym rozwiązaniem .Net jest użycie klas z przestrzeni nazw System.DirectoryServices. Pozwalają na bezpośrednie zapytanie do serwera AD. Oto mała próbka, która by to zrobiła:

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

Ten kod łączy się bezpośrednio z serwerem AD przy użyciu podanych poświadczeń. Jeśli dane uwierzytelniające są niepoprawne, seekcher.FindOne () zgłosi wyjątek. ErrorCode to kod odpowiadający błędowi COM „nieprawidłowa nazwa użytkownika / hasło”.

Nie musisz uruchamiać kodu jako użytkownik AD. W rzeczywistości z powodzeniem używam go do wyszukiwania informacji na serwerze AD od klienta spoza domeny!

Mathieu Garstecki
źródło
co z typami uwierzytelniania? myślę, że zapomniałeś o tym w powyższym kodzie. :-) Domyślnie DirectoryEntry.AuthenticationType jest ustawiony na Zabezpieczony, prawda? ten kod nie będzie działał na niezabezpieczonych LDAP (może to być Anonimowy lub Brak). czy mam z tym rację?
jerbersoft
Wadą zapytań do serwera AD jest to, że masz uprawnienia do wysyłania zapytań do serwera AD. Twoje poświadczenie może być prawidłowe, ale jeśli nie masz uprawnień do zapytania AD, pojawi się błąd. Właśnie dlatego powstał tak zwany Fast Bind ; weryfikujesz poświadczenia bez upoważnienia użytkownika do zrobienia czegoś.
Ian Boyd
2
czy nie pozwoliłoby to nikomu przejść w przypadku zgłoszenia wyjątku COMException z jakiegokolwiek innego powodu przed sprawdzeniem poświadczeń?
Stefan Paul Noack
11

Jeszcze jedno wywołanie platformy .NET w celu szybkiego uwierzytelnienia poświadczeń LDAP:

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
    }
}
palswim
źródło
To jest jedyne rozwiązanie, które działało dla mnie, ponieważ PrincipalContext nie działa dla mnie.
Daniel
PrincipalContext nie jest poprawny dla bezpiecznego połączenia LDAP (alias LDAPS, który korzysta z portu 636
Kiquenet
10

Wypróbuj ten kod (UWAGA: Zgłoszono, że nie działa w systemie 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

z wyjątkiem tego, że musisz utworzyć własny niestandardowy wyjątek dla „LogonException”

Charles Bretana
źródło
Nie używaj obsługi wyjątków do zwracania informacji z metody. „Nieznana nazwa użytkownika lub złe hasło” nie jest wyjątkowe, jest to standardowe zachowanie dla LogonUser. Po prostu zwróć false.
Treb
tak ... to był port ze starej biblioteki VB6 ... napisanej mniej więcej w 2003 roku ... (kiedy po raz pierwszy wyszedł .Net)
Charles Bretana,
Jeśli działa w systemie Windows 2000, ten kod nie będzie działał ( support.microsoft.com/kb/180548 )
Ian Boyd
1
Przemyślenie tego. Logowanie Oczekiwane zachowanie użytkownika, jego celem jest zalogowanie użytkownika . Jeśli nie wykona tego zadania, jest to wyjątek. W rzeczywistości metoda powinna zwracać wartość void, a nie wartość logiczną. Ponadto, jeśli właśnie zwrócono wartość logiczną, konsument metody nie ma możliwości poinformowania użytkownika o przyczynie niepowodzenia.
Charles Bretana
5

Jeśli utkniesz w .NET 2.0 i kodzie zarządzanym, oto inny sposób, który działa z kontami lokalnymi i domenowymi:

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;
}   
Chauwel
źródło
Działa dobrze z lokalnymi kontami komputera, na którym uruchomił skrypt
eka808,
BTW, ta metoda jest potrzebna, aby ta funkcja działała publicznie. SecureString ToSecureString (ciąg PwString) {char [] PasswordChars = PwString.ToCharArray (); Hasło SecureString = nowy SecureString (); foreach (char c w PasswordChars) Password.AppendChar (c); ProcessStartInfo foo = new ProcessStartInfo (); foo.Password = Hasło; return foo.Password; }
eka808,
Przeciwnie, i tak należy używać SecureString do haseł. WPF PasswordBox obsługuje to.
Stephen Drew,
5

Uwierzytelnianie systemu Windows może się nie powieść z różnych powodów: niepoprawnej nazwy użytkownika lub hasła, zablokowanego konta, wygasłego hasła i innych. Aby rozróżnić te błędy, wywołaj funkcję LogonUser API za pomocą P / Invoke i sprawdź kod błędu, jeśli funkcja zwraca false:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;

public static class Win32Authentication
{
    private class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private SafeTokenHandle() // called by P/Invoke
            : base(true)
        {
        }

        protected override bool ReleaseHandle()
        {
            return CloseHandle(this.handle);
        }
    }

    private enum LogonType : uint
    {
        Network = 3, // LOGON32_LOGON_NETWORK
    }

    private enum LogonProvider : uint
    {
        WinNT50 = 3, // LOGON32_PROVIDER_WINNT50
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr handle);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LogonUser(
        string userName, string domain, string password,
        LogonType logonType, LogonProvider logonProvider,
        out SafeTokenHandle token);

    public static void AuthenticateUser(string userName, string password)
    {
        string domain = null;
        string[] parts = userName.Split('\\');
        if (parts.Length == 2)
        {
            domain = parts[0];
            userName = parts[1];
        }

        SafeTokenHandle token;
        if (LogonUser(userName, domain, password, LogonType.Network, LogonProvider.WinNT50, out token))
            token.Dispose();
        else
            throw new Win32Exception(); // calls Marshal.GetLastWin32Error()
    }
}

Przykładowe użycie:

try
{
    Win32Authentication.AuthenticateUser("EXAMPLE\\user", "P@ssw0rd");
    // Or: Win32Authentication.AuthenticateUser("[email protected]", "P@ssw0rd");
}
catch (Win32Exception ex)
{
    switch (ex.NativeErrorCode)
    {
        case 1326: // ERROR_LOGON_FAILURE (incorrect user name or password)
            // ...
        case 1327: // ERROR_ACCOUNT_RESTRICTION
            // ...
        case 1330: // ERROR_PASSWORD_EXPIRED
            // ...
        case 1331: // ERROR_ACCOUNT_DISABLED
            // ...
        case 1907: // ERROR_PASSWORD_MUST_CHANGE
            // ...
        case 1909: // ERROR_ACCOUNT_LOCKED_OUT
            // ...
        default: // Other
            break;
    }
}

Uwaga: LogonUser wymaga relacji zaufania z domeną, dla której sprawdzasz poprawność.

Michael Liu
źródło
czy możesz wyjaśnić, dlaczego twoja odpowiedź jest lepsza niż odpowiedź najwyżej głosowana?
Mohammad Ali
1
@MohammadAli: Jeśli chcesz wiedzieć, dlaczego nie udało się sprawdzić poprawności poświadczeń (nieprawidłowe poświadczenia, zablokowane konto, wygasłe hasło itp.), Funkcja API LogonUser powie ci. W przeciwieństwie do tego metoda PrincipalContext.ValidateCredentials (zgodnie z komentarzami do odpowiedzi marc_s) nie; zwraca false we wszystkich tych przypadkach. Z drugiej strony LogonUser wymaga relacji zaufania z domeną, ale PrincipalContext.ValidateCredentials (tak myślę) nie.
Michael Liu
2

Moja prosta funkcja

 private bool IsValidActiveDirectoryUser(string activeDirectoryServerDomain, string username, string password)
    {
        try
        {
            DirectoryEntry de = new DirectoryEntry("LDAP://" + activeDirectoryServerDomain, username + "@" + activeDirectoryServerDomain, password, AuthenticationTypes.Secure);
            DirectorySearcher ds = new DirectorySearcher(de);
            ds.FindOne();
            return true;
        }
        catch //(Exception ex)
        {
            return false;
        }
    }
hossein andarkhora
źródło
1

Oto moje kompletne rozwiązanie uwierzytelniające w celach informacyjnych.

Najpierw dodaj cztery następujące odniesienia

 using System.DirectoryServices;
 using System.DirectoryServices.Protocols;
 using System.DirectoryServices.AccountManagement;
 using System.Net; 

private void AuthUser() { 


      try{
            string Uid = "USER_NAME";
            string Pass = "PASSWORD";
            if (Uid == "")
            {
                MessageBox.Show("Username cannot be null");
            }
            else if (Pass == "")
            {
                MessageBox.Show("Password cannot be null");
            }
            else
            {
                LdapConnection connection = new LdapConnection("YOUR DOMAIN");
                NetworkCredential credential = new NetworkCredential(Uid, Pass);
                connection.Credential = credential;
                connection.Bind();

                // after authenticate Loading user details to data table
                PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
                UserPrincipal user = UserPrincipal.FindByIdentity(ctx, Uid);
                DirectoryEntry up_User = (DirectoryEntry)user.GetUnderlyingObject();
                DirectorySearcher deSearch = new DirectorySearcher(up_User);
                SearchResultCollection results = deSearch.FindAll();
                ResultPropertyCollection rpc = results[0].Properties;
                DataTable dt = new DataTable();
                DataRow toInsert = dt.NewRow();
                dt.Rows.InsertAt(toInsert, 0);

                foreach (string rp in rpc.PropertyNames)
                {
                    if (rpc[rp][0].ToString() != "System.Byte[]")
                    {
                        dt.Columns.Add(rp.ToString(), typeof(System.String));

                        foreach (DataRow row in dt.Rows)
                        {
                            row[rp.ToString()] = rpc[rp][0].ToString();
                        }

                    }  
                }
             //You can load data to grid view and see for reference only
                 dataGridView1.DataSource = dt;


            }
        } //Error Handling part
        catch (LdapException lexc)
        {
            String error = lexc.ServerErrorMessage;
            string pp = error.Substring(76, 4);
            string ppp = pp.Trim();

            if ("52e" == ppp)
            {
                MessageBox.Show("Invalid Username or password, contact ADA Team");
            }
            if ("775​" == ppp)
            {
                MessageBox.Show("User account locked, contact ADA Team");
            }
            if ("525​" == ppp)
            {
                MessageBox.Show("User not found, contact ADA Team");
            }
            if ("530" == ppp)
            {
                MessageBox.Show("Not permitted to logon at this time, contact ADA Team");
            }
            if ("531" == ppp)
            {
                MessageBox.Show("Not permitted to logon at this workstation, contact ADA Team");
            }
            if ("532" == ppp)
            {
                MessageBox.Show("Password expired, contact ADA Team");
            }
            if ("533​" == ppp)
            {
                MessageBox.Show("Account disabled, contact ADA Team");
            }
            if ("533​" == ppp)
            {
                MessageBox.Show("Account disabled, contact ADA Team");
            }



        } //common error handling
        catch (Exception exc)
        {
            MessageBox.Show("Invalid Username or password, contact ADA Team");

        }

        finally {
            tbUID.Text = "";
            tbPass.Text = "";

        }
    }
Gayan Chinthaka Dharmarathna
źródło