Rest Client with OAuth2
Simple RestClient
Simplest way is to configure a RestClient like this:
@Configuration
public class RestClientConfig {
@Bean
public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager) {
OAuth2ClientHttpRequestInterceptor requestInterceptor =
new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
return RestClient.builder()
.requestInterceptor(requestInterceptor)
.build();
}
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService clientService) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.refreshToken()
.clientCredentials()
.build();
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, clientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
}
@Service
public class ApiClient {
private final RestClient restClient;
public EngagementApiClientImpl(RestClient restClient) {
this.restClient = restClientbuilder.build();
}
public List<User> getUsers() {
return this.restClient.get()
.uri("https://service.com/v1/users")
.attributes(clientRegistrationId("myOAuthClient"))
.retrieve()
.body(Users.class);
}
}
Together with this application.properties
:
spring.security.oauth2.client.registration.myOAuthClient.authorization-grant-type=client_credentials
spring.security.oauth2.client.registration.myOAuthClient.client-id=id
spring.security.oauth2.client.registration.myOAuthClient.client-secret=secret
spring.security.oauth2.client.registration.myOAuthClient.provider=myOAuthProvider
spring.security.oauth2.client.provider.myOAuthProvider.tokenUri=https://ch/auth/realms/myrealm/protocol/openid-connect/token
Testable RestClient
Problem with above simple RestClient is that it is not easy testable.
When we want to use @RestClientTest
`annotation, we need to inject RestClient.Builder instead of using a RestClient directly.
@RestClientTest(ApiClient.class)
class ApiClientTest {
@Autowired
private EngagementApiClientImpl client;
@Autowired
private MockRestServiceServer server;
@Test
void GetUsers_EmptyList() {
// Arrange
this.server.expect(requestTo("https://service.com/v1/engagement/users"))
.andRespond(withSuccess("[]", MediaType.APPLICATION_JSON));
// Act
List<EngagementUser> result = client.getUsers();
// Assert
assertEquals(0, result.size());
}
}
Above ApiClient class would look like this:
@Service
public class ApiClient {
private final RestClient restClient;
public EngagementApiClientImpl(RestClient.Builder restClientbuilder, OAuth2AuthorizedClientManager authorizedClientManager) {
OAuth2ClientHttpRequestInterceptor requestInterceptor =
new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
this.restClient = restClientbuilder.requestInterceptor(requestInterceptor).build();
}
public List<User> getUsers() {
return this.restClient.get()
.uri("https://service.com/v1/users")
.attributes(clientRegistrationId("myOAuthClient"))
.retrieve()
.body(Users.class);
}
}