ASP.NET Core 2.0 LDAP Active Directory Authentication

namespace: System.DirectoryServices.AccountManagement;



 public bool UserAuthentication(string username, string password)
 {
        PrincipalContext pc = new PrincipalContext(ContextType.Domain, "<DOMAIN NAME>");
        bool isValid = pc.ValidateCredentials(username, password);
        return isValid;
 }

According to #2089, it is only available in Windows Compatibility-Pack for .NET Core. I currently use Novell.Directory.Ldap.NETStandard.

public bool ValidateUser(string domainName, string username, string password)
{
   string userDn = $"{username}@{domainName}";
   try
   {
      using (var connection = new LdapConnection {SecureSocketLayer = false})
      {
         connection.Connect(domainName, LdapConnection.DEFAULT_PORT);
         connection.Bind(userDn, password);
         if (connection.Bound)
            return true;
      }
   }
   catch (LdapException ex)
   {
      // Log exception
   }
   return false;
}

For authentication and authorization, we can use Cookie Authentication Middleware with claims.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, 
   ILoggerFactory loggerFactory)
{
   app.UseCookieAuthentication(new CookieAuthenticationOptions
   {                
      AuthenticationScheme = "AuthenticationScheme",
      LoginPath = new PathString("/Account/Login"),
      AccessDeniedPath = new PathString("/Common/AccessDenied"),
      AutomaticAuthenticate = true,
      AutomaticChallenge = true
   });
}

It has few moving pieces, so I created a working sample project at GitHub. There are two main pieces - LdapAuthenticationService and SignInManager.


Thanks to Win's Answer for pointing out that I needed to use Windows Compatibility Pack, I was able to figure this out.

The first thing I had to do was install the Nuget package

Install-Package Microsoft.Windows.Compatibility 

At the time, I needed a preview version, so I appended -Version 2.0.0-preview1-26216-02 on the end of this command

Then, add using statements for System.DirectoryServices and System.DirectoryServices.AccountManagement

Then, just plug this logic into my HandleAuthenticateAsync method:

const string LDAP_PATH = "EX://exldap.example.com:5555";
const string LDAP_DOMAIN = "exldap.example.com:5555";

using (var context = new PrincipalContext(ContextType.Domain, LDAP_DOMAIN, "service_acct_user", "service_acct_pswd")) {
    if (context.ValidateCredentials(username, password)) {
        using (var de = new DirectoryEntry(LDAP_PATH))
        using (var ds = new DirectorySearcher(de)) {
            // other logic to verify user has correct permissions

            // User authenticated and authorized
            var identities = new List<ClaimsIdentity> { new ClaimsIdentity("custom auth type") };
            var ticket = new AuthenticationTicket(new ClaimsPrincipal(identities), Options.Scheme);
            return Task.FromResult(AuthenticateResult.Success(ticket));
        }
    }
}

// User not authenticated
return Task.FromResult(AuthenticateResult.Fail("Invalid auth key."));