Configuring Spring OAuth2 Client for client_credentials flow - spring-boot

We have the following client configuration for our oauth2 clients in place that worked quite well with spring boot 1.4.0:
#Configuration
#ConfigurationProperties(prefix = "pmc.oauth.client")
public class OAuthClientConfig {
#NotNull
private String scope;
#NotNull
private String clientSecret;
#NotNull
private String clientId;
#NotNull
private String accessTokenUri;
private int clientReadTimeout = 60000;
private int clientConnectTimeout = 60000;
#Bean
public OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails() {
ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();
resourceDetails.setAccessTokenUri(accessTokenUri);
resourceDetails.setClientId(clientId);
resourceDetails.setClientSecret(clientSecret);
resourceDetails.setScope(Collections.singletonList(scope));
return resourceDetails;
}
#Bean
public OAuth2ClientContext oauth2ClientContext() {
DefaultOAuth2ClientContext defaultOAuth2ClientContext = new DefaultOAuth2ClientContext();
return defaultOAuth2ClientContext;
}
#Bean
public OAuth2RestTemplate oAuth2RestTemplate(OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails, OAuth2ClientContext oauth2ClientContext) {
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(oAuth2ProtectedResourceDetails, oauth2ClientContext);
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
restTemplate.setRequestFactory(factory);
return restTemplate;
}
}
After updating to Spring-Boot 1.4.1 I've noticed that when autowiring the OAuth2RestTemplate another implementation of OAuth2ProtectedResourceDetails takes precedence (due to the org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ProtectedResourceDetailsConfiguration - autoconfiguration which marks a bean of AuthorizationCodeResourceDetails as primary) over our own defined bean of type ClientCredentialsResourceDetails.
I know I can fix this by not autowiring the resourceDetails and clientContext for the rest template bean and supplying the concrete implementations directly but I was just wondering whether we are configuring our rest template in a way that was not intended by the spring team?
Any thoughts on how to properly configure our rest template for the client_credentials flow?
Cheers,
Ben

I was struggling with the same issue. However, I found that if you add the annotation #EnableOAuth2Client annotation along with:
security.oauth2.client.grant-type = client_credentials
you get the proper injected ClientCredentialsResourceDetails details instance rather than the AuthorizationCodeResourceDetails. In addition, trying to exclude the OAuth2ProtectedResourceDetailsConfiguration does not work. It is a package private class. If you try to exclude it by name, you will get an error stating it is not an auto configuration class. Look at the code for OAuth2RestOperationsConfiguration class. It is what determines which resource to allocate based on inspecting properties within the security.oauth2.client prefix.

I stuck into the same issue right now. My solution is to use a named bean for my ResourceDetails bean and use #Qualifier to inject the proper one when I'm creating the rest template (in your case in the parameters of the oAuth2RestTemplate method).
It would be nice to know the exact cause of this problem with the version upgrade between 1.4.0 and 1.4.1 though.

Related

Mockito: How to mock WebClient configuring Beans

