Grails - Spring security ldap active directory authentication - bad credentials error

486 views Asked by At

I'm trying to implement security to REST API's using grails-sporing-security-rest and grails-spring-security-ldap plugins.

The flow should be like this, once authentication provider is implemented it will authenticate the user using the authentication mechanism.

but in my case, i'm trying to authenicate the JSON user name and password POST request, and below is the output .

if you observe the log, SpringSecurityLDAPTemplate authentication is successful, but when the request go to next filter, RestAuthenticationFilter, it says Bad Credentials error. This is making some confusion. The /api/login is spring security endpoint, which will take username and password if authentication is successful, it should return the appropriate token, but why is it being sent to RestAuthenticationFilter again?

With correct username and password:

o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/login'; against '/api/**'
o.s.security.web.FilterChainProxy        : /api/login at position 1 of 7 in additional filter chain; firing Filter: 'SecurityRequestHolderFilter'
o.s.security.web.FilterChainProxy        : /api/login at position 2 of 7 in additional filter chain; firing Filter: 'MutableLogoutFilter'
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/api/login'; against '/logoff'
o.s.security.web.FilterChainProxy        : /api/login at position 3 of 7 in additional filter chain; firing Filter: 'RestAuthenticationFilter'
g.p.s.rest.RestAuthenticationFilter      : Actual URI is /api/login; endpoint URL is /api/login
g.p.s.rest.RestAuthenticationFilter      : Applying authentication filter to this request
c.DefaultJsonPayloadCredentialsExtractor : Extracted credentials from JSON payload. Username: xxxxxxx, password: [PROTECTED]
g.p.s.rest.RestAuthenticationFilter      : Trying to authenticate the request
o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider
o.s.s.ldap.SpringSecurityLdapTemplate    : Searching for entry under DN '', base = 'dc=xxx,dc=xxx,dc=xxx,dc=xxx', filter = '(&(objectClass=user)(userPrincipalName={0}))'
 g.p.s.rest.RestAuthenticationFilter      : Authentication failed: Bad credentials
g.p.s.r.token.bearer.BearerTokenReader   : Looking for bearer token in Authorization header, query string or Form-Encoded body parameter
g.p.s.r.token.bearer.BearerTokenReader   : No token found
g.p.s.r.token.bearer.BearerTokenReader   : Token: null
.BearerTokenAuthenticationFailureHandler : Sending status code 401 and header WWW-Authenticate: Bearer

with wrong username and password:

o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider
ctiveDirectoryLdapAuthenticationProvider : Active Directory authentication failed: Supplied password was invalid
 g.p.s.rest.RestAuthenticationFilter      : Authentication failed: Bad credentials

Resources.Groovy

import myapp.MyUserDetailsContextMapper
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider

// Place your Spring DSL code here
beans = {
    ldapAuthProvider1(ActiveDirectoryLdapAuthenticationProvider, "xxx" ,"xxxx" ) {
        userDetailsContextMapper = ldapUserDetailsMapper
    }
    ldapUserDetailsMapper(MyUserDetailsContextMapper) {

    }
}

application.groovy

grails.plugin.springsecurity.filterChain.chainMap = [
        //Stateless chain
        [
                pattern: '/api/**',
                filters: 'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter'
        ],

        //Traditional, stateful chain
        [
                pattern: '/stateful/**',
                filters: 'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter'
        ]
]
grails.plugin.springsecurity.rest.token.storage.jwt.secret="xyz1234dsgsd4546dfhg345745754785756856856785679fghfgj"
grails.plugin.springsecurity.rest.login.active=true
grails.plugin.springsecurity.rest.login.endpointUrl="/api/login"
grails.plugin.springsecurity.rest.login.failureStatusCode=401

application.yml - grails configurations

grails:
    plugin:
        springsecurity:
            providerNames: ["ldapAuthProvider1","anonymousAuthenticationProvider"]
            useSecurityEventListener: true
            ldap:
                search:
                    attributesToReturn: ['mail', 'displayName']
                    filter: '(sAMAccountName={0})'
                    searchSubtree: true
                authorities:
                    retrieveDatabaseRoles:  false
                    ignorePartialResultException: true
                authenticator:
                    useBind: true
                    attributesToReturn: null
                auth:
                    useAuthPassword: true
            rest:
                login:
                    useJsonCredentials: true
                    usernamePropertyName: username
                    passwordPropertyName: password

UPDATE:

Troubleshooted issue further, and noticed this.

below liner of code is in SpringSecurityLdapTemplate class and Method searchForSingleEntryInternal - from spring-security-ldap 4.2.3 release. Which is being invoked from ActiveDirectoryLdapAuthenticationProvider class to fetch the information from LDAP.

final DistinguishedName searchBaseDn = new DistinguishedName(base);
    final NamingEnumeration<SearchResult> resultsEnum = ctx.search(searchBaseDn,
            filter, params, buildControls(searchControls));

    if (logger.isDebugEnabled()) {
        logger.debug("Searching for entry under DN '" + ctxBaseDn + "', base = '"
                + searchBaseDn + "', filter = '" + filter + "'");
    }

    Set<DirContextOperations> results = new HashSet<DirContextOperations>();
    try {
        while (resultsEnum.hasMore()) {
            SearchResult searchResult = resultsEnum.next();
            DirContextAdapter dca = (DirContextAdapter) searchResult.getObject();
            Assert.notNull(dca,
                    "No object returned by search, DirContext is not correctly configured");

In this case searchResult.getObject is returning null which causing authentication failure, but i verified that i'm able to get the attributes from resultsEnum.

Any assistance is greatly appreciated. Thanks.

1

There are 1 answers

0
Kiran On

This line of code which is part of ActiveDirectoryAuthenticationProvider class is creating trouble, as it's expecting the LdapContext object , infact its passing as null. Extended AbstractAuthenticationProvider and created custom authentication handler to resolve the issue.

DirContextAdapter dca = (DirContextAdapter) searchResult.getObject();