9

We want to setup a microservice which provides a REST API so it is configured as a OAuth2 resource server. This service should also act as a OAuth2 client with the client credential grant. Here is the configuration:

spring.oauth2.client.id=clientCredentialsResource
spring.oauth2.client.accessTokenUri=http://localhost:9003/oauth/token
spring.oauth2.client.userAuthorizationUri=http://localhost:9003/oauth/authorize
spring.oauth2.client.grantType=client_credentials
spring.oauth2.client.clientId=<service-id>
spring.oauth2.client.clientSecret=<service-pw>

The resource server part works fine. For the client part we want to use Feign, Ribbon and Eureka:

@FeignClient("user")
public interface UserClient
{
  @RequestMapping( method = RequestMethod.GET, value = "/user/{uid}")
  Map<String, String> getUser(@PathVariable("uid") String uid);
}

Based on the gist in issue https://github.com/spring-cloud/spring-cloud-security/issues/56 I created a feign request intercepter which sets the access token from the autowired OAuth2RestOperations template in the feign request header

@Autowired
private OAuth2RestOperations restTemplate; 

template.header(headerName, String.format("%s %s", tokenTypeName, restTemplate.getAccessToken().toString()));

But this gives me the error on calling the user service:

error="access_denied", error_description="Unable to obtain a new access token for resource 'clientCredentialsResource'. The provider manager is not configured to support it.

As I can see the OAuth2ClientAutoConfiguration creates always an instance of AuthorizationCodeResourceDetails for an web application but not the required ClientCredentialsResourceDetails which is only used for non-web applications. In the end the no access token privider is responsible for the resource details and the call failed in

AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:146) 

I tried to overwrite the auto configuration but failed. Can somebody please give me a hint how to do it?

Daniel Sass
  • 314
  • 2
  • 4
  • 13

1 Answers1

8

To switch off this piece of autoconfiguration you can set spring.oauth2.client.clientId= (empty), (per the source code), otherwise you have to "exclude" it in the @EnableAutoConfiguration. If you do that you can just set up your own OAuth2RestTemplate and fill in the "real" client ID from your own configuration, e.g.

@Configuration
@EnableOAuth2Client
public class MyConfiguration {

  @Value("myClientId")
  String myClientId;

  @Bean
  @ConfigurationProperties("spring.oauth2.client")
  @Primary
  public ClientCredentialsResourceDetails oauth2RemoteResource() {
    ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
    details.setClientId(myClientId);
    return details;
  }

  @Bean
  public OAuth2ClientContext oauth2ClientContext() {
    return new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest());
  }

  @Bean
  @Primary
  public OAuth2RestTemplate oauth2RestTemplate(
      OAuth2ClientContext oauth2ClientContext,
      OAuth2ProtectedResourceDetails details) {
    OAuth2RestTemplate template = new OAuth2RestTemplate(details,
      oauth2ClientContext);
    return template;
  }

}
Kyle
  • 3,549
  • 1
  • 19
  • 25
Dave Syer
  • 56,583
  • 10
  • 155
  • 143
  • Thanks for the quick answer! This is something I tried. Excluding the autoconfiguration doesn't work because an auto-wiring dependency failed. I tried now to set the clientId empty which worked, but for some reason I can't auto-wire OAuth2RestOperations restTemplate than anymore from which I wanted to get the access token. Any ideas? – Daniel Sass Apr 17 '15 at 11:57
  • I guess you need to create a `@Bean` of that type as well. I'll update the code in the answer. – Dave Syer Apr 17 '15 at 12:24
  • Actually you may need to not set `spring.oauth2.client.clientId` at all (rather than leaving it empty). Let us know if that works. – Dave Syer Apr 17 '15 at 12:27
  • Just thinking about it: Wouldn't it be more convenient to construct the credentials resource details from the spring.oauth2.client.grantType value in the auto configuration? So everyone can choose which grant he want to use for the client. – Daniel Sass Apr 17 '15 at 12:53
  • Pull requests welcome. I scratched my head a bit on that, and decided it was too hard for today (mainly because `@EnableOAuth2Sso` only makes sense for auth code). But if anyone has a proposal, let's have it. – Dave Syer Apr 17 '15 at 16:02
  • 2
    @DaveSyer, setting spring.oauth2.client.clientId='' (or seemingly equivalently, leaving it unset) has the additional side-effect of disabling the OAuth2SsoConfiguration, which it turns out I still need. Because I need to customize the OAuth2RestTemplate bean, I've already done as you suggested here to replace OAuth2ClientAutoConfiguration, but replacing both seems like a lot just to customize the OAuth2RestTemplate bean; do you have alternative suggestions? – Tommy Knowlton May 19 '15 at 19:09
  • 2
    Yes, there's a `*Customizer` callback (added since this thread started). If you want SSO with a customized rest template that's the way to do it. – Dave Syer May 19 '15 at 21:05
  • 1
    @DaveSyer can you expand on the reference to a *Customizer callback please? Can you provide an example or link to documentation at all? I can't seem to find any mention of a "Customizer callback" or any "*Customizer" class... – SunshinyDoyle Feb 09 '16 at 19:13
  • Customizer is here: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/UserInfoRestTemplateCustomizer.java – Dave Syer Feb 09 '16 at 22:28