Preface
Let’s imagine your task is to add integration to a service that requires token authorization. You’re given URL for the REST API to consume. You’re told you need to provide HTTP header “Authorization” with value of “Bearer ” + TOKEN. And you’re given URL to an endpoint to obtain an access token together with following parameters:
- grant_type = client_credentials
- scope = MY-SCOPE
- client_id = MY-CLIENT-ID
- client_secret = MY-CLIENT-CREDENTIALS
Hey! That looks like OAuth 2.0 and Spring Security should handle it. So it does.
Assumptions
We’re developing a Spring Boot 4.0 based application. In this case the best choice for consuming a REST API is to use the Spring RestClient. Moreover Spring Boot is providing us with dependency version management, so we don’t have to hardcode versions matching Spring Boot version.
Moreover I will focus only on client-credentials grant type, which is suitable for securing machine-to-machine communication.
Solution
Maven dependency
In this particular situation we need a Spring Boot starter for OAuth 2.0 client, which is described as:
The OAuth 2.0 Client features provide support for the Client role as defined in the OAuth 2.0 Authorization Framework.
The Maven dependency is:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security-oauth2-client</artifactId>
</dependency>
Turning off default Spring Security configuration
NOTE: This section is relevant only for those cases when your project exposes its own REST API and Spring Security was not present in your project before.
If your application (microservice) had already exposed some REST API and it was not secured then after adding abovementioned Maven dependency it turns out your REST API requires authorization now and responds with HTTP 403. To revert this effect we need to defined our own SecurityFilterChain bean:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity unused) {
return new SecurityFilterChain() {
public boolean matches(HttpServletRequest request) {
return false;
}
public List<Filter> getFilters() {
return Collections.emptyList();
}
};
}
RestClient for access token fetching
I prefer to have a separate RestClient instance only for access token fetching. That way we can configure for this particular task, it can have dedicated timeout configuration and so on.
@Bean
public RestClient tokenRestClient(RestClient.Builder builder) {
// you should configure timeouts here as well
return builder.configureMessageConverters((converters) -> {
converters.addCustomConverter(new FormHttpMessageConverter());
converters.addCustomConverter(new OAuth2AccessTokenResponseHttpMessageConverter());
})
.defaultStatusHandler(new OAuth2ErrorResponseErrorHandler())
.build();
}
OAuth2AuthorizedClientManager bean
This is the main part of setting up OAuth 2.0 client support:
@Bean
public OAuth2AuthorizedClientManager myAuthorizedClientManager(
ClientRegistrationRepository repository,
OAuth2AuthorizedClientService authorizedClientService,
@Qualifier("tokenRestClient") RestClient restClient) {
var tokenResponseClient = new RestClientClientCredentialsTokenResponseClient();
tokenResponseClient.setRestClient(restClient);
var provider = new ClientCredentialsOAuth2AuthorizedClientProvider();
provider.setAccessTokenResponseClient(tokenResponseClient);
var manager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(
repository,
authorizedClientService);
manager.setAuthorizedClientProvider(provider);
return manager;
}
Spring Boot properties
spring:
security:
oauth2.client:
provider:
my-token-provider:
token-uri: ACCESS_TOKEN_ENDPOINT_URL
registration:
my-oauth-client:
provider: my-token-provider
authorization-grant-type: client_credentials
client-authentication-method: client_secret_post
client-id: MY-CLIENT-ID
client-secret: MY-CLIENT-SECRET
scope: MY-SCOPE
NOTE: The property “client-authentication-method: client_secret_post” causes that a request for an access token will be an HTTP POST with JSON formatter payload. Otherwise it would be an HTTP POST with a form-urlencoded payload.
Final RestClient
Now we’re ready to define the final RestClient bean that will be used to consume token-secured REST API:
import static org.springframework.security.oauth2.client.web.ClientAttribute.clientRegistrationId;
// ...
@Bean
public RestClient mainRestClient(
RestClient.Builder builder,
OAuth2AuthorizedClientManager authorizedClientManager) {
var interceptor = new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
interceptor.setPrincipalResolver(request -> null);
// you should configure timeouts here as well
return builder.requestInterceptor(interceptor)
.requestInitializer(request -> clientRegistrationId("my-oauth-client")
.accept(request.getAttributes()))
.build();
}
Now we can use the last RestClient to consume some token-secured REST API in a transparent way. The request initializer will take care to inform Spring Security which OAuth client configuration to use. The request interceptor will take care to fetch an access token and create a proper “Authorization” HTTP header. Voila! 🙂
class SomeService {
@Autowired
@Qualifier("mainRestClient")
private RestClient restClient;
public SomeData getSomeData(String key) {
return restClient.get()
.uri("/some/path/{key}", key)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.body(SomeData.class);
}
}








