Skip to content

Force password change on next login with Active Directory using Apache LDAP API

We use Active Directory (AD) and when users are added they get a password set and a flag to enforce “User must change password at next logon” which results in an AD attribute pwdLastSet=0

I have a Java application using Apache LDAP API to authenticate but when I am doing that I get error code 49 INVALID_CREDENTIALS and no indication to change password.

How can I with Apache LDAP API detect that user has to change password first?

My simple authenticator:

    public void authenticate(String uid, String password) {
    String status = "";
    try {
        LdapConnectionConfig config = new LdapConnectionConfig();
        config.setUseSsl(true);
        config.setLdapHost("activedirectory.domain.net");
        config.setLdapPort(636);
        config.setTrustManagers(new NoVerificationTrustManager());
        config.setName(_ldapMgmtUser);
        config.setCredentials(_ldapMgmtPassword);

        final DefaultPoolableLdapConnectionFactory factory = new DefaultPoolableLdapConnectionFactory(config);
        final LdapConnectionPool pool = new LdapConnectionPool(factory);
        pool.setTestOnBorrow(true);
        final LdapConnectionTemplate ldapConnectionTemplate = new LdapConnectionTemplate(pool);

        final PasswordWarning warning = ldapConnectionTemplate.authenticate(_rootDn, "(sAMAccountName=" + uid + ")",
                SearchScope.SUBTREE, password.toCharArray());

        status = "User credentials authenticated";
        if (warning != null) {
            status = status + " n Warning!!" + warning.toString();
        }
        System.out.println(status);
    } catch (final PasswordException e) {
        System.err.println("############# PasswordException #############");
        status = e.toString();
        e.printStackTrace();
    } catch (Exception e) {
        System.err.println("############# Exception #############");
        e.printStackTrace();

    } finally {
    }
    return;
}

Answer

I noted that when using LdapConnectionTemplate and authenticate(...) (as above) it doesn’t return any useful error codes in the exception and no PasswordWarning. Shouldn’t this scenario return a PasswordWarning? https://nightlies.apache.org/directory/api/2.0.1/apidocs/org/apache/directory/ldap/client/template/PasswordWarning.html

If I use: LdapNetworkConnection and connection.bind(...) it returns an LdapException with message 80090308: LdapErr: DSID-0C090453, comment: AcceptSecurityContext error, data 773, v3839 where 773 is what is expected.

It feels like LdapConnectionTemplate has a (few) bug(s).

The full code:

    public void authenticateWithBind(String uid, String password) {
        String status = "";
        LdapConnection connection = null;
        try {
            LdapConnectionConfig config = new LdapConnectionConfig();
            config.setUseSsl(true);
            config.setLdapHost("activedirectory.domain.net");
            config.setLdapPort(636);
            config.setTrustManagers(new NoVerificationTrustManager());
            config.setName(_ldapMgmtUser);
            config.setCredentials(_ldapMgmtPassword);

            connection = new LdapNetworkConnection(config);
            connection.bind(_ldapMgmtUser, _ldapMgmtPassword);

            EntryCursor cursor = connection.search(_rootDn, "(sAMAccountName=" + uid + ")", SearchScope.SUBTREE, "*");
            
            while (cursor.next()) {
                Entry entry = cursor.get();
                System.out.println("DN: " + entry.getDn());
                System.out.println("Attribute: " + entry.get("pwdLastSet"));

                connection.bind(entry.getDn(), password);
                status = "User credentials authenticated";
                System.out.println(status);
            }
        } catch (LdapException e) {
            System.err.println("############# LdapException #############");
            e.printStackTrace();
            System.err.println("Error message: " + e.getMessage());

        } catch (Exception e) {
            System.err.println("############# Exception #############");
            e.printStackTrace();
            System.err.println("Error message: " + e.getMessage());
        } finally {
            closeConnection(connection);
        }
        return;
    }