Validate a username and password against Active Directory?
Asked Answered
S

15

592

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

Stenotype answered 14/11, 2008 at 15:59 Comment(0)
L
703

If you work on .NET 3.5 or newer, 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:

Update:

As outlined in this other SO question (and its answers), there is an issue with this call possibly returning True for old passwords of a user. Just be aware of this behavior and don't be too surprised if this happens :-) (thanks to @MikeGledhill for pointing this out!)

Lisabeth answered 31/1, 2009 at 22:35 Comment(25)
I tried: PrincipalContext pc = new PrincipalContext(ContextType.Domain, "yourdomain"); Your mileage may vary.Vulpine
Christian: you're absolutely right. I didn't have a server at hand to test my assumptions - and they were wrong. You need the plain domain name - not the LDAP domain descriptor. Thanks for pointing that out!Lisabeth
This worked great, only for me I had to prefix the username with the domain name, ie pc.ValidateCredentials("DOMAN\\UserName", "Password");Diazo
@myotherme: yes, get a UserPrincipal, and then use the user.GetAuthorizationGroups(); to get that list. Check out the great MSDN article on that topic: msdn.microsoft.com/en-us/magazine/cc135979.aspxLisabeth
In my domain, I had to specify pc.ValidateCredentials("myuser", "mypassword", ContextOptions.Negotiate) or I would get System.DirectoryServices.Protocols.DirectoryOperationException: The server cannot handle directory requests.Astraddle
If a password's expired or the accounts disabled, then ValidateCredentials will return false. Unfortuantly, it doesn't tell you why it's returned false (which is a pity as it means I can't do something sensible like redirect the user to change their password).Dilative
Also beware the 'Guest' account -- if the domain-level Guest account is enabled, ValidateCredentials returns true if you give it a non-existant user. As a result, you may want to call UserPrinciple.FindByIdentity to see if the passed in user ID exists first.Dilative
We did in our dev/test domain (one that gets buggered about with randomly to test different scenarios). If it wasn't for that, it wouldn't have been caught, leaving a gaping security hole. The problem is whilst we have control over our own domain, we don't have any control over our customer's domain, so I'd rather have bullet proof code than make assumptions :-) Pity this behaviour wasn't documented on the MSDN as it took an age to track down...Dilative
@AlexPeck: the reason why you had to do this (like me) was that .NET uses the following technologies by default: LDAP+SSL, Kerberos, then RPC. I suspect RPC is off in your network (good!) and Kerberos doesn't actually get used by .NET unless you explicitly tell it using ContextOptions.Negotiate.Overabundance
I also want to add that I needed to set ContextOptions.Negotiate to fix a performance issue on our production server. It was taking 20-30 seconds to validate credentials without specifying that enumeration.Transposition
@Sarah - it's pretty safe to assume that the password would be passed into a method containing this code by some process (user input etc) - the code shown is just an example but you need to, at some point, pass a password somewhere!Kenzi
@Kisame: use UserPrincipal.CurrentLisabeth
Beware - if you use this to validate and you have a AD lockout policy (e,g, three incorrect attempts), it will lock you out.Version
I get error as: A local error occurred. Stack trace: at System.DirectoryServices.Protocols.LdapConnection.BindHelper(NetworkCredential newCredential, Boolean needSetCredential) at System.DirectoryServices.AccountManagement.CredentialValidator.lockedLdapBind(LdapConnection current, NetworkCredential creds, ContextOptions contextOptions) at System.DirectoryServices.AccountManagement.CredentialValidator.BindLdap(NetworkCredential creds, ContextOptions contextOptions) at System.DirectoryServices.AccountManagement.CredeEnglishism
@marc_s, is there a way to handle exceptions using this code such as incorrect username/pwd, 'account disabled`, etc...? ThanksAles
@Sam: well, if the return value is false - then something is wrong (credentials not valid). I don't think you can get any more information than that... (also you shouldn't be showing any more to a user anyway!)Lisabeth
If you get that DirectoryOperationException, instead of dropping straight down to just Negotiate as mentioned in comments above, at least try Negotiate | Signing | Sealing first. You might think that's what it did by default without any options (based on documentation in the PrincipalContext constructuor), but if you use ILSpy you'll see the default it tries is actually Simple | SecureSocketLayer. And with this type of exception it just fails and doesn't try anything else. Try with Negotiate | Signing | Sealing and if that works you're still secure. That's what happened/worked for me.Returnee
Do be aware that if the user CHANGES their Active Directory password, this piece of code will continue to happily authenticate the user using their old AD password. Yup, really. Have a read here: #8950001Prize
How can I get error codes like https://mcmap.net/q/64319/-validate-a-username-and-password-against-active-directory using PrincipalContext.ValidateCredentials ?Pleasing
@Kiquenet: you cannot - ValidateUser will only ever return a bool (true/false) - nothing moreLisabeth
Not valid for secure LDAP connection (aka LDAPS, which uses port 636) ?Pleasing
@BrettVeenstra Why reason using ContextOptions.Negotiate? I get The server cannot handle directory requests error using my TestAd.aspx. In the same server: In a website get the error. In another website it works right. Validate the same credentials both.Pleasing
@nzpcmad You should set your AD timeout policy to temporary lockout. This is good security practice to prevent brute force account compromise.Eldest
@ChrisJ This is good security practice as you do not want to give information if someone was guessing accounts. If they get the username, password wrong, don't tell them which one, nor if the password is expired. Only honour legit login credentials. This protects you from account name harvesting for example which can be used to attempt compromise via other vectors.Eldest
@ChrisJ well you could quite easily validate the username existence and do checks on enabled status yourself. That would then imply if the password was the issue. It makes sense for this method to not reveal the username/pw info as extremely often developers just pass on the error message. This encourages best practices by being the default.Eldest
A
79