I'm using Mockio, Wiremock and WebClient and I want to test my service layer.
My goal is to use an instance of the webclient and do a real request to wiremock.
Therefore I have to use a standard configuration and not my oauth config from the production mode.
In the service class, I execute reuqets to another api. So the class under test ist annotated with #Service.
Here is the class:
#Service
public class UserServiceImpl implements UserService{
private final Logger log = Logger.getLogger(this.getClass().getName());
private final WebClient webClient;
private final ApplicationConstants applicationConstants;
public UserServiceImpl (WebClient webClient, ApplicationConstants applicationConstants) {
this.applicationConstants = applicationConstants;
this.webClient = webClient;
}
#Override
public User getUserById(#NotNull(message = "userId must not be null.") #NotBlank(message = "userId must not be blank.") String userId) {
return webClient.get()...
}
I configured my WebClient to use Oauth via two Bean Methods in a class annotated with #Configuration.
#Configuration
public class WebClientConfig {
#Bean
public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
...
}
/*
Manages the auth process and token refresh process
*/
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
...
}
}
Because I want to use the webClient without oauth to call wiremock, I want to replace the Beans to return a simple Webclient.builder().build();
So I did:
#ExtendWith({SpringExtension.class, WireMockExtension.class, MockitoExtension.class})
public class TestClass {
#Mock
WebClientConfig webClientConfig;
#MockBean
WebClient webClient;
#InjectMocks
UserServiceImpl userService;
In general as I understand Mockito, i would use my class under test ( the userServiceImpl) with #InjectMocks, so a real instance is used and the dependencies are injected. Therefor I have to provide a Mock for the Webclient. As I don't want to Mock the webclient and just want to configure it different, I do not have to use #Mock. Instead it should be somethig like #MockBean as this annotation creates a bean and replaces existing ones in the context. So I have to mock the Webclientconfig class with #Mock and define something like
when(webclientConfig).webclient(any(OAuth2AuthorizedClientManager.class)).thenReturn(Webclient.builder.build);
But this does not work as I always get a nullpointer exception on the call.
So the basic questions are:
Is my understanding of Mockito right?
How do I have to Manage the Webclient config?
Looks like a case of configuring MockBeans before the rest of the application starts up, which is answered here:
Configure #MockBean component before application start
As of this writing, the answer above mentions using #Primary or #MockBean(answer = Answers.CALLS_REAL_METHODS) as workarounds.

Bean and Autowiring

So I have a bean definition for a class and an autowiring of the same class. Can you tell me if these are separate. I think they are, but it does not make sense with the rest of the code (I am following a tutorial).
#Bean
public OAuth2RestTemplate googleOpenIdTemplate(final OAuth2ClientContext clientContext) {
final OAuth2RestTemplate template = new OAuth2RestTemplate(googleOpenId(), clientContext);
return template;
}
#Autowired
private OAuth2RestTemplate restTemplate;
The two snippets of code would created two different instances of the same class with DIFFERENT configurations, correct?
If I had
OAuth2RestTemplate instance = (OAuth2RestTemplate) context.getBean(googleOpenIdTemplate)
I would be retrieving a bean with the same configurations (singleton vs prototype) whereas in the first case, I would just get a bean of OAuth2RestTemplate without the same configurations.

Cannot inject LoadBalanced annotated OAuth2RestTemplate

I am using Spring Cloud Angel.SR4. My Configuration class for creating an OAuth2RestTemplate bean is as follows:
#Configuration
public class OAuthClientConfiguration {
#Autowired
private MyClientCredentialsResourceDetails resource;
public OAuthClientConfiguration() {
}
#Bean
#Qualifier("MyOAuthRestTemplate")
public OAuth2RestTemplate restTemplate() {
return new OAuth2RestTemplate(this.resource);
}
}
This configuration is totally fine since I am using this RestTemplate in a Feign RequestInterceptor for injecting access tokens to the feign requests. The problem is that when I annotate an autowired OAuth2RestTemplate with #LoadBalanced the dependency injection engine raises a NoSuchBeanDefinitionException exception. For example, the following would raise an exception:
#LoadBalanced
#Autowired
#Qualifier("MyOAuthRestTemplate")
private OAuth2RestTemplate restTemplate;
and when I remove the #LoadBalanced, everything works fine. What is wrong with #LoadBalanced? Do I need any additional configurations (I already have #EnableEurekaClient)?
I found a workaround. The problem was that I had misunderstood the #LoadBalanced annotation. This is just a qualifier for the auto-created load-balanced RestTemplate bean, and it would not create a proxy around the annotated RestTemplate for injecting the load balancing capability.
After seeing this https://github.com/spring-cloud/spring-cloud-commons/blob/v1.0.3.RELEASE/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerAutoConfiguration.java, I revised my OAuth2RestTemplate bean definition as follows, and it solved the problem.
#Bean
#Qualifier("MyOAuthRestTemplate")
public OAuth2RestTemplate restTemplate(RestTemplateCustomizer customizer) {
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(this.resource);
customizer.customize(restTemplate);
return restTemplate;
}
I used #LoadBalanced with restTemplate in spring cloud with ribbon behind the scenes.
adding #LoadBalanced in the bean definition it works
like this:
in my class i have
#Autowired
#LoadBalanced
#Qualifier("bookRepositoryServiceRestTemplate") private RestTemplate bookRepositoryServiceRestTemplate;
and in my configuration class i have:
#Configuration
public class ServiceConfig {
#Bean
#LoadBalanced
public RestTemplate bookRepositoryServiceRestTemplate(SpringClientFactory clientFactory, LoadBalancerClient loadBalancer){
RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory = new RibbonClientHttpRequestFactory(clientFactory,loadBalancer);
return new RestTemplate(ribbonClientHttpRequestFactory);
}
....
}
this works for me
I hope that this can help

Consume an OAuth-secured REST webservice using Spring oauth2

I want to consume a REST webservice from a server which protects his resources using oauth2.
I use Spring boot (JHipster).
To do this i have in SecurityConfiguration class this :
#Value("${oauth.resource:http://sercverUsingOAuth2}")
private String baseUrl;
#Value("${oauth.authorize:http://sercverUsingOAuth2/rest/oauth/token}")
private String authorizeUrl;
#Value("${oauth.token:http://sercverUsingOAuth2/rest/oauth/token}")
private String tokenUrl;
#Bean
public OAuth2RestOperations oauth2RestTemplate() {
AccessTokenRequest atr = new DefaultAccessTokenRequest();
return new OAuth2RestTemplate(resource(),
new DefaultOAuth2ClientContext(atr));
}
#Bean
protected OAuth2ProtectedResourceDetails resource() {
AuthorizationCodeResourceDetails resource = new AuthorizationCodeResourceDetails();
resource.setAccessTokenUri(tokenUrl);
resource.setUserAuthorizationUri(authorizeUrl);
resource.setClientId("client_id");
resource.setClientSecret("client_secret");
resource.setGrantType("grant_type");
return resource;
}
This class (SecurityConfiguration) is annoted using :
#Configuration
#EnableWebSecurity
#EnableOAuth2Client
And this is my controller (Spring MVC) :
#RestController
#RequestMapping("/consume")
public class MyContrtoller {
#Inject
private OAuth2RestOperations oauth2RestTemplate;
#RequestMapping(value = "/oauth2", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public List<DataModel> getProducts() {
ResponseEntity<MyModel> forEntity = oauth2RestTemplate
.getForEntity("http://sercverUsingOAuth2/rest/resourceToConsume",
MyModel.class);
return forEntity.getBody().getData();
}
}
However when i want to consume my webservice (http://myHost/consume/oauth2) i get this Exception :
org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException:
Unable to obtain a new access token for resource 'null'. The provider manager
is not configured to support it.
I have googled and i found this :
github
stackoverflow
But it doesn't help me.
Thanks.
You are using the same URL for the authorization url and the token url. That was my first clue, then I saw your comments.
Even though you are changing the grant type, you are still using "AuthorizationCodeResourceDetails" when you should be using "ClientCredentialsResourceDetails" instead. This type of ResourceDetails is meant to be used for the case you are explaining.
ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails();
resource.setAccessTokenUri(TOKEN_URL);
resource.setClientId(CLIENT_ID);
resource.setClientSecret(CLIENT_SECRET);
resource.setClientAuthenticationScheme(AuthenticationScheme.form); //This line isn't always needed
return resource;

How to overwrite Spring Cloud OAuth2 client autoconfiguration?

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?
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;
}
}

Resources