11

In previous versions of OAuth2 it was possible to add a custom token granter by adding it to the xml configuration in the <authorization-server> element.

I wonder how I could extend the authorization server with Java Config using a AuthorizationServerConfigurerAdapter, without losing the default configuration, which contains the implicit, client credentials, refresh token and authorization code grant types.

First attempt was using creating the TokenGranter with @Component:

@Component("customTokenGranter")
public class CustomTokenGranter {
     //implementation
}

This leads to a dependency resolution exception because the tokenServices needed to construct the Granter cannot be autowired.

Second attempt was using the configure method

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception
{
    endpoints
        .tokenGranter(new CustomTokenGranter(endpoints.getTokenServices(),
                endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory()));

}

Using this, the default grant types will not be registered.

I also tried a second configuration with a lower order, but without success. What else could I do to add my custom grant type?

Christian Metzler
  • 2,971
  • 5
  • 24
  • 30

4 Answers4

11

You need to add the default ones too, e.g. using a CompositeTokenGranter:

        List<TokenGranter> tokenGranters = getTokenGranters(); // implementation up to you
        tokenGranter = new CompositeTokenGranter(tokenGranters);
        endpoints.tokenGranter(tokenGranter);
Dave Syer
  • 56,583
  • 10
  • 155
  • 143
  • 2
    I solved it by developing a decorator for the original CompositeTokenGranter which can be initialized with the result of endpoints.getTokenGranter(). The decorator has also an add method to add additional granters. Then in the grant method I try the custom granters first and then delegate to the original CompositeTokenGranter. – Christian Metzler Aug 13 '14 at 10:23
  • FYI: There seems to be a mistake in the documentation. The TokenGranter has to be configured with the AuthorizationServerEndpointsConfigurer - the documentation says it should be the AuthorizationServerSecurityConfigurer. – Christian Metzler Aug 13 '14 at 13:47
  • 3
    did you not get into issues when calling `endpoints.getTokenGranter()`? because `clientDetailsService` (within `endpoints` is not properly initialized at this stage. and you also can't use `endpoints.clientDetailsService(clientDetailsService)` to set it, as `yooture.web.config.SecurityOauth2Configuration.AuthorizationServerConfiguration.configure(ClientDetailsServiceConfigurer)` will still be called and overwrite some references again. – domi Aug 20 '14 at 14:45
  • @domi Yes I am getting the issue like this. Even without a custom granter the authentication does not work when using composite granter in this way. Has anyone been able to crack this? – Anshul Goel Oct 02 '15 at 10:39
  • 1
    I believe that issue doesn't exist in master (which will be 2.0.8). You can try it and see (there's a sample in github). – Dave Syer Oct 03 '15 at 16:18
  • Where can I find the example? I could not find anything at https://github.com/spring-projects/spring-security-oauth/tree/master/samples/oauth2 ? – Wim Deblauwe Mar 21 '16 at 13:13
  • 1
    Did you look in the integration tests (https://github.com/spring-projects/spring-security-oauth/tree/master/tests/annotation)? – Dave Syer Mar 21 '16 at 19:38
  • A custom `OAuth2RequestFactory` is failing to manage `csrf` token updating between the `/oauth/authorize` and `/oauth/token` endpoints. Are you willing to comment? Here is the link: http://stackoverflow.com/questions/37061697/invalid-xsrf-token-at-oauth-token – CodeMed May 06 '16 at 22:25
11

Here is another way. Copied from here.

In this example, a new custom TokenGranter, named CustomTokenGranter, is added to a CompositeTokenGranter with the default TokenGranters. I like this example because it uses the AuthorizationServerEndpointsConfigurer's public method getTokenGranter() to retrieve the default TokenGranter's.

@Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
        endpoints.tokenGranter(tokenGranter(endpoints));
    }

    private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
        List<TokenGranter> granters = new ArrayList<TokenGranter>(Arrays.asList(endpoints.getTokenGranter()));
        granters.add(new CustomTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), "custom"));
        return new CompositeTokenGranter(granters);
    }
Jose Martinez
  • 11,452
  • 7
  • 53
  • 68
  • Hi the AuthorizationServerEndpointsConfigurer endpoint TokenGranters is null on start up. Could you tell me how to fix this? – Ninja Dude Feb 22 '21 at 04:01
  • @NinjaDude add a new list. – Jose Martinez Feb 22 '21 at 16:02
  • I mean endpoints.getTokenGranter() == null when i start up my spring boot application. i put a debug point and saw it. so essentially, there is only CustomTokenGranter present. The rest of the default Token Granters are missing (eg refreshtoken, authorization code granter etc). – Ninja Dude Feb 23 '21 at 11:25
  • @NinjaDude List granters = endpoints.getTokenGranter() == null ? New ArrayList<>() : new ArrayList(Arrays.asList(endpoints.getTokenGranter())); – Jose Martinez Feb 23 '21 at 14:44
5

I couldn't find a way to do it because of the dependency on ClientDetailService making it difficult to get the default granters from the getTokenGranter method. I copied over the code from AuthorizationServerEndpointsConfigurer#tokenGranter() and passed in my clientDetailService and other beans directly to the constructors. Note that I add to create a DefaultOAuth2RequestFactory to pass to the granters and to the endpoints:

public TokenGranter tokenGranter() {

            ClientDetailsService clientDetails = clientDetailsService;
            AuthorizationServerTokenServices tokenServices = tokenServices();
            AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
            OAuth2RequestFactory requestFactory = requestFactory();

            List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();

            tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices,
                    clientDetails, requestFactory));
            tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
            tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory));
            tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
            tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
                        clientDetails, requestFactory));
            tokenGranters.add(new CustomTokenGranter(authenticationManager, tokenServices(), clientDetailsService,
                    requestFactory));

            return new CompositeTokenGranter(tokenGranters);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints
                .tokenServices(tokenServices())
                .tokenStore(tokenStore())
                .tokenEnhancer(tokenEnhancer())
                .authorizationCodeServices(authorizationCodeServices())
                .userApprovalHandler(userApprovalHandler())
                .authenticationManager(authenticationManager)
                .requestFactory(requestFactory())
                .tokenGranter(tokenGranter());
    }

That being said, I ended up removing that code and simply added another AuthenticationProvider instead because my new grant type was using a subclass of UsernamePasswordAuthenticationToken anyway, which is the Authentication type used by the password grant by default.

lordm2k
  • 161
  • 2
  • 6
2

According to the documentation, we have :

Grant Types

The grant types supported by the AuthorizationEndpoint can be configured via the AuthorizationServerEndpointsConfigurer. By default all grant types are supported except password (see below for details of how to switch it on). The following properties affect grant types:

authenticationManager: password grants are switched on by injecting an AuthenticationManager. ......

See documentation. So you can inject the AuthenticationManager like this :

    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                    .authenticationManager(authenticationManager)
........
Abdelhafid
  • 799
  • 7
  • 13