diff --git a/docs/modules/ROOT/pages/servlet/oauth2/index.adoc b/docs/modules/ROOT/pages/servlet/oauth2/index.adoc
index d363b11c72f..512400577d0 100644
--- a/docs/modules/ROOT/pages/servlet/oauth2/index.adoc
+++ b/docs/modules/ROOT/pages/servlet/oauth2/index.adoc
@@ -100,7 +100,12 @@ spring:
When using Spring Boot, this is all that is required.
The default arrangement provided by Spring Boot is equivalent to the following:
-[source,java]
+.Configure Resource Server with JWTs
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
@@ -126,6 +131,39 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+import org.springframework.security.config.annotation.web.invoke
+
+@Configuration
+@EnableWebSecurity
+class SecurityConfig {
+
+ @Bean
+ fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
+ http {
+ authorizeHttpRequests {
+ authorize(anyRequest, authenticated)
+ }
+ oauth2ResourceServer {
+ jwt { }
+ }
+ }
+
+ return http.build()
+ }
+
+ @Bean
+ fun jwtDecoder(): JwtDecoder {
+ return JwtDecoders.fromIssuerLocation("https://my-auth-server.com")
+ }
+
+}
+----
+=====
+
[[oauth2-resource-server-access-token-opaque]]
==== Opaque Token Support
@@ -146,7 +184,12 @@ spring:
When using Spring Boot, this is all that is required.
The default arrangement provided by Spring Boot is equivalent to the following:
-[source,java]
+.Configure Resource Server with Opaque Tokens
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
@@ -173,6 +216,41 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+import org.springframework.security.config.annotation.web.invoke
+
+@Configuration
+@EnableWebSecurity
+class SecurityConfig {
+
+ @Bean
+ fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
+ http {
+ authorizeHttpRequests {
+ authorize(anyRequest, authenticated)
+ }
+ oauth2ResourceServer {
+ opaqueToken { }
+ }
+ }
+
+ return http.build()
+ }
+
+ @Bean
+ fun opaqueTokenIntrospector(): OpaqueTokenIntrospector {
+ return SpringOpaqueTokenIntrospector(
+ "https://my-auth-server.com/oauth2/introspect", "my-client-id", "my-client-secret"
+ )
+ }
+
+}
+----
+=====
+
[[oauth2-resource-server-custom-jwt]]
=== Protect Access with a custom JWT
@@ -202,7 +280,12 @@ You can provide the public key as a classpath resource (called `my-public-key.pu
When using Spring Boot, this is all that is required.
The default arrangement provided by Spring Boot is equivalent to the following:
-[source,java]
+.Configure Resource Server with Custom JWTs
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
@@ -232,10 +315,47 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+import org.springframework.security.config.annotation.web.invoke
+
+@Configuration
+@EnableWebSecurity
+class SecurityConfig {
+
+ @Bean
+ fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
+ http {
+ authorizeHttpRequests {
+ authorize(anyRequest, authenticated)
+ }
+ oauth2ResourceServer {
+ jwt { }
+ }
+ }
+
+ return http.build()
+ }
+
+ @Bean
+ fun jwtDecoder(): JwtDecoder {
+ return NimbusJwtDecoder.withPublicKey(publicKey()).build()
+ }
+
+ private fun publicKey(): RSAPublicKey {
+ // ...
+ }
+
+}
+----
+=====
+
[NOTE]
====
Spring Security does not provide an endpoint for minting tokens.
-However, Spring Security does provides the `JwtEncoder` interface along with one implementation, which is `NimbusJwtEncoder`.
+However, Spring Security does provide the `JwtEncoder` interface along with one implementation, which is `NimbusJwtEncoder`.
====
[[oauth2-client]]
@@ -293,9 +413,14 @@ It is very common to require users to log in via OAuth2.
https://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect 1.0] provides a special token called the `id_token` which is designed to provide an OAuth2 Client with the ability to perform user identity verification and log users in.
In certain cases, OAuth2 can be used directly to log users in (as is the case with popular social login providers that do not implement OpenID Connect such as GitHub and Facebook).
-The following example configures the application to act as an OAuth2 Client capable of logging users in with OAuth2 or OpenID Connect.
+The following example configures the application to act as an OAuth2 Client capable of logging users in with OAuth2 or OpenID Connect:
-[source,java]
+.Configure OAuth2 Login
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
@@ -312,6 +437,30 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+import org.springframework.security.config.annotation.web.invoke
+
+@Configuration
+@EnableWebSecurity
+class SecurityConfig {
+
+ @Bean
+ fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
+ http {
+ // ...
+ oauth2Login { }
+ }
+
+ return http.build()
+ }
+
+}
+----
+=====
+
In addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ClientRegistrationRepository` bean.
The following example configures an `InMemoryClientRegistrationRepository` bean using Spring Boot configuration properties:
@@ -351,9 +500,14 @@ Without this scope, Spring Security will use OAuth2-specific components (such as
Making requests to a third party API that is protected by OAuth2 is a core use case of OAuth2 Client.
This is accomplished by authorizing a client (represented by the `OAuth2AuthorizedClient` class in Spring Security) and accessing protected resources by placing a `Bearer` token in the `Authorization` header of an outbound request.
-The following example configures the application to act as an OAuth2 Client capable of requesting protected resources from a third party API.
+The following example configures the application to act as an OAuth2 Client capable of requesting protected resources from a third party API:
-[source,java]
+.Configure OAuth2 Client
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
@@ -370,6 +524,30 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+import org.springframework.security.config.annotation.web.invoke
+
+@Configuration
+@EnableWebSecurity
+class SecurityConfig {
+
+ @Bean
+ fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
+ http {
+ // ...
+ oauth2Client { }
+ }
+
+ return http.build()
+ }
+
+}
+----
+=====
+
[NOTE]
====
The above example does not provide a way to log users in.
@@ -406,9 +584,43 @@ Spring Security provides implementations of `OAuth2AuthorizedClientManager` for
Spring Security registers a default `OAuth2AuthorizedClientManager` bean for you when one does not exist.
====
+The easiest way to use an `OAuth2AuthorizedClientManager` is via an `ExchangeFilterFunction` that intercepts requests through a `WebClient`.
+To use `WebClient`, you will need to add the `spring-webflux` dependency along with a reactive client implementation:
+
+.Add Spring WebFlux Dependency
+[tabs]
+======
+Gradle::
++
+[source,gradle,role="primary"]
+----
+implementation 'org.springframework:spring-webflux'
+implementation 'io.projectreactor.netty:reactor-netty'
+----
+
+Maven::
++
+[source,maven,role="secondary"]
+----
+
+ org.springframework
+ spring-webflux
+
+
+ io.projectreactor.netty
+ reactor-netty
+
+----
+======
+
The following example uses the default `OAuth2AuthorizedClientManager` to configure a `WebClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:
-[source,java]
+.Configure `WebClient` with `ExchangeFilterFunction`
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
public class WebClientConfig {
@@ -425,10 +637,34 @@ public class WebClientConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+@Configuration
+class WebClientConfig {
+
+ @Bean
+ fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager): WebClient {
+ val filter = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
+ return WebClient.builder()
+ .apply(filter.oauth2Configuration())
+ .build()
+ }
+
+}
+----
+=====
+
This configured `WebClient` can be used as in the following example:
[[oauth2-client-accessing-protected-resources-example]]
-[source,java]
+.Use `WebClient` to Access Protected Resources
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId;
@@ -451,8 +687,36 @@ public class MessagesController {
.block();
}
+ public record Message(String message) {
+ }
+
+}
+----
+
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId
+
+@RestController
+class MessagesController(private val webClient: WebClient) {
+
+ @GetMapping("/messages")
+ fun messages(): ResponseEntity> {
+ return webClient.get()
+ .uri("http://localhost:8090/messages")
+ .attributes(clientRegistrationId("my-oauth2-client"))
+ .retrieve()
+ .toEntityList(Message::class.java)
+ .block()!!
+ }
+
+ data class Message(val message: String)
+
}
----
+=====
[[oauth2-client-access-protected-resources-current-user]]
=== Access Protected Resources for the Current User
@@ -467,9 +731,14 @@ Other advanced scenarios exist, such as configuring one `ClientRegistration` for
All such scenarios would use the same basic configuration.
====
-The following example configures the application to act as an OAuth2 Client capable of logging the user in _and_ requesting protected resources from a third party API.
+The following example configures the application to act as an OAuth2 Client capable of logging the user in _and_ requesting protected resources from a third party API:
-[source,java]
+.Configure OAuth2 Login and OAuth2 Client
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
@@ -487,6 +756,31 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+import org.springframework.security.config.annotation.web.invoke
+
+@Configuration
+@EnableWebSecurity
+class SecurityConfig {
+
+ @Bean
+ fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
+ http {
+ // ...
+ oauth2Login { }
+ oauth2Client { }
+ }
+
+ return http.build()
+ }
+
+}
+----
+=====
+
In addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ClientRegistrationRepository` bean.
The following example configures an `InMemoryClientRegistrationRepository` bean using Spring Boot configuration properties:
@@ -521,9 +815,43 @@ Spring Security provides implementations of `OAuth2AuthorizedClientManager` for
Spring Security registers a default `OAuth2AuthorizedClientManager` bean for you when one does not exist.
====
-The following example uses the default `OAuth2AuthorizedClientManager` to configure a `WebClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request.
+The easiest way to use an `OAuth2AuthorizedClientManager` is via an `ExchangeFilterFunction` that intercepts requests through a `WebClient`.
+To use `WebClient`, you will need to add the `spring-webflux` dependency along with a reactive client implementation:
-[source,java]
+.Add Spring WebFlux Dependency
+[tabs]
+======
+Gradle::
++
+[source,gradle,role="primary"]
+----
+implementation 'org.springframework:spring-webflux'
+implementation 'io.projectreactor.netty:reactor-netty'
+----
+
+Maven::
++
+[source,maven,role="secondary"]
+----
+
+ org.springframework
+ spring-webflux
+
+
+ io.projectreactor.netty
+ reactor-netty
+
+----
+======
+
+The following example uses the default `OAuth2AuthorizedClientManager` to configure a `WebClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request:
+
+.Configure `WebClient` with `ExchangeFilterFunction`
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
public class WebClientConfig {
@@ -540,10 +868,34 @@ public class WebClientConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+@Configuration
+class WebClientConfig {
+
+ @Bean
+ fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager): WebClient {
+ val filter = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
+ return WebClient.builder()
+ .apply(filter.oauth2Configuration())
+ .build()
+ }
+
+}
+----
+=====
+
This configured `WebClient` can be used as in the following example:
[[oauth2-client-accessing-protected-resources-current-user-example]]
-[source,java]
+.Use `WebClient` to Access Protected Resources (Current User)
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@RestController
public class MessagesController {
@@ -563,9 +915,34 @@ public class MessagesController {
.block();
}
+ public record Message(String message) {
+ }
+
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+@RestController
+class MessagesController(private val webClient: WebClient) {
+
+ @GetMapping("/messages")
+ fun messages(): ResponseEntity> {
+ return webClient.get()
+ .uri("http://localhost:8090/messages")
+ .retrieve()
+ .toEntityList(Message::class.java)
+ .block()!!
+ }
+
+ data class Message(val message: String)
+
+}
+----
+=====
+
[NOTE]
====
Unlike the <>, notice that we do not need to tell Spring Security about the `clientRegistrationId` we'd like to use.
@@ -581,7 +958,12 @@ For example, Spring Security provides support for the `jwt-bearer` grant type, b
With Spring Security 6.2 and later, we can simply publish a bean for one or more `OAuth2AuthorizedClientProvider` and they will be picked up automatically.
The following example simply enables the `jwt-bearer` grant type:
-[source,java]
+.Enable `jwt-bearer` Grant Type
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
public class SecurityConfig {
@@ -594,6 +976,22 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+@Configuration
+class SecurityConfig {
+
+ @Bean
+ fun jwtBearer(): OAuth2AuthorizedClientProvider {
+ return JwtBearerOAuth2AuthorizedClientProvider()
+ }
+
+}
+----
+=====
+
A default `OAuth2AuthorizedClientManager` will be published automatically by Spring Security when one is not already provided.
[TIP]
@@ -604,7 +1002,12 @@ Any custom `OAuth2AuthorizedClientProvider` bean will also be picked up and appl
In order to achieve the above configuration prior to Spring Security 6.2, we had to publish this bean ourselves and ensure we re-enabled default grant types as well.
To understand what is being configured behind the scenes, here's what the configuration might have looked like:
-[source,java]
+.Enable `jwt-bearer` Grant Type (prior to 6.2)
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
public class SecurityConfig {
@@ -634,13 +1037,50 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+@Configuration
+class SecurityConfig {
+
+ @Bean
+ fun authorizedClientManager(
+ clientRegistrationRepository: ClientRegistrationRepository,
+ authorizedClientRepository: OAuth2AuthorizedClientRepository
+ ): OAuth2AuthorizedClientManager {
+ val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
+ .authorizationCode()
+ .refreshToken()
+ .clientCredentials()
+ .password()
+ .provider(JwtBearerOAuth2AuthorizedClientProvider())
+ .build()
+
+ val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
+ clientRegistrationRepository, authorizedClientRepository
+ )
+ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
+
+ return authorizedClientManager
+ }
+
+}
+----
+=====
+
[[oauth2-client-customize-existing-grant-type]]
=== Customize an Existing Grant Type
The ability to <> by publishing a bean also provides the opportunity for customizing an existing grant type without the need to re-define the defaults.
For example, if we want to customize the clock skew of the `OAuth2AuthorizedClientProvider` for the `client_credentials` grant, we can simply publish a bean like so:
-[source,java]
+.Customize Client Credentials Grant Type
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
public class SecurityConfig {
@@ -657,6 +1097,24 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+@Configuration
+class SecurityConfig {
+
+ @Bean
+ fun clientCredentials(): OAuth2AuthorizedClientProvider {
+ val authorizedClientProvider = ClientCredentialsOAuth2AuthorizedClientProvider()
+ authorizedClientProvider.setClockSkew(Duration.ofMinutes(5))
+ return authorizedClientProvider
+ }
+
+}
+----
+=====
+
[[oauth2-client-customize-request-parameters]]
=== Customize Token Request Parameters
@@ -667,7 +1125,12 @@ With Spring Security 6.2 and later, we can simply publish a bean of type `OAuth2
The following example customizes token request parameters for the `authorization_code` grant without the DSL:
-[source,java]
+.Customize Token Request Parameters for Authorization Code Grant
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
public class SecurityConfig {
@@ -686,12 +1149,47 @@ public class SecurityConfig {
}
private static Converter> parametersConverter() {
- // ...
+ return (grantRequest) -> {
+ MultiValueMap parameters = new LinkedMultiValueMap<>();
+ parameters.set("audience", "xyz_value");
+
+ return parameters;
+ };
}
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+@Configuration
+class SecurityConfig {
+
+ @Bean
+ fun authorizationCodeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient {
+ val requestEntityConverter = OAuth2AuthorizationCodeGrantRequestEntityConverter()
+ requestEntityConverter.addParametersConverter(parametersConverter())
+
+ val accessTokenResponseClient = DefaultAuthorizationCodeTokenResponseClient()
+ accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter)
+
+ return accessTokenResponseClient
+ }
+
+ private fun parametersConverter(): Converter> {
+ return Converter> { grantRequest ->
+ LinkedMultiValueMap().also { parameters ->
+ parameters["audience"] = "xyz_value"
+ }
+ }
+ }
+
+}
+----
+=====
+
[TIP]
====
Notice that we don't need to customize the `SecurityFilterChain` bean in this case, and can stick with the defaults.
@@ -701,7 +1199,12 @@ If using Spring Boot with no additional customizations, we can actually omit the
Prior to Spring Security 6.2, we had to ensure that this customization was applied for both OAuth2 Login (if we are using this feature) and OAuth2 Client components using the Spring Security DSL.
To understand what is being configured behind the scenes, here's what the configuration might have looked like:
-[source,java]
+.Customize Token Request Parameters for Authorization Code Grant (prior to 6.2)
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
@@ -736,21 +1239,66 @@ public class SecurityConfig {
}
private static Converter> parametersConverter() {
- return (grantRequest) -> {
- MultiValueMap parameters = new LinkedMultiValueMap<>();
- parameters.set("audience", "xyz_value");
+ // ...
+ }
- return parameters;
- };
+}
+----
+
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+import org.springframework.security.config.annotation.web.invoke
+
+@Configuration
+@EnableWebSecurity
+class SecurityConfig {
+
+ @Bean
+ fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
+ val requestEntityConverter = OAuth2AuthorizationCodeGrantRequestEntityConverter()
+ requestEntityConverter.addParametersConverter(parametersConverter())
+
+ val tokenResponseClient = DefaultAuthorizationCodeTokenResponseClient()
+ tokenResponseClient.setRequestEntityConverter(requestEntityConverter)
+
+ http {
+ authorizeHttpRequests {
+ authorize(anyRequest, authenticated)
+ }
+ oauth2Login {
+ tokenEndpoint {
+ accessTokenResponseClient = tokenResponseClient
+ }
+ }
+ oauth2Client {
+ authorizationCodeGrant {
+ accessTokenResponseClient = tokenResponseClient
+ }
+ }
+ }
+
+ return http.build()
+ }
+
+ private fun parametersConverter(): Converter> {
+ // ...
}
}
----
+=====
-In either case, for other grant types we can publish additional `OAuth2AccessTokenResponseClient` beans to override the defaults.
+For other grant types we can publish additional `OAuth2AccessTokenResponseClient` beans to override the defaults.
For example, to customize token requests for the `client_credentials` grant we can publish the following bean:
-[source,java]
+.Customize Token Request Parameters for Client Credentials Grant
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
public class SecurityConfig {
@@ -775,6 +1323,32 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+@Configuration
+class SecurityConfig {
+
+ @Bean
+ fun clientCredentialsAccessTokenResponseClient(): OAuth2AccessTokenResponseClient {
+ val requestEntityConverter = OAuth2ClientCredentialsGrantRequestEntityConverter()
+ requestEntityConverter.addParametersConverter(parametersConverter())
+
+ val accessTokenResponseClient = DefaultClientCredentialsTokenResponseClient()
+ accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter)
+
+ return accessTokenResponseClient
+ }
+
+ private fun parametersConverter(): Converter> {
+ // ...
+ }
+
+}
+----
+=====
+
Spring Security automatically resolves the following generic types of `OAuth2AccessTokenResponseClient` beans:
* `OAuth2AuthorizationCodeGrantRequest` (see `DefaultAuthorizationCodeTokenResponseClient`)
@@ -785,7 +1359,7 @@ Spring Security automatically resolves the following generic types of `OAuth2Acc
[TIP]
====
-Publishing a bean of type `OAuth2AccessTokenResponseClient` will automatically enable the `jwt-bearer` grant type without the need to configure it separately.
+Publishing a bean of type `OAuth2AccessTokenResponseClient` will automatically enable the `jwt-bearer` grant type without the need to <>.
====
[[oauth2-client-customize-rest-operations]]
@@ -798,7 +1372,12 @@ With Spring Security 6.2 and later, we can simply publish beans of type `OAuth2A
The following example customizes the `RestOperations` for all of the supported grant types:
-[source,java]
+.Customize `RestOperations` for OAuth2 Client
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
public class SecurityConfig {
@@ -856,6 +1435,62 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+@Configuration
+class SecurityConfig {
+
+ @Bean
+ fun authorizationCodeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient {
+ val accessTokenResponseClient = DefaultAuthorizationCodeTokenResponseClient()
+ accessTokenResponseClient.setRestOperations(restTemplate())
+
+ return accessTokenResponseClient
+ }
+
+ @Bean
+ fun refreshTokenAccessTokenResponseClient(): OAuth2AccessTokenResponseClient {
+ val accessTokenResponseClient = DefaultRefreshTokenTokenResponseClient()
+ accessTokenResponseClient.setRestOperations(restTemplate())
+
+ return accessTokenResponseClient
+ }
+
+ @Bean
+ fun clientCredentialsAccessTokenResponseClient(): OAuth2AccessTokenResponseClient {
+ val accessTokenResponseClient = DefaultClientCredentialsTokenResponseClient()
+ accessTokenResponseClient.setRestOperations(restTemplate())
+
+ return accessTokenResponseClient
+ }
+
+ @Bean
+ fun passwordAccessTokenResponseClient(): OAuth2AccessTokenResponseClient {
+ val accessTokenResponseClient = DefaultPasswordTokenResponseClient()
+ accessTokenResponseClient.setRestOperations(restTemplate())
+
+ return accessTokenResponseClient
+ }
+
+ @Bean
+ fun jwtBearerAccessTokenResponseClient(): OAuth2AccessTokenResponseClient {
+ val accessTokenResponseClient = DefaultJwtBearerTokenResponseClient()
+ accessTokenResponseClient.setRestOperations(restTemplate())
+
+ return accessTokenResponseClient
+ }
+
+ @Bean
+ fun restTemplate(): RestTemplate {
+ // ...
+ }
+
+}
+----
+=====
+
A default `OAuth2AuthorizedClientManager` will be published automatically by Spring Security when one is not already provided.
[TIP]
@@ -868,7 +1503,12 @@ Prior to Spring Security 6.2, we had to ensure this customization was applied to
We had to use both the Spring Security DSL (for the `authorization_code` grant) and publish a bean of type `OAuth2AuthorizedClientManager` for other grant types.
To understand what is being configured behind the scenes, here's what the configuration might have looked like:
-[source,java]
+.Customize `RestOperations` for OAuth2 Client (prior to 6.2)
+[tabs]
+=====
+Java::
++
+[source,java,role="primary"]
----
@Configuration
@EnableWebSecurity
@@ -881,9 +1521,7 @@ public class SecurityConfig {
accessTokenResponseClient.setRestOperations(restTemplate());
http
- .authorizeHttpRequests((authorize) -> authorize
- .anyRequest().authenticated()
- )
+ // ...
.oauth2Login((oauth2Login) -> oauth2Login
.tokenEndpoint((tokenEndpoint) -> tokenEndpoint
.accessTokenResponseClient(accessTokenResponseClient)
@@ -954,6 +1592,90 @@ public class SecurityConfig {
}
----
+Kotlin::
++
+[source,kotlin,role="secondary"]
+----
+import org.springframework.security.config.annotation.web.invoke
+
+@Configuration
+@EnableWebSecurity
+class SecurityConfig {
+
+ @Bean
+ fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
+ val tokenResponseClient = DefaultAuthorizationCodeTokenResponseClient()
+ tokenResponseClient.setRestOperations(restTemplate())
+
+ http {
+ // ...
+ oauth2Login {
+ tokenEndpoint {
+ accessTokenResponseClient = tokenResponseClient
+ }
+ }
+ oauth2Client {
+ authorizationCodeGrant {
+ accessTokenResponseClient = tokenResponseClient
+ }
+ }
+ }
+
+ return http.build()
+ }
+
+ @Bean
+ fun authorizedClientManager(
+ clientRegistrationRepository: ClientRegistrationRepository?,
+ authorizedClientRepository: OAuth2AuthorizedClientRepository?
+ ): OAuth2AuthorizedClientManager {
+ val refreshTokenAccessTokenResponseClient = DefaultRefreshTokenTokenResponseClient()
+ refreshTokenAccessTokenResponseClient.setRestOperations(restTemplate())
+
+ val clientCredentialsAccessTokenResponseClient = DefaultClientCredentialsTokenResponseClient()
+ clientCredentialsAccessTokenResponseClient.setRestOperations(restTemplate())
+
+ val passwordAccessTokenResponseClient = DefaultPasswordTokenResponseClient()
+ passwordAccessTokenResponseClient.setRestOperations(restTemplate())
+
+ val jwtBearerAccessTokenResponseClient = DefaultJwtBearerTokenResponseClient()
+ jwtBearerAccessTokenResponseClient.setRestOperations(restTemplate())
+
+ val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()
+ jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient)
+
+ val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
+ .authorizationCode()
+ .refreshToken { refreshToken ->
+ refreshToken.accessTokenResponseClient(refreshTokenAccessTokenResponseClient)
+ }
+ .clientCredentials { clientCredentials ->
+ clientCredentials.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient)
+ }
+ .password { password ->
+ password.accessTokenResponseClient(passwordAccessTokenResponseClient)
+ }
+ .provider(jwtBearerAuthorizedClientProvider)
+ .build()
+
+ val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
+ clientRegistrationRepository, authorizedClientRepository
+ )
+ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
+
+ return authorizedClientManager
+ }
+
+ @Bean
+ fun restTemplate(): RestTemplate {
+ // ...
+ }
+
+}
+----
+=====
+
+
[[further-reading]]
== Further Reading