3

I'm investigating how the client id and client secret can be authenticated with LDAP.

Note: this Kotlin code...

@Configuration
@EnableAuthorizationServer
class OAuth2AuthorizationServerConfig() : AuthorizationServerConfigurerAdapter() {

I'm relatively new to Spring and it seems that this is not something I should be trying to? However, it seems like a useful option. Why? Because it allows me to delegate the client secret management to an LDAP directory and, effectively, allow my ops team to change the secret (in some managed way of coure). With this my application does not need to know the secret. This seems pretty neat?

The oauth end point is basic auth - this seems to be what Spring gives me with the @EnableAuthorizationServer annotation. Requests to the http://somehost/oauth/token specify grant_type:client_credentials.

I created code to get an arbitrary token (sandbox)...and what I'd like is to just specify the client and scopes that apply to that client and not specify the secret...

@Throws(Exception::class)
override fun configure(
        clients: ClientDetailsServiceConfigurer
) {
    // Inlining will create a store per credential entry
    val serviceBuilder = clients.inMemory()
    serviceBuilder.withClient("user").secret("test").scopes("XXX")
}

I have tried loads of different ideas to add an LDAP Authentication Provider to the managed set of providers in the ProviderManager and thus far have failed. If I debug into the authenticate method at run time I only always have the AnonymousAuthenticationProvider and DaoAuthenticationProvider

The following probably shows my inexperience but here's one example and please read past the probable crazyness - just trying to see if I can inject an LDAPAuthenticationProvider...

@Autowired
lateinit var providerMan: AuthenticationManager

@Throws(Exception::class)
override fun configure(endpoints: AuthorizationServerEndpointsConfigurer) {
    (providerMan as ProviderManager).providers.add(0,
            LdapAuthenticationProvider(
                    PasswordComparisonAuthenticator(PasswordPolicyAwareContextSource("ldap://something"))
            )
    )
}

Question is therefore fairly simple... Is there a way to add an LdapAuthenticationProvider such that I can use LDAP to authenticate the client id and client secret?

BBB
  • 31
  • 4

2 Answers2

0

To have client ids and secrets in LDAP, you need an LDAP-based ClientDetailsService implementation, and that does not exist in Spring Security OAuth. Maybe it would be feasible to build that implementation as a bridge to the LDAPAuthenticationProvider, but I am not sure that would be a good idea. In any case, you'd have to build the implementation yourself.

rainerfrey
  • 560
  • 4
  • 14
  • **How to use the Ldap compare operation to verify the password?** ..... I've run into a roadblock - ... because `loadClientByClientId(String clientId)` does not know the client_secret I have no way of using the Ldap compare operation to verify the password. And the `PasswordEncoder#matches(...)` method does not know the clientId? Any further advice? – BBB Dec 07 '17 at 14:43
  • okay so I can 'hack' it by making `MyClientDetailsService` a `@Component` and `@Autowire` it into my `AuthorizationServerConfigurerAdapter`. Then hold onto the client id and pass `MyClientDetailsService` to my custom `PasswordEncoder` Then at run time the PasswordEncoder can request an Ldap password compare with the `rawPassword` passed into `matches` and the clientId I hold onto from `MyClientDetailsService` seems a bit weird/dodgy? – BBB Dec 07 '17 at 17:36
  • I'm not really familiar with all the internals, but from a look at the JDBC implementation the `loadClientByClientId(String clientId)` seem not to be required (or supposed to) verify the password. Spring Security seems to set up a dedicated authentication provider that uses a ClientDetailUserDetailsService that wraps the client detail into a user detail, and compares the password after loading the client detail. – rainerfrey Dec 10 '17 at 21:58
0

I wanted to add some elaboration to the answer for the community.

I have a prototype working thanks to the advice. I'm using Apache LDAP API to connect to an LDAP server.

The solution is fairly simple - simply create a ClientDetailsService that retrieves the LDAP entry that matches the clientId passed to loadClientByClientId and return an appropriate ClientDetails object (use BaseClientDetails provided by Spring).

You also need to create a custom PasswordEncoder or set the right encoder if Spring gives you the necessary:

override fun configure(security: AuthorizationServerSecurityConfigurer) {
    security.passwordEncoder(SomePasswordEncoder)
}

The default is a plain text password encoder.

BBB
  • 31
  • 4
  • Where do you get the password for client? In general you can't get the password from LDAP. – dur Dec 06 '17 at 22:14
  • My understanding is that the bind user used to connect to LDAP directory (AD in my case) has to have permission to the password attribute. – BBB Dec 07 '17 at 09:19
  • So you use the `client_secret` in your custom `ClientDetailsService` as the LDAP password for the user (`client_id`)? And then you get the password from LDAP for that user, right? If the password is wrong, you throw an exception in your `ClientDetailsService`, right? – dur Dec 07 '17 at 09:25
  • No, with my current prototype I just return the ClientDetails with the client_secret set. Spring plumbing does the client_secret comparison via the DaoAuthenticationProvider that Spring provides with the AuthorizationServer. I've been reading a bit more and see that I might need to consider an LDAP compare request to do the password comparison (your initial comment). I 'think' this means I'll need a custom PasswordEncoder BUT I don't yet know how because the encoder does not have the clientid at run time :-(. – BBB Dec 07 '17 at 14:18