Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
In
spring-security-oauth2:2.4.0.RELEASE
classes such as
OAuth2RestTemplate
,
OAuth2ProtectedResourceDetails
and
ClientCredentialsAccessTokenProvider
have all been marked as deprecated.
From the javadoc on these classes it points to a
spring security migration guide
that insinuates that people should migrate to the core spring-security 5 project. However I'm having trouble finding how I would implement my use case in this project.
All of the documentation and examples talk about integrating with a 3rd part OAuth provider if you want incoming requests to your application to be authenticated and you want to use the 3rd party OAuth provider to verify the identity.
In my use case all I want to do is make a request with a
RestTemplate
to an external service that is protected by OAuth. Currently I create an
OAuth2ProtectedResourceDetails
with my client id and secret which I pass into an
OAuth2RestTemplate
. I also have a custom
ClientCredentialsAccessTokenProvider
added to the
OAuth2ResTemplate
that just adds some extra headers to the token request that are required by the OAuth provider I'm using.
In the spring-security 5 documentation I've found a section that mentions
customising the token request
, but again that looks to be in the context of authenticating an incoming request with a 3rd party OAuth provider. It is not clear how you would use this in combination with something like a
ClientHttpRequestInterceptor
to ensure that each outgoing request to an external service first gets a token and then gets that added to the request.
Also in the migration guide linked above there is reference to a
OAuth2AuthorizedClientService
which it says is useful for using in interceptors, but again this looks like it relies on things like the
ClientRegistrationRepository
which seems to be where it maintains registrations for third party providers if you want to use that provide to ensure an incoming request is authenticated.
Is there any way I can make use of the new functionality in spring-security 5 for registering OAuth providers in order to get a token to add to outgoing requests from my application?
OAuth 2.0 Client features of Spring Security 5.2.x do not support
RestTemplate
, but only
WebClient
. See
Spring Security Reference
:
HTTP Client support
WebClient
integration for Servlet Environments (for requesting
protected resources)
In addition,
RestTemplate
will be deprecated in a future version. See
RestTemplate javadoc
:
NOTE:
As of 5.0, the non-blocking, reactive
org.springframework.web.reactive.client.WebClient
offers a modern
alternative to the
RestTemplate
with efficient support for both sync
and async, as well as streaming scenarios. The
RestTemplate
will be
deprecated in a future version and will not have major new features
added going forward. See the
WebClient
section of the Spring Framework
reference documentation for more details and example code.
Therefore, the best solution would be to abandon
RestTemplate
in favor of
WebClient
.
Using
WebClient
for Client Credentials Flow
Configure client registration and provider either programmatically or using Spring Boot auto-configuration:
spring:
security:
oauth2:
client:
registration:
custom:
client-id: clientId
client-secret: clientSecret
authorization-grant-type: client_credentials
provider:
custom:
token-uri: http://localhost:8081/oauth/token
…and the OAuth2AuthorizedClientManager
@Bean
:
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
Configure the WebClient
instance to use ServerOAuth2AuthorizedClientExchangeFilterFunction
with the provided OAuth2AuthorizedClientManager
:
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("custom");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
Now, if you try to make a request using this WebClient
instance, it will first request a token from the authorization server and include it in the request.
–
–
–
–
–
Hi maybe it's too late however RestTemplate is still supported in Spring Security 5, to non-reactive app RestTemplate is still used what you have to do is only configure spring security properly and create an interceptor as mentioned on migration guide
Use the following configuration to use client_credentials flow
application.yml
spring:
security:
oauth2:
resourceserver:
jwk-set-uri: ${okta.oauth2.issuer}/v1/keys
client:
registration:
okta:
client-id: ${okta.oauth2.clientId}
client-secret: ${okta.oauth2.clientSecret}
scope: "custom-scope"
authorization-grant-type: client_credentials
provider: okta
provider:
okta:
authorization-uri: ${okta.oauth2.issuer}/v1/authorize
token-uri: ${okta.oauth2.issuer}/v1/token
Configuration to OauthResTemplate
@Configuration
@RequiredArgsConstructor
public class OAuthRestTemplateConfig {
public static final String OAUTH_WEBCLIENT = "OAUTH_WEBCLIENT";
private final RestTemplateBuilder restTemplateBuilder;
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
private final ClientRegistrationRepository clientRegistrationRepository;
@Bean(OAUTH_WEBCLIENT)
RestTemplate oAuthRestTemplate() {
var clientRegistration = clientRegistrationRepository.findByRegistrationId(Constants.OKTA_AUTH_SERVER_ID);
return restTemplateBuilder
.additionalInterceptors(new OAuthClientCredentialsRestTemplateInterceptorConfig(authorizedClientManager(), clientRegistration))
.setReadTimeout(Duration.ofSeconds(5))
.setConnectTimeout(Duration.ofSeconds(1))
.build();
@Bean
OAuth2AuthorizedClientManager authorizedClientManager() {
var authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
var authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, oAuth2AuthorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
Interceptor
public class OAuthClientCredentialsRestTemplateInterceptor implements ClientHttpRequestInterceptor {
private final OAuth2AuthorizedClientManager manager;
private final Authentication principal;
private final ClientRegistration clientRegistration;
public OAuthClientCredentialsRestTemplateInterceptor(OAuth2AuthorizedClientManager manager, ClientRegistration clientRegistration) {
this.manager = manager;
this.clientRegistration = clientRegistration;
this.principal = createPrincipal();
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId(clientRegistration.getRegistrationId())
.principal(principal)
.build();
OAuth2AuthorizedClient client = manager.authorize(oAuth2AuthorizeRequest);
if (isNull(client)) {
throw new IllegalStateException("client credentials flow on " + clientRegistration.getRegistrationId() + " failed, client is null");
request.getHeaders().add(HttpHeaders.AUTHORIZATION, BEARER_PREFIX + client.getAccessToken().getTokenValue());
return execution.execute(request, body);
private Authentication createPrincipal() {
return new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptySet();
@Override
public Object getCredentials() {
return null;
@Override
public Object getDetails() {
return null;
@Override
public Object getPrincipal() {
return this;
@Override
public boolean isAuthenticated() {
return false;
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
@Override
public String getName() {
return clientRegistration.getClientId();
This will generate access_token in the first call and whenever the token is expired. OAuth2AuthorizedClientManager will manage all this to you
–
–
–
–
I found @matt Williams answer quite helpful. Though I would like add in case someone would like to programatically pass clientId and secret for WebClient configuration. Here is how it can be Done.
@Configuration
public class WebClientConfig {
public static final String TEST_REGISTRATION_ID = "test-client";
@Bean
public ReactiveClientRegistrationRepository clientRegistrationRepository() {
var clientRegistration = ClientRegistration.withRegistrationId(TEST_REGISTRATION_ID)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.clientId("<client_id>")
.clientSecret("<client_secret>")
.tokenUri("<token_uri>")
.build();
return new InMemoryReactiveClientRegistrationRepository(clientRegistration);
@Bean
public WebClient testWebClient(ReactiveClientRegistrationRepository clientRegistrationRepo) {
var oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepo, new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauth.setDefaultClientRegistrationId(TEST_REGISTRATION_ID);
return WebClient.builder()
.baseUrl("https://.test.com")
.filter(oauth)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
–
The above answer from @Anar Sultanov helped me get to this point, but as I had to add some additional headers to my OAuth token request I thought I would provide a full answer for how I solved the issue for my use case.
Configure provider details
Add the following to application.properties
spring.security.oauth2.client.registration.uaa.client-id=${CLIENT_ID:}
spring.security.oauth2.client.registration.uaa.client-secret=${CLIENT_SECRET:}
spring.security.oauth2.client.registration.uaa.scope=${SCOPE:}
spring.security.oauth2.client.registration.uaa.authorization-grant-type=client_credentials
spring.security.oauth2.client.provider.uaa.token-uri=${UAA_URL:}
Implement custom ReactiveOAuth2AccessTokenResponseClient
As this is server-to-server communication we need to use the ServerOAuth2AuthorizedClientExchangeFilterFunction
. This only accepts a ReactiveOAuth2AuthorizedClientManager
, not the non-reactive OAuth2AuthorizedClientManager
. Therefore when we use ReactiveOAuth2AuthorizedClientManager.setAuthorizedClientProvider()
(to give it the provider to use to make the OAuth2 request) we have to give it a ReactiveOAuth2AuthorizedClientProvider
instead of the non-reactive OAuth2AuthorizedClientProvider
. As per the spring-security reference documentation if you use a non-reactive DefaultClientCredentialsTokenResponseClient
you can use the .setRequestEntityConverter()
method to alter the OAuth2 token request, but the reactive equivalent WebClientReactiveClientCredentialsTokenResponseClient
does not provide this facility, so we have to implement our own (we can make use of the existing WebClientReactiveClientCredentialsTokenResponseClient
logic).
My implementation was called UaaWebClientReactiveClientCredentialsTokenResponseClient
(implementation omitted as it only very slightly alters the headers()
and body()
methods from the default WebClientReactiveClientCredentialsTokenResponseClient
to add some extra headers/body fields, it does not change the underlying auth flow).
Configure WebClient
The ServerOAuth2AuthorizedClientExchangeFilterFunction.setClientCredentialsTokenResponseClient()
method has been deprecated, so following the deprecation advice from that method:
Deprecated. Use ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager)
instead. Create an instance of ClientCredentialsReactiveOAuth2AuthorizedClientProvider
configured with a WebClientReactiveClientCredentialsTokenResponseClient
(or a custom one) and than supply it to DefaultReactiveOAuth2AuthorizedClientManager
.
This ends up with configuration looking something like:
@Bean("oAuth2WebClient")
public WebClient oauthFilteredWebClient(final ReactiveClientRegistrationRepository
clientRegistrationRepository)
final ClientCredentialsReactiveOAuth2AuthorizedClientProvider
clientCredentialsReactiveOAuth2AuthorizedClientProvider =
new ClientCredentialsReactiveOAuth2AuthorizedClientProvider();
clientCredentialsReactiveOAuth2AuthorizedClientProvider.setAccessTokenResponseClient(
new UaaWebClientReactiveClientCredentialsTokenResponseClient());
final DefaultReactiveOAuth2AuthorizedClientManager defaultReactiveOAuth2AuthorizedClientManager =
new DefaultReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository,
new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
defaultReactiveOAuth2AuthorizedClientManager.setAuthorizedClientProvider(
clientCredentialsReactiveOAuth2AuthorizedClientProvider);
final ServerOAuth2AuthorizedClientExchangeFilterFunction oAuthFilter =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(defaultReactiveOAuth2AuthorizedClientManager);
oAuthFilter.setDefaultClientRegistrationId("uaa");
return WebClient.builder()
.filter(oAuthFilter)
.build();
Use WebClient
as normal
The oAuth2WebClient
bean is now ready to be used to access resources protected by our configured OAuth2 provider in the way you would make any other request using a WebClient
.
–
–
–
This is a simple alternative to OAuth2RestTemplate
. The following snippet has been tested using Spring Boot 3.0.0-M4
and there is no application.yml
configuration is needed.
SecurityConfig.java
@Bean
public ReactiveClientRegistrationRepository getRegistration() {
ClientRegistration registration = ClientRegistration
.withRegistrationId("custom")
.tokenUri("<token_URI>")
.clientId("<client_id>")
.clientSecret("<secret>")
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.build();
return new InMemoryReactiveClientRegistrationRepository(registration);
@Bean
public WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations) {
InMemoryReactiveOAuth2AuthorizedClientService clientService = new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrations);
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrations, clientService);
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth.setDefaultClientRegistrationId("custom");
return WebClient.builder()
.filter(oauth)
.filter(errorHandler()) // This is an optional
.build();
public static ExchangeFilterFunction errorHandler() {
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
if (clientResponse.statusCode().is5xxServerError() || clientResponse.statusCode().is4xxClientError()) {
return clientResponse.bodyToMono(String.class)
.flatMap(errorBody -> Mono.error(new IllegalAccessException(errorBody)));
} else {
return Mono.just(clientResponse);
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0-M4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
<dependencies>
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.