|
@@ -93,7 +93,9 @@ The `OAuth2AuthorizedClientManager` is responsible for managing the authorizatio
|
|
|
|
|
|
The following code shows an example of how to register an `OAuth2AuthorizedClientManager` `@Bean` and associate it with an `OAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
|
|
The following code shows an example of how to register an `OAuth2AuthorizedClientManager` `@Bean` and associate it with an `OAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Bean
|
|
@Bean
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
@@ -117,6 +119,27 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Bean
|
|
|
|
+fun authorizedClientManager(
|
|
|
|
+ clientRegistrationRepository: ClientRegistrationRepository,
|
|
|
|
+ authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
|
|
|
|
+ val authorizedClientProvider: OAuth2AuthorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
|
|
|
+ .authorizationCode()
|
|
|
|
+ .refreshToken()
|
|
|
|
+ .clientCredentials()
|
|
|
|
+ .password()
|
|
|
|
+ .build()
|
|
|
|
+ val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
|
|
|
|
+ clientRegistrationRepository, authorizedClientRepository)
|
|
|
|
+ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
|
|
|
|
+ return authorizedClientManager
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
The following sections will go into more detail on the core components used by OAuth 2.0 Client and the configuration options available:
|
|
The following sections will go into more detail on the core components used by OAuth 2.0 Client and the configuration options available:
|
|
|
|
|
|
* <<oauth2Client-core-interface-class>>
|
|
* <<oauth2Client-core-interface-class>>
|
|
@@ -206,12 +229,21 @@ A `ClientRegistration` can be initially configured using discovery of an OpenID
|
|
|
|
|
|
`ClientRegistrations` provides convenience methods for configuring a `ClientRegistration` in this way, as can be seen in the following example:
|
|
`ClientRegistrations` provides convenience methods for configuring a `ClientRegistration` in this way, as can be seen in the following example:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
ClientRegistration clientRegistration =
|
|
ClientRegistration clientRegistration =
|
|
ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build();
|
|
ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build();
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+val clientRegistration = ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build()
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
The above code will query in series `https://idp.example.com/issuer/.well-known/openid-configuration`, and then `https://idp.example.com/.well-known/openid-configuration/issuer`, and finally `https://idp.example.com/.well-known/oauth-authorization-server/issuer`, stopping at the first to return a 200 response.
|
|
The above code will query in series `https://idp.example.com/issuer/.well-known/openid-configuration`, and then `https://idp.example.com/.well-known/openid-configuration/issuer`, and finally `https://idp.example.com/.well-known/oauth-authorization-server/issuer`, stopping at the first to return a 200 response.
|
|
|
|
|
|
As an alternative, you can use `ClientRegistrations.fromOidcIssuerLocation()` to only query the OpenID Connect Provider's Configuration endpoint.
|
|
As an alternative, you can use `ClientRegistrations.fromOidcIssuerLocation()` to only query the OpenID Connect Provider's Configuration endpoint.
|
|
@@ -234,7 +266,9 @@ The auto-configuration also registers the `ClientRegistrationRepository` as a `@
|
|
|
|
|
|
The following listing shows an example:
|
|
The following listing shows an example:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Controller
|
|
@Controller
|
|
public class OAuth2ClientController {
|
|
public class OAuth2ClientController {
|
|
@@ -254,6 +288,27 @@ public class OAuth2ClientController {
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Controller
|
|
|
|
+class OAuth2ClientController {
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private lateinit var clientRegistrationRepository: ClientRegistrationRepository
|
|
|
|
+
|
|
|
|
+ @GetMapping("/")
|
|
|
|
+ fun index(): String {
|
|
|
|
+ val oktaRegistration =
|
|
|
|
+ this.clientRegistrationRepository.findByRegistrationId("okta")
|
|
|
|
+
|
|
|
|
+ //...
|
|
|
|
+
|
|
|
|
+ return "index";
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
|
|
[[oauth2Client-authorized-client]]
|
|
[[oauth2Client-authorized-client]]
|
|
==== OAuth2AuthorizedClient
|
|
==== OAuth2AuthorizedClient
|
|
@@ -274,7 +329,9 @@ From a developer perspective, the `OAuth2AuthorizedClientRepository` or `OAuth2A
|
|
|
|
|
|
The following listing shows an example:
|
|
The following listing shows an example:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Controller
|
|
@Controller
|
|
public class OAuth2ClientController {
|
|
public class OAuth2ClientController {
|
|
@@ -296,6 +353,29 @@ public class OAuth2ClientController {
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Controller
|
|
|
|
+class OAuth2ClientController {
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private lateinit var authorizedClientService: OAuth2AuthorizedClientService
|
|
|
|
+
|
|
|
|
+ @GetMapping("/")
|
|
|
|
+ fun index(authentication: Authentication): String {
|
|
|
|
+ val authorizedClient: OAuth2AuthorizedClient =
|
|
|
|
+ this.authorizedClientService.loadAuthorizedClient("okta", authentication.getName());
|
|
|
|
+ val accessToken = authorizedClient.accessToken
|
|
|
|
+
|
|
|
|
+ ...
|
|
|
|
+
|
|
|
|
+ return "index";
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
[NOTE]
|
|
[NOTE]
|
|
Spring Boot 2.x auto-configuration registers an `OAuth2AuthorizedClientRepository` and/or `OAuth2AuthorizedClientService` `@Bean` in the `ApplicationContext`.
|
|
Spring Boot 2.x auto-configuration registers an `OAuth2AuthorizedClientRepository` and/or `OAuth2AuthorizedClientService` `@Bean` in the `ApplicationContext`.
|
|
However, the application may choose to override and register a custom `OAuth2AuthorizedClientRepository` or `OAuth2AuthorizedClientService` `@Bean`.
|
|
However, the application may choose to override and register a custom `OAuth2AuthorizedClientRepository` or `OAuth2AuthorizedClientService` `@Bean`.
|
|
@@ -328,7 +408,9 @@ The `OAuth2AuthorizedClientProviderBuilder` may be used to configure and build t
|
|
|
|
|
|
The following code shows an example of how to configure and build an `OAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
|
|
The following code shows an example of how to configure and build an `OAuth2AuthorizedClientProvider` composite that provides support for the `authorization_code`, `refresh_token`, `client_credentials` and `password` authorization grant types:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Bean
|
|
@Bean
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
@@ -352,6 +434,27 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Bean
|
|
|
|
+fun authorizedClientManager(
|
|
|
|
+ clientRegistrationRepository: ClientRegistrationRepository,
|
|
|
|
+ authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
|
|
|
|
+ val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
|
|
|
+ .authorizationCode()
|
|
|
|
+ .refreshToken()
|
|
|
|
+ .clientCredentials()
|
|
|
|
+ .password()
|
|
|
|
+ .build()
|
|
|
|
+ val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
|
|
|
|
+ clientRegistrationRepository, authorizedClientRepository)
|
|
|
|
+ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
|
|
|
|
+ return authorizedClientManager
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
When an authorization attempt succeeds, the `DefaultOAuth2AuthorizedClientManager` will delegate to the `OAuth2AuthorizationSuccessHandler`, which (by default) will save the `OAuth2AuthorizedClient` via the `OAuth2AuthorizedClientRepository`.
|
|
When an authorization attempt succeeds, the `DefaultOAuth2AuthorizedClientManager` will delegate to the `OAuth2AuthorizationSuccessHandler`, which (by default) will save the `OAuth2AuthorizedClient` via the `OAuth2AuthorizedClientRepository`.
|
|
In the case of a re-authorization failure, eg. a refresh token is no longer valid, the previously saved `OAuth2AuthorizedClient` will be removed from the `OAuth2AuthorizedClientRepository` via the `RemoveAuthorizedClientOAuth2AuthorizationFailureHandler`.
|
|
In the case of a re-authorization failure, eg. a refresh token is no longer valid, the previously saved `OAuth2AuthorizedClient` will be removed from the `OAuth2AuthorizedClientRepository` via the `RemoveAuthorizedClientOAuth2AuthorizationFailureHandler`.
|
|
The default behaviour may be customized via `setAuthorizationSuccessHandler(OAuth2AuthorizationSuccessHandler)` and `setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)`.
|
|
The default behaviour may be customized via `setAuthorizationSuccessHandler(OAuth2AuthorizationSuccessHandler)` and `setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)`.
|
|
@@ -361,7 +464,9 @@ This can be useful when you need to supply an `OAuth2AuthorizedClientProvider` w
|
|
|
|
|
|
The following code shows an example of the `contextAttributesMapper`:
|
|
The following code shows an example of the `contextAttributesMapper`:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Bean
|
|
@Bean
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
@@ -404,6 +509,46 @@ private Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesM
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Bean
|
|
|
|
+fun authorizedClientManager(
|
|
|
|
+ clientRegistrationRepository: ClientRegistrationRepository,
|
|
|
|
+ authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
|
|
|
|
+ val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
|
|
|
+ .password()
|
|
|
|
+ .refreshToken()
|
|
|
|
+ .build()
|
|
|
|
+ val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
|
|
|
|
+ clientRegistrationRepository, authorizedClientRepository)
|
|
|
|
+ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
|
|
|
|
+
|
|
|
|
+ // Assuming the `username` and `password` are supplied as `HttpServletRequest` parameters,
|
|
|
|
+ // map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
|
|
|
|
+ authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())
|
|
|
|
+ return authorizedClientManager
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, MutableMap<String, Any>> {
|
|
|
|
+ return Function { authorizeRequest ->
|
|
|
|
+ var contextAttributes: MutableMap<String, Any> = mutableMapOf()
|
|
|
|
+ val servletRequest: HttpServletRequest = authorizeRequest.getAttribute(HttpServletRequest::class.java.name)
|
|
|
|
+ val username: String = servletRequest.getParameter(OAuth2ParameterNames.USERNAME)
|
|
|
|
+ val password: String = servletRequest.getParameter(OAuth2ParameterNames.PASSWORD)
|
|
|
|
+ if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
|
|
|
|
+ contextAttributes = hashMapOf()
|
|
|
|
+
|
|
|
|
+ // `PasswordOAuth2AuthorizedClientProvider` requires both attributes
|
|
|
|
+ contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username
|
|
|
|
+ contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password
|
|
|
|
+ }
|
|
|
|
+ contextAttributes
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
The `DefaultOAuth2AuthorizedClientManager` is designed to be used *_within_* the context of a `HttpServletRequest`.
|
|
The `DefaultOAuth2AuthorizedClientManager` is designed to be used *_within_* the context of a `HttpServletRequest`.
|
|
When operating *_outside_* of a `HttpServletRequest` context, use `AuthorizedClientServiceOAuth2AuthorizedClientManager` instead.
|
|
When operating *_outside_* of a `HttpServletRequest` context, use `AuthorizedClientServiceOAuth2AuthorizedClientManager` instead.
|
|
|
|
|
|
@@ -413,7 +558,9 @@ An OAuth 2.0 Client configured with the `client_credentials` grant type can be c
|
|
|
|
|
|
The following code shows an example of how to configure an `AuthorizedClientServiceOAuth2AuthorizedClientManager` that provides support for the `client_credentials` grant type:
|
|
The following code shows an example of how to configure an `AuthorizedClientServiceOAuth2AuthorizedClientManager` that provides support for the `client_credentials` grant type:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Bean
|
|
@Bean
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
@@ -434,6 +581,24 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Bean
|
|
|
|
+fun authorizedClientManager(
|
|
|
|
+ clientRegistrationRepository: ClientRegistrationRepository,
|
|
|
|
+ authorizedClientService: OAuth2AuthorizedClientService): OAuth2AuthorizedClientManager {
|
|
|
|
+ val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
|
|
|
+ .clientCredentials()
|
|
|
|
+ .build()
|
|
|
|
+ val authorizedClientManager = AuthorizedClientServiceOAuth2AuthorizedClientManager(
|
|
|
|
+ clientRegistrationRepository, authorizedClientService)
|
|
|
|
+ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
|
|
|
|
+ return authorizedClientManager
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
|
|
|
|
[[oauth2Client-auth-grant-support]]
|
|
[[oauth2Client-auth-grant-support]]
|
|
=== Authorization Grant Support
|
|
=== Authorization Grant Support
|
|
@@ -545,7 +710,9 @@ OPTIONAL. Space delimited, case sensitive list of ASCII string values that speci
|
|
|
|
|
|
The following example shows how to configure the `DefaultOAuth2AuthorizationRequestResolver` with a `Consumer<OAuth2AuthorizationRequest.Builder>` that customizes the Authorization Request for `oauth2Login()`, by including the request parameter `prompt=consent`.
|
|
The following example shows how to configure the `DefaultOAuth2AuthorizationRequestResolver` with a `Consumer<OAuth2AuthorizationRequest.Builder>` that customizes the Authorization Request for `oauth2Login()`, by including the request parameter `prompt=consent`.
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@EnableWebSecurity
|
|
@EnableWebSecurity
|
|
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
|
|
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
|
|
@@ -587,6 +754,47 @@ public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@EnableWebSecurity
|
|
|
|
+class SecurityConfig : WebSecurityConfigurerAdapter() {
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private lateinit var customClientRegistrationRepository: ClientRegistrationRepository
|
|
|
|
+
|
|
|
|
+ override fun configure(http: HttpSecurity) {
|
|
|
|
+ http {
|
|
|
|
+ authorizeRequests {
|
|
|
|
+ authorize(anyRequest, authenticated)
|
|
|
|
+ }
|
|
|
|
+ oauth2Login {
|
|
|
|
+ authorizationEndpoint {
|
|
|
|
+ authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private fun authorizationRequestResolver(
|
|
|
|
+ clientRegistrationRepository: ClientRegistrationRepository?): OAuth2AuthorizationRequestResolver? {
|
|
|
|
+ val authorizationRequestResolver = DefaultOAuth2AuthorizationRequestResolver(
|
|
|
|
+ clientRegistrationRepository, "/oauth2/authorization")
|
|
|
|
+ authorizationRequestResolver.setAuthorizationRequestCustomizer(
|
|
|
|
+ authorizationRequestCustomizer())
|
|
|
|
+ return authorizationRequestResolver
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
|
|
|
|
+ return Consumer { customizer ->
|
|
|
|
+ customizer
|
|
|
|
+ .additionalParameters { params -> params["prompt"] = "consent" }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
For the simple use case, where the additional request parameter is always the same for a specific provider, it may be added directly in the `authorization-uri` property.
|
|
For the simple use case, where the additional request parameter is always the same for a specific provider, it may be added directly in the `authorization-uri` property.
|
|
|
|
|
|
For example, if the value for the request parameter `prompt` is always `consent` for the provider `okta`, than simply configure as follows:
|
|
For example, if the value for the request parameter `prompt` is always `consent` for the provider `okta`, than simply configure as follows:
|
|
@@ -610,7 +818,9 @@ Alternatively, if your requirements are more advanced, you can take full control
|
|
|
|
|
|
The following example shows a variation of `authorizationRequestCustomizer()` from the preceding example, and instead overrides the `OAuth2AuthorizationRequest.authorizationRequestUri` property.
|
|
The following example shows a variation of `authorizationRequestCustomizer()` from the preceding example, and instead overrides the `OAuth2AuthorizationRequest.authorizationRequestUri` property.
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
|
|
private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
|
|
return customizer -> customizer
|
|
return customizer -> customizer
|
|
@@ -619,6 +829,21 @@ private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomi
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
|
|
|
|
+ return Consumer { customizer: OAuth2AuthorizationRequest.Builder ->
|
|
|
|
+ customizer
|
|
|
|
+ .authorizationRequestUri { uriBuilder: UriBuilder ->
|
|
|
|
+ uriBuilder
|
|
|
|
+ .queryParam("prompt", "consent").build()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
|
|
|
|
===== Storing the Authorization Request
|
|
===== Storing the Authorization Request
|
|
|
|
|
|
@@ -705,7 +930,9 @@ IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representa
|
|
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultAuthorizationCodeTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
|
|
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultAuthorizationCodeTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
|
|
The default `RestOperations` is configured as follows:
|
|
The default `RestOperations` is configured as follows:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
new FormHttpMessageConverter(),
|
|
new FormHttpMessageConverter(),
|
|
@@ -714,6 +941,17 @@ RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
|
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+val restTemplate = RestTemplate(listOf(
|
|
|
|
+ FormHttpMessageConverter(),
|
|
|
|
+ OAuth2AccessTokenResponseHttpMessageConverter()))
|
|
|
|
+
|
|
|
|
+restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
|
|
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
|
|
|
|
|
|
`OAuth2AccessTokenResponseHttpMessageConverter` is a `HttpMessageConverter` for an OAuth 2.0 Access Token Response.
|
|
`OAuth2AccessTokenResponseHttpMessageConverter` is a `HttpMessageConverter` for an OAuth 2.0 Access Token Response.
|
|
@@ -806,7 +1044,9 @@ IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representa
|
|
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultRefreshTokenTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
|
|
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultRefreshTokenTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
|
|
The default `RestOperations` is configured as follows:
|
|
The default `RestOperations` is configured as follows:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
new FormHttpMessageConverter(),
|
|
new FormHttpMessageConverter(),
|
|
@@ -815,6 +1055,17 @@ RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
|
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+val restTemplate = RestTemplate(listOf(
|
|
|
|
+ FormHttpMessageConverter(),
|
|
|
|
+ OAuth2AccessTokenResponseHttpMessageConverter()))
|
|
|
|
+
|
|
|
|
+restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
|
|
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
|
|
|
|
|
|
`OAuth2AccessTokenResponseHttpMessageConverter` is a `HttpMessageConverter` for an OAuth 2.0 Access Token Response.
|
|
`OAuth2AccessTokenResponseHttpMessageConverter` is a `HttpMessageConverter` for an OAuth 2.0 Access Token Response.
|
|
@@ -825,7 +1076,9 @@ It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error
|
|
|
|
|
|
Whether you customize `DefaultRefreshTokenTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
|
|
Whether you customize `DefaultRefreshTokenTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
// Customize
|
|
// Customize
|
|
OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...
|
|
OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...
|
|
@@ -841,6 +1094,23 @@ OAuth2AuthorizedClientProvider authorizedClientProvider =
|
|
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
|
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+// Customize
|
|
|
|
+val refreshTokenTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...
|
|
|
|
+
|
|
|
|
+val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
|
|
|
+ .authorizationCode()
|
|
|
|
+ .refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }
|
|
|
|
+ .build()
|
|
|
|
+
|
|
|
|
+...
|
|
|
|
+
|
|
|
|
+authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
[NOTE]
|
|
[NOTE]
|
|
`OAuth2AuthorizedClientProviderBuilder.builder().refreshToken()` configures a `RefreshTokenOAuth2AuthorizedClientProvider`,
|
|
`OAuth2AuthorizedClientProviderBuilder.builder().refreshToken()` configures a `RefreshTokenOAuth2AuthorizedClientProvider`,
|
|
which is an implementation of an `OAuth2AuthorizedClientProvider` for the Refresh Token grant.
|
|
which is an implementation of an `OAuth2AuthorizedClientProvider` for the Refresh Token grant.
|
|
@@ -880,7 +1150,9 @@ IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representa
|
|
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultClientCredentialsTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
|
|
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultClientCredentialsTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
|
|
The default `RestOperations` is configured as follows:
|
|
The default `RestOperations` is configured as follows:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
new FormHttpMessageConverter(),
|
|
new FormHttpMessageConverter(),
|
|
@@ -889,6 +1161,17 @@ RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
|
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+val restTemplate = RestTemplate(listOf(
|
|
|
|
+ FormHttpMessageConverter(),
|
|
|
|
+ OAuth2AccessTokenResponseHttpMessageConverter()))
|
|
|
|
+
|
|
|
|
+restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
|
|
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
|
|
|
|
|
|
`OAuth2AccessTokenResponseHttpMessageConverter` is a `HttpMessageConverter` for an OAuth 2.0 Access Token Response.
|
|
`OAuth2AccessTokenResponseHttpMessageConverter` is a `HttpMessageConverter` for an OAuth 2.0 Access Token Response.
|
|
@@ -899,7 +1182,9 @@ It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error
|
|
|
|
|
|
Whether you customize `DefaultClientCredentialsTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
|
|
Whether you customize `DefaultClientCredentialsTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
// Customize
|
|
// Customize
|
|
OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...
|
|
OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...
|
|
@@ -914,6 +1199,22 @@ OAuth2AuthorizedClientProvider authorizedClientProvider =
|
|
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
|
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+// Customize
|
|
|
|
+val clientCredentialsTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...
|
|
|
|
+
|
|
|
|
+val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
|
|
|
+ .clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }
|
|
|
|
+ .build()
|
|
|
|
+
|
|
|
|
+...
|
|
|
|
+
|
|
|
|
+authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
[NOTE]
|
|
[NOTE]
|
|
`OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsOAuth2AuthorizedClientProvider`,
|
|
`OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials()` configures a `ClientCredentialsOAuth2AuthorizedClientProvider`,
|
|
which is an implementation of an `OAuth2AuthorizedClientProvider` for the Client Credentials grant.
|
|
which is an implementation of an `OAuth2AuthorizedClientProvider` for the Client Credentials grant.
|
|
@@ -941,7 +1242,9 @@ spring:
|
|
|
|
|
|
...and the `OAuth2AuthorizedClientManager` `@Bean`:
|
|
...and the `OAuth2AuthorizedClientManager` `@Bean`:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Bean
|
|
@Bean
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
@@ -962,9 +1265,29 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Bean
|
|
|
|
+fun authorizedClientManager(
|
|
|
|
+ clientRegistrationRepository: ClientRegistrationRepository,
|
|
|
|
+ authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
|
|
|
|
+ val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
|
|
|
+ .clientCredentials()
|
|
|
|
+ .build()
|
|
|
|
+ val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
|
|
|
|
+ clientRegistrationRepository, authorizedClientRepository)
|
|
|
|
+ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
|
|
|
|
+ return authorizedClientManager
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
You may obtain the `OAuth2AccessToken` as follows:
|
|
You may obtain the `OAuth2AccessToken` as follows:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Controller
|
|
@Controller
|
|
public class OAuth2ClientController {
|
|
public class OAuth2ClientController {
|
|
@@ -995,6 +1318,36 @@ public class OAuth2ClientController {
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+class OAuth2ClientController {
|
|
|
|
+
|
|
|
|
+ @Autowired
|
|
|
|
+ private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager
|
|
|
|
+
|
|
|
|
+ @GetMapping("/")
|
|
|
|
+ fun index(authentication: Authentication?,
|
|
|
|
+ servletRequest: HttpServletRequest,
|
|
|
|
+ servletResponse: HttpServletResponse): String {
|
|
|
|
+ val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
|
|
|
|
+ .principal(authentication)
|
|
|
|
+ .attributes(Consumer { attrs: MutableMap<String, Any> ->
|
|
|
|
+ attrs[HttpServletRequest::class.java.name] = servletRequest
|
|
|
|
+ attrs[HttpServletResponse::class.java.name] = servletResponse
|
|
|
|
+ })
|
|
|
|
+ .build()
|
|
|
|
+ val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
|
|
|
|
+ val accessToken: OAuth2AccessToken = authorizedClient.accessToken
|
|
|
|
+
|
|
|
|
+ ...
|
|
|
|
+
|
|
|
|
+ return "index"
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
[NOTE]
|
|
[NOTE]
|
|
`HttpServletRequest` and `HttpServletResponse` are both OPTIONAL attributes.
|
|
`HttpServletRequest` and `HttpServletResponse` are both OPTIONAL attributes.
|
|
If not provided, it will default to `ServletRequestAttributes` using `RequestContextHolder.getRequestAttributes()`.
|
|
If not provided, it will default to `ServletRequestAttributes` using `RequestContextHolder.getRequestAttributes()`.
|
|
@@ -1031,7 +1384,9 @@ IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representa
|
|
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultPasswordTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
|
|
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultPasswordTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
|
|
The default `RestOperations` is configured as follows:
|
|
The default `RestOperations` is configured as follows:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
new FormHttpMessageConverter(),
|
|
new FormHttpMessageConverter(),
|
|
@@ -1040,6 +1395,17 @@ RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
|
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
|
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+val restTemplate = RestTemplate(listOf(
|
|
|
|
+ FormHttpMessageConverter(),
|
|
|
|
+ OAuth2AccessTokenResponseHttpMessageConverter()))
|
|
|
|
+
|
|
|
|
+restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
|
|
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
|
|
|
|
|
|
`OAuth2AccessTokenResponseHttpMessageConverter` is a `HttpMessageConverter` for an OAuth 2.0 Access Token Response.
|
|
`OAuth2AccessTokenResponseHttpMessageConverter` is a `HttpMessageConverter` for an OAuth 2.0 Access Token Response.
|
|
@@ -1050,7 +1416,9 @@ It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error
|
|
|
|
|
|
Whether you customize `DefaultPasswordTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
|
|
Whether you customize `DefaultPasswordTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
// Customize
|
|
// Customize
|
|
OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordTokenResponseClient = ...
|
|
OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordTokenResponseClient = ...
|
|
@@ -1066,6 +1434,22 @@ OAuth2AuthorizedClientProvider authorizedClientProvider =
|
|
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
|
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+val passwordTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> = ...
|
|
|
|
+
|
|
|
|
+val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
|
|
|
+ .password { it.accessTokenResponseClient(passwordTokenResponseClient) }
|
|
|
|
+ .refreshToken()
|
|
|
|
+ .build()
|
|
|
|
+
|
|
|
|
+...
|
|
|
|
+
|
|
|
|
+authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
[NOTE]
|
|
[NOTE]
|
|
`OAuth2AuthorizedClientProviderBuilder.builder().password()` configures a `PasswordOAuth2AuthorizedClientProvider`,
|
|
`OAuth2AuthorizedClientProviderBuilder.builder().password()` configures a `PasswordOAuth2AuthorizedClientProvider`,
|
|
which is an implementation of an `OAuth2AuthorizedClientProvider` for the Resource Owner Password Credentials grant.
|
|
which is an implementation of an `OAuth2AuthorizedClientProvider` for the Resource Owner Password Credentials grant.
|
|
@@ -1093,7 +1477,9 @@ spring:
|
|
|
|
|
|
...and the `OAuth2AuthorizedClientManager` `@Bean`:
|
|
...and the `OAuth2AuthorizedClientManager` `@Bean`:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Bean
|
|
@Bean
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
public OAuth2AuthorizedClientManager authorizedClientManager(
|
|
@@ -1135,10 +1521,51 @@ private Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesM
|
|
};
|
|
};
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Bean
|
|
|
|
+fun authorizedClientManager(
|
|
|
|
+ clientRegistrationRepository: ClientRegistrationRepository,
|
|
|
|
+ authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
|
|
|
|
+ val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
|
|
|
|
+ .password()
|
|
|
|
+ .refreshToken()
|
|
|
|
+ .build()
|
|
|
|
+ val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
|
|
|
|
+ clientRegistrationRepository, authorizedClientRepository)
|
|
|
|
+ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
|
|
|
|
+
|
|
|
|
+ // Assuming the `username` and `password` are supplied as `HttpServletRequest` parameters,
|
|
|
|
+ // map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
|
|
|
|
+ authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())
|
|
|
|
+ return authorizedClientManager
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, MutableMap<String, Any>> {
|
|
|
|
+ return Function { authorizeRequest ->
|
|
|
|
+ var contextAttributes: MutableMap<String, Any> = mutableMapOf()
|
|
|
|
+ val servletRequest: HttpServletRequest = authorizeRequest.getAttribute(HttpServletRequest::class.java.name)
|
|
|
|
+ val username = servletRequest.getParameter(OAuth2ParameterNames.USERNAME)
|
|
|
|
+ val password = servletRequest.getParameter(OAuth2ParameterNames.PASSWORD)
|
|
|
|
+ if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
|
|
|
|
+ contextAttributes = hashMapOf()
|
|
|
|
+
|
|
|
|
+ // `PasswordOAuth2AuthorizedClientProvider` requires both attributes
|
|
|
|
+ contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username
|
|
|
|
+ contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password
|
|
|
|
+ }
|
|
|
|
+ contextAttributes
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
|
|
You may obtain the `OAuth2AccessToken` as follows:
|
|
You may obtain the `OAuth2AccessToken` as follows:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Controller
|
|
@Controller
|
|
public class OAuth2ClientController {
|
|
public class OAuth2ClientController {
|
|
@@ -1169,6 +1596,36 @@ public class OAuth2ClientController {
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Controller
|
|
|
|
+class OAuth2ClientController {
|
|
|
|
+ @Autowired
|
|
|
|
+ private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager
|
|
|
|
+
|
|
|
|
+ @GetMapping("/")
|
|
|
|
+ fun index(authentication: Authentication?,
|
|
|
|
+ servletRequest: HttpServletRequest,
|
|
|
|
+ servletResponse: HttpServletResponse): String {
|
|
|
|
+ val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
|
|
|
|
+ .principal(authentication)
|
|
|
|
+ .attributes(Consumer {
|
|
|
|
+ it[HttpServletRequest::class.java.name] = servletRequest
|
|
|
|
+ it[HttpServletResponse::class.java.name] = servletResponse
|
|
|
|
+ })
|
|
|
|
+ .build()
|
|
|
|
+ val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
|
|
|
|
+ val accessToken: OAuth2AccessToken = authorizedClient.accessToken
|
|
|
|
+
|
|
|
|
+ ...
|
|
|
|
+
|
|
|
|
+ return "index"
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
[NOTE]
|
|
[NOTE]
|
|
`HttpServletRequest` and `HttpServletResponse` are both OPTIONAL attributes.
|
|
`HttpServletRequest` and `HttpServletResponse` are both OPTIONAL attributes.
|
|
If not provided, it will default to `ServletRequestAttributes` using `RequestContextHolder.getRequestAttributes()`.
|
|
If not provided, it will default to `ServletRequestAttributes` using `RequestContextHolder.getRequestAttributes()`.
|
|
@@ -1184,7 +1641,9 @@ If not provided, it will default to `ServletRequestAttributes` using `RequestCon
|
|
The `@RegisteredOAuth2AuthorizedClient` annotation provides the capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`.
|
|
The `@RegisteredOAuth2AuthorizedClient` annotation provides the capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`.
|
|
This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `OAuth2AuthorizedClientManager` or `OAuth2AuthorizedClientService`.
|
|
This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `OAuth2AuthorizedClientManager` or `OAuth2AuthorizedClientService`.
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Controller
|
|
@Controller
|
|
public class OAuth2ClientController {
|
|
public class OAuth2ClientController {
|
|
@@ -1200,6 +1659,23 @@ public class OAuth2ClientController {
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Controller
|
|
|
|
+class OAuth2ClientController {
|
|
|
|
+ @GetMapping("/")
|
|
|
|
+ fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
|
|
|
|
+ val accessToken = authorizedClient.accessToken
|
|
|
|
+
|
|
|
|
+ ...
|
|
|
|
+
|
|
|
|
+ return "index"
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses an <<oauth2Client-authorized-manager-provider, OAuth2AuthorizedClientManager>> and therefore inherits it's capabilities.
|
|
The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses an <<oauth2Client-authorized-manager-provider, OAuth2AuthorizedClientManager>> and therefore inherits it's capabilities.
|
|
|
|
|
|
|
|
|
|
@@ -1219,7 +1695,9 @@ It directly uses an <<oauth2Client-authorized-manager-provider, OAuth2Authorized
|
|
|
|
|
|
The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
|
|
The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Bean
|
|
@Bean
|
|
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
|
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
|
@@ -1231,6 +1709,18 @@ WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Bean
|
|
|
|
+fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
|
|
|
|
+ val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
|
|
|
|
+ return WebClient.builder()
|
|
|
|
+ .apply(oauth2Client.oauth2Configuration())
|
|
|
|
+ .build()
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
|
|
==== Providing the Authorized Client
|
|
==== Providing the Authorized Client
|
|
|
|
|
|
@@ -1238,7 +1728,9 @@ The `ServletOAuth2AuthorizedClientExchangeFilterFunction` determines the client
|
|
|
|
|
|
The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:
|
|
The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@GetMapping("/")
|
|
@GetMapping("/")
|
|
public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
|
|
public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
|
|
@@ -1257,11 +1749,35 @@ public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedCl
|
|
return "index";
|
|
return "index";
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
+
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@GetMapping("/")
|
|
|
|
+fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
|
|
|
|
+ val resourceUri: String = ...
|
|
|
|
+ val body: String = webClient
|
|
|
|
+ .get()
|
|
|
|
+ .uri(resourceUri)
|
|
|
|
+ .attributes(oauth2AuthorizedClient(authorizedClient)) <1>
|
|
|
|
+ .retrieve()
|
|
|
|
+ .bodyToMono()
|
|
|
|
+ .block()
|
|
|
|
+
|
|
|
|
+ ...
|
|
|
|
+
|
|
|
|
+ return "index"
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
<1> `oauth2AuthorizedClient()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
|
|
<1> `oauth2AuthorizedClient()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
|
|
|
|
|
|
The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
|
|
The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@GetMapping("/")
|
|
@GetMapping("/")
|
|
public String index() {
|
|
public String index() {
|
|
@@ -1280,6 +1796,28 @@ public String index() {
|
|
return "index";
|
|
return "index";
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
+
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@GetMapping("/")
|
|
|
|
+fun index(): String {
|
|
|
|
+ val resourceUri: String = ...
|
|
|
|
+
|
|
|
|
+ val body: String = webClient
|
|
|
|
+ .get()
|
|
|
|
+ .uri(resourceUri)
|
|
|
|
+ .attributes(clientRegistrationId("okta")) <1>
|
|
|
|
+ .retrieve()
|
|
|
|
+ .bodyToMono()
|
|
|
|
+ .block()
|
|
|
|
+
|
|
|
|
+ ...
|
|
|
|
+
|
|
|
|
+ return "index"
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
<1> `clientRegistrationId()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
|
|
<1> `clientRegistrationId()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
|
|
|
|
|
|
|
|
|
|
@@ -1291,7 +1829,9 @@ If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authe
|
|
|
|
|
|
The following code shows the specific configuration:
|
|
The following code shows the specific configuration:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Bean
|
|
@Bean
|
|
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
|
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
|
@@ -1304,6 +1844,20 @@ WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Bean
|
|
|
|
+fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
|
|
|
|
+ val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
|
|
|
|
+ oauth2Client.setDefaultOAuth2AuthorizedClient(true)
|
|
|
|
+ return WebClient.builder()
|
|
|
|
+ .apply(oauth2Client.oauth2Configuration())
|
|
|
|
+ .build()
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
[WARNING]
|
|
[WARNING]
|
|
It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
|
|
It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
|
|
|
|
|
|
@@ -1311,7 +1865,9 @@ Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a
|
|
|
|
|
|
The following code shows the specific configuration:
|
|
The following code shows the specific configuration:
|
|
|
|
|
|
-[source,java]
|
|
|
|
|
|
+====
|
|
|
|
+.Java
|
|
|
|
+[source,java,role="primary"]
|
|
----
|
|
----
|
|
@Bean
|
|
@Bean
|
|
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
|
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
|
@@ -1324,5 +1880,19 @@ WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
|
|
}
|
|
}
|
|
----
|
|
----
|
|
|
|
|
|
|
|
+.Kotlin
|
|
|
|
+[source,kotlin,role="secondary"]
|
|
|
|
+----
|
|
|
|
+@Bean
|
|
|
|
+fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
|
|
|
|
+ val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
|
|
|
|
+ oauth2Client.setDefaultClientRegistrationId("okta")
|
|
|
|
+ return WebClient.builder()
|
|
|
|
+ .apply(oauth2Client.oauth2Configuration())
|
|
|
|
+ .build()
|
|
|
|
+}
|
|
|
|
+----
|
|
|
|
+====
|
|
|
|
+
|
|
[WARNING]
|
|
[WARNING]
|
|
It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
|
|
It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
|