Several solutions presented here lack the ability to differentiate between a wrong user / password, and a password that needs to be changed. That can be done in the following way:

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

If the users password is wrong, or the user doesn't exists, error will contain

"8009030C: LdapErr: DSID-0C0904DC, comment: AcceptSecurityContext error, data 52e, v1db1",

if the users password needs to be changed, it will contain

"8009030C: LdapErr: DSID-0C0904DC, comment: AcceptSecurityContext error, data 773, v1db1"

The lexc.ServerErrorMessage data value is a hex representation of the Win32 Error Code. These are the same error codes which would be returned by otherwise invoking the Win32 LogonUser API call. The list below summarizes a range of common values with hex and decimal values:

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)
Asthenopia answered 14/6, 2012 at 12:55 Comment(6)
Unfortunately, some AD installations doesn't return the LDAP sub error code, which means that this solution won't work.Unmeaning
Don't forget to add some references to the project: System.DirectoryServices and System.DirectoryServices.ProtocolsIsidoro
My question, though, is this: how do you get the LDAP server name? If you're writing a portable application, you can't expect the user to know or need to supply the names of AD servers on every network.Isidoro
I have users which are restricted to logging in to specific workstations; how do I specify the workstation that I am trying the login for? (workstation1 would fail with data 531, workstation2 would work fine, for example)Serotherapy
I feel weird as I don't think you're getting enough credit. This is a fully managed method without the trouble of Win32 API call to determine if "user must reset password" which clearly none of the other answers achieved. Is there any loophole in this method causing it's low appreciation rate? hmm...Phlebosclerosis
It should be noted that this will not differentiate between an invalid password and a user is locked out, because a locked account will return a 775 even if the password is wrong.Rella
N
77

We do this on our Intranet

You have to use System.DirectoryServices;

Here are the guts of the code

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();
        }
    }
}
Nagging answered 14/11, 2008 at 16:10 Comment(8)
What do you put in "path"? The name of the domain? The name of the server? The LDAP path to the domain? The LDAP path to the server?Byrdie
Answer1: No we run it as a web service so it can be called from multiple locations in the main web app. Answer2: Path contains LDAP info... LDAP://DC=domainname1,DC=domainname2,DC=comNagging
It would seem that this could allow LDAP injection. You may want to make sure to escape or remove any parenthesis in the strAccountIdNegro
Does this mean that strPassword is stored in LDAP in plain text?Cedilla
There should never be a need to explicitly call Close() on a using variable.Banuelos
I need validate and then get some information of validated user. this code work for me. tank youFreshet
use a path like entry.Path = @"LDAP://OU=""Company Users"",OU=Company,DC=boise,DC=esiconstruction,DC=com"; The path is right to left where the rightmost ou organization unit is the top node.Shaunda
How to authenticate the DirectoryEntry with Managed Service account(Password is not available)?Textual
C
37

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

