Authentication

Authentication against an LDAP follows this multi-step process:

  1. DN Resolution
  2. Password Validation
  3. Entry Resolution
// use a secure connection for authentication
ConnectionConfig connConfig = ConnectionConfig.builder()
  .url("ldap://directory.ldaptive.org")
  .useStartTLS(true)
  .build();

// use a search dn resolver
SearchDnResolver dnResolver = SearchDnResolver.builder()
  .factory(new DefaultConnectionFactory(connConfig))
  .dn("ou=people,dc=ldaptive,dc=org")
  .filter("(uid={user})")
  .build();

// perform a simple bind for password validation
SimpleBindAuthenticationHandler authHandler = new SimpleBindAuthenticationHandler(new DefaultConnectionFactory(connConfig));

Authenticator auth = new Authenticator(dnResolver, authHandler);
AuthenticationResponse response = auth.authenticate(
  new AuthenticationRequest("dfisher", new Credential("password"), new String[] {"mail", "sn"}));
if (response.isSuccess()) { // authentication succeeded
  LdapEntry entry = response.getLdapEntry(); // read mail and sn attributes
} else { // authentication failed
  String msg = response.getDiagnosticMessage(); // read the failure message
  ResponseControl[] ctls = response.getControls(); // read any response controls
}

DN Resolution

Authentication typically begins by gathering an identifier from the user that matches some attribute on their LDAP entry. The DN of the LDAP entry can then be resolved using that identifier. The interface for DN resolvers looks like:

public interface DnResolver
{
  String resolve(User user) throws LdapException;
}

Ldaptive provides the following DN resolution implementations:

SearchDnResolver

Resolves an entry DN by performing an LDAP search. This resolver has the following properties:

Name Default Value Description
baseDn ”” baseDN to search on
userFilter null search filter to execute; e.g. (mail={user})
userFilterArgs null search filter arguments
allowMultipleDns false whether to throw an exception if multiple entries are found with the search filter
subtreeSearch false whether a subtree search should be performed; by default a onelevel search is used

The {user} search filter argument is always assigned the user value from AuthenticationRequest#getUser(), so the userFilterArgs property only needs to be set when you specify custom arguments. Note that if you supply a DefaultConnectionFactory a connection will be opened and closed for every authentication.

If your directory does not allow anonymous access to the attribute used for DN resolution then you can configure a BindConnectionInitializer:

ConnectionConfig connConfig = ConnectionConfig.builder()
  .url("ldap://directory.ldaptive.org")
  .useStartTLS(true)
  .connectionInitializers(new BindConnectionInitializer("cn=manager,ou=people,dc=ldaptive,dc=org", new Credential("password")))
  .build();
SearchDnResolver dnResolver = new SearchDnResolver(new DefaultConnectionFactory(connConfig));

FormatDnResolver

Resolves an entry DN by using String#format(String, Object[]). This resolver is typically used when an entry DN can be formatted directly from the user identifier. For instance, entry DNs of the form uid=dfisher,ou=people,dc=ldaptive,dc=org could be formatted from uid=%s,ou=people,dc=ldaptive,dc=org. This resolver has the following properties:

Name Default Value Description
formatString null format of the DN
formatArgs null format arguments

The %1$s format argument is always assigned the user value from AuthenticationRequest#getUser(). So any arguments supplied in formatArgs will begin at %2$s.

NoOpDnResolver

Does not perform any resolution. The user value from AuthenticationRequest#getUser() is returned as the DN. Used by authentication mechanisms that do not leverage an entry DN, such as DIGEST-MD5.

AggregateDnResolver

Uses multiple DN resolvers to look up a user’s DN. Each DN resolver is invoked on a separate thread. If multiple DNs are allowed then the first one retrieved is returned. Note that you must use the AggregateAuthenticationHandler class with this implementation. The labels provided must link a single DN resolver to a single authentication handler.

// define two connection configs for the servers to aggregate
ConnectionConfig connConfig1 = ConnectionConfig.builder()
  .url("ldap://directory1.ldaptive.org")
  .useStartTLS(true)
  .build();
ConnectionConfig connConfig2 = ConnectionConfig.builder()
  .url("ldap://directory2.ldaptive.org")
  .useStartTLS(true)
  .build();

// use two search dn resolvers
final AggregateDnResolver resolver = AggregateDnResolver.builder()
  .resolver("directory1", SearchDnResolver.builder()
    .factory(new DefaultConnectionFactory(connConfig1))
    .dn("ou=people,dc=ldaptive,dc=org")
    .filter("(uid={user})")
    .build())
  .resolver("directory2", SearchDnResolver.builder()
    .factory(new DefaultConnectionFactory(connConfig2))
    .dn("ou=accounts,dc=ldaptive,dc=org")
    .filter("(mail={user})")
    .build())
  .build();

// use two bind authentication handlers
final AggregateAuthenticationHandler handler = AggregateAuthenticationHandler.builder()
  .handler("directory1", new SimpleBindAuthenticationHandler(new DefaultConnectionFactory(connConfig1)))
  .handler("directory2", new SimpleBindAuthenticationHandler(new DefaultConnectionFactory(connConfig2)))
  .build();

// create an authenticator that aggregates over both directories
Authenticator auth = new Authenticator(resolver, handler);

Use cases

Password Validation

Password validation is done by an AuthenticationHandler. It’s purpose is to use the entry DN and the credential to determine if authentication should succeed. The interface for authentication handlers looks like:

public interface AuthenticationHandler
{
  AuthenticationHandlerResponse authenticate(AuthenticationCriteria criteria) throws LdapException;
}

Ldaptive provides the following authentication handler implementations:

SimpleBindAuthenticationHandler

Authenticates an entry DN by performing an LDAP simple bind operation with that DN and the credential. This is the most common method of authentication against an LDAP and should be used in most circumstances.

CompareAuthenticationHandler

Authenticates an entry DN by performing an LDAP compare operation on the userPassword attribute. This authentication handler should be used in cases where you do not have authorization to perform binds, but do have authorization to read the userPassword attribute. This authentication handler has the following properties:

Name Default Value Description
passwordScheme SHA hash algorithm used by the LDAP for userPassword; Must be a valid Java MessageDigest algorithm.

Entry Resolution

The authentication process always returns an LDAP entry for the DN that attempted authentication. By default a new LdapEntry is simply created with the DN, no LDAP interaction occurs. However, you may wish to return some or all of the user’s LDAP attributes after authentication. The interface for entry resolvers looks like:

public interface EntryResolver
{
  LdapEntry resolve(AuthenticationCriteria criteria, AuthenticationHandlerResponse response) throws LdapException;
}

The connection supplied is the connection that authentication occurred on and the criteria is what was used to authenticate. The entry resolver is only invoked if authentication succeeds. Ldaptive provides the following entry resolver implementations:

SearchEntryResolver

Performs an object level search on the same connection that authentication occurred on and returns any requested attributes. This entry resolver has the following properties:

Name Default Value Description
returnAttributes null attributes to return from the search; null means return all attributes

NoOpEntryResolver

Returns a new LdapEntry that contains only the authenticated DN. This is the default entry resolver.

Response Processing

Authentication response handlers are an optional component of the authenticator which can be used to post process authentication responses. The interface for authentication response handlers looks like:

public interface AuthenticationResponseHandler
{
  void process(AuthenticationResponse response);
}

Potential use cases for authentication response handlers include:

See the account state documentation for examples on how response handlers can be leveraged with various password policy implementations.