Auth0 Home Blog Docs

Audience not supported in Spring's @EnableOAuth2Client

spring-boot
client-credentials-g
spring-security

#1

We’re using Auth0 as Authorization Server in a micro services environment. Our batch application needs to connect to the main API through client credentials (machine-to-machine). We are using Spring’s RestTemplate and @EnableOAuth2Client to get an access_token and make calls to the Resource server. We’re using latest spring-boot (2.0.1.RELEASE). But, Auth0 needs the "audience"property filled and the ClientCredentialsResourceDetails() doesn’t have this property. So, I want to write an interceptor to add this property. Here’s the Auth0Config class:

@Configuration
@EnableOAuth2Client
public class OAuthConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(OAuthConfig.class);

    @Value("${planbatch.target.client_id}")
    private String oAuth2ClientId;

    @Value("${planbatch.target.client_secret}")
    private String oAuth2ClientSecret;

    @Value("${planbatch.target.access_token_uri}")
    private String accessTokenUri;

    @Value("${planbatch.target.audience}")
    private String audience;

    @Bean
    public RestTemplate oAuthRestTemplate() {

        ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
        resourceDetails.setId("1");
        resourceDetails.setClientId(oAuth2ClientId);
        resourceDetails.setClientSecret(oAuth2ClientSecret);
        resourceDetails.setAccessTokenUri(accessTokenUri);
        resourceDetails.setClientAuthenticationScheme(AuthenticationScheme.form);

        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);

        final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
        interceptors.add(new AddAudienceInterceptor(audience));
        restTemplate.setInterceptors(interceptors);
        return restTemplate;
    }
}

And here’s the interceptor:

public class AddAudienceInterceptor implements ClientHttpRequestInterceptor {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(AddAudienceInterceptor.class);
    
        private String audience;
    
        public AddAudienceInterceptor(String audience) {
            this.audience = audience;
        }
    
        @Override
        public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
    
            if (bytes.length > 0) {
                ObjectMapper mapper = new ObjectMapper();
                Auth0ClientCredentials credentials = mapper.readValue(bytes, Auth0ClientCredentials.class);
                credentials.setAudience(audience);
                bytes = mapper.writeValueAsBytes(credentials);
            }
            return clientHttpRequestExecution.execute(httpRequest, bytes);
        }
    }

The problem is that the interceptor works on the calls to the ResourceServer but the goal is to intercept the calls to the AuthorizationServer (Auth0 in my case).

The body of the call to the AuthorizationServer is currently:

Body:
{ grant_type: 'client_credentials',
  client_id: 'my_client_id',
  client_secret: 'my_client_secret' }

What I need to achieve is:

Body:
{ grant_type: 'client_credentials',
  client_id: 'my_client_id',
  client_secret: 'my_client_secret',
  audience: 'my_audience' }

Anyone any idea how I can achieve this? Thanks for you help!


#2

Hi rogier

I found myself in the same situation as you. I couldn’t make the Interceptor to be triggered and so my request failed as it did not contained the audience property. I went for a different approach then.
I basically added an extra AccessTokenProvider to the RestTemplate. That AccessTokenProvider has an RequestEnhancer which is in charge to add extra parameters to the request. It looks like:

		//Create the Resource Details
        ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
        resourceDetails.setClientId(config.getString("auth0.clientId"));
        resourceDetails.setClientSecret(config.getString("auth0.clientSecret"));
        resourceDetails.setAccessTokenUri(config.getString("auth0.issuer"));
        resourceDetails.setClientAuthenticationScheme(AuthenticationScheme.query);

        //Add extra parameters
        DefaultAccessTokenRequest defaultAccessTokenRequest = new DefaultAccessTokenRequest();
        Map<String,String> params = new HashMap<>();
        params.put("audience",config.getString("auth0.audience"));
        defaultAccessTokenRequest.setAll(params);

        //Create a RequestEnhancer that will look for extra parameters
        DefaultRequestEnhancer defaultRequestEnhancer = new DefaultRequestEnhancer();
        defaultRequestEnhancer.setParameterIncludes(Collections.singletonList("audience"));
        
        //Create a new Token Provider
        ClientCredentialsAccessTokenProvider clientCredentialsAccessTokenProvider =  new ClientCredentialsAccessTokenProvider();
        clientCredentialsAccessTokenProvider.setTokenRequestEnhancer(defaultRequestEnhancer);

        //Create the RestTemplate and add a the Token Provider
        this.restTemplate = new HalOAuth2RestTemplate(resourceDetails,new DefaultOAuth2ClientContext(defaultAccessTokenRequest));
        this.restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
        this.restTemplate.setAccessTokenProvider(clientCredentialsAccessTokenProvider);

It has the same result as adding the Interceptor, but not sure if it will suit your requirements.