Compliancy answered 14/11, 2008 at 16:15 Comment(2)
This code is bad because it's also doing an authorization check (check if the user is allowed to read active directory information). The username and password can be valid, but the user not allowed to read info - and get an exception. In other words you can have a valid username&password, but still get an exception.Byrdie
i am actually in the process of asking for the native equivalent of PrincipleContext - which only exists in .NET 3.5. But if you are using .NET 3.5 or newer you should use PrincipleContextByrdie
C
28

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
Concavoconvex answered 14/11, 2008 at 23:51 Comment(3)
May I ask where you got the devinitions for Expired and Must_Change... Found them nowhere but here :)Lafleur
From an MSDN article: msdn.microsoft.com/en-us/library/windows/desktop/…Concavoconvex
Thanks. I was trying to find out way my validation was returning false all the time. It was because the user needs to change his password.Regeneracy
E
22

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.)

Epitomize answered 14/11, 2008 at 16:1 Comment(7)
As @Concavoconvex points out, LogonUser API has many useful traits beyond a System.DirectoryServices call.Epitomize
@cciotti: No, that's wrong. The BEST way to correctly authenticate someone is to use LogonUserAPI as @Epitomize write. All other methods described in this post will NOT WORK 100%. Just a note however, I do believe you have to be domain joined inorder to call LogonUser.Concavoconvex
@Concavoconvex to generate credentials you have to be able to connect to the domain by handing in a valid domain account. However I'm pretty sure your machine doesn't necessarily need to be a member of the domain.Epitomize
The LogonUser API requires the user to have the Act as a part of the operating system privelage; which isn't something that users get - and not something you want to be granting to every user in the organization. (msdn.microsoft.com/en-us/library/aa378184(v=vs.85).aspx)Byrdie
Agreed Ian, only LogonUser and SSPI can express the full range of possible results from cred checks. Doing SSPI in C# as asked for by the OP is far from simple hence the LogonUser recommendation. You seen a library that bundles it up elegantly without resorting to unmanaged code. I've not looked TBH.Epitomize
LogonUser only needs Act as part of the operating system for Windows 2000 and below according to support.microsoft.com/kb/180548 ... It looks clean for Server 2003 and higher.Dilative
LogonUser will not work if the domain is not a domain you trust. If you want to validate domain credentials, you will have to bind to the AD's LDAP server.Byrdie
C
20

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 !

Contrary answered 14/11, 2008 at 16:17 Comment(3)
how about authentication types? i think you forgot it in your code above. :-) by default DirectoryEntry.AuthenticationType is set to Secured right? that code is not going to work on LDAPs that are not secured (Anonymous or None perhaps). am i correct with this?Stouthearted
The down-side to querying an AD server is that you have permission to query the AD server. Your credential can be valid, but if you don't have permission to query AD, then you will get the error. That is why the so-called Fast Bind was created; you validate credentials without authorizing the user's ability to do something.Byrdie
wouldn't this allow anyone to pass in case a COMException is thrown for any other reason before the credentials are checked?Presswork
M
14

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
    }
}
Moton answered 7/4, 2011 at 21:55 Comment(2)
This is the only solution that has worked for me, using PrincipalContext hasn't worked for me.Chufa
PrincipalContext not valid for a secure LDAP connection (aka LDAPS, which uses port 636Pleasing
G
11

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"

Gabriellegabrielli answered 14/11, 2008 at 16:6 Comment(4)
Don't use exception handling for returning information from a method. "Unknown user name or bad password" is not exceptional, it is standard behaviour for LogonUser. Just return false.Gipson
yes... this was a port from an old VB6 library... written 2003 or so... (when .Net first came out)Gabriellegabrielli
If running on Windows 2000 this code will not work (support.microsoft.com/kb/180548)Byrdie
Rethinking this. Logon User's expected behavior, it's purpose, is to log the user on. If it fails to perform that task, it IS an exception. In fact, the method should return void, not a Boolean. Plus, if you just returned a Boolean the method's consumer has no way to inform the user what the reason for the failure was.Gabriellegabrielli
F
8

Windows authentication can fail for various reasons: an incorrect user name or password, a locked account, an expired password, and more. To distinguish between these errors, call the LogonUser API function via P/Invoke and check the error code if the function returns 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()
    }
}

Sample usage:

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;
    }
}

Note: LogonUser requires a trust relationship with the domain you're validating against.

Fulbert answered 27/12, 2016 at 21:5 Comment(2)
can you explain why your answer is better than the highest voted answer?Hendecahedron
@MohammadAli: If you need to know why credential validation failed (incorrect credentials, a locked account, an expired password, etc.), the LogonUser API function will tell you. In contrast, the PrincipalContext.ValidateCredentials method (according to comments on marc_s's answer) won't; it returns false in all these cases. On the other hand, LogonUser requires a trust relationship with the domain, but PrincipalContext.ValidateCredentials (I think) does not.Fulbert
A
5

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;
}   
Anticoagulant answered 7/4, 2011 at 11:4 Comment(3)
Works well with local accounts of the machine he launch the scriptKotta
BTW, this method is needed to make this works public static SecureString ToSecureString(string PwString) { char[] PasswordChars = PwString.ToCharArray(); SecureString Password = new SecureString(); foreach (char c in PasswordChars) Password.AppendChar(c); ProcessStartInfo foo = new ProcessStartInfo(); foo.Password = Password; return foo.Password; }Kotta
On the contrary, one should use SecureString for passwords anyway. WPF PasswordBox supports it.Susceptive
B
3

My Simple Function

 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;
        }
    }
Builtin answered 27/12, 2015 at 14:14 Comment(0)
B
2

For me both of these below worked, make sure your Domain is given with LDAP:// in start

//"LDAP://" + domainName
private void btnValidate_Click(object sender, RoutedEventArgs e)
{
    try
    {
        DirectoryEntry de = new DirectoryEntry(txtDomainName.Text, txtUsername.Text, txtPassword.Text);
        DirectorySearcher dsearch = new DirectorySearcher(de);
        SearchResult results = null;

        results = dsearch.FindOne();

        MessageBox.Show("Validation Success.");
    }
    catch (LdapException ex)
    {
        MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
    }
}

private void btnValidate2_Click(object sender, RoutedEventArgs e)
{
    try
    {
        LdapConnection lcon = new LdapConnection(new LdapDirectoryIdentifier((string)null, false, false));
        NetworkCredential nc = new NetworkCredential(txtUsername.Text,
                               txtPassword.Text, txtDomainName.Text);
        lcon.Credential = nc;
        lcon.AuthType = AuthType.Negotiate;
        lcon.Bind(nc);

        MessageBox.Show("Validation Success.");
    }
    catch (LdapException ex)
    {
        MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
    }
}
Buchmanism answered 16/12, 2020 at 12:30 Comment(0)
C
0

Here my complete authentication solution for your reference.

First, add the following four references

 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 = "";

        }
    }
Conclusive answered 24/2, 2020 at 4:57 Comment(0)
B
0

I'm using this procedure as a DLL to login in other app that we developed...
(We are currently using this with OpenEdge Progress)

public static string AzureLogin(string user, string password) {

    string status;

    try {
        new DirectorySearcher(new DirectoryEntry("LDAP://yourdomain.com", user, password) {
            AuthenticationType = AuthenticationTypes.Secure,
            Username = user,
            Password = password
        })  {
            Filter = "(objectclass=user)"
        }.FindOne().Properties["displayname"][0].ToString();

        status = $"SUCCESS - User {user} has logged in.";

    } catch(System.Exception e) {
        status = $"ERROR - While logging in: {e}";
    }

    return status;
}
Baillieu answered 18/7, 2022 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.