2018-09-17 23:26:33 -04:00
= WebClient
[NOTE]
====
The following documentation is for use within Reactive environments.
2021-07-30 17:56:54 -04:00
For Servlet environments, refer to xref:servlet/oauth2/oauth2-client.adoc#oauth2Client-webclient-servlet[ WebClient for Servlet] environments.
2018-09-17 23:26:33 -04:00
====
Spring Framework has built in support for setting a Bearer token.
2020-08-07 08:59:24 -04:00
====
.Java
[source,java,role="primary"]
2018-09-17 23:26:33 -04:00
----
webClient.get()
.headers(h -> h.setBearerAuth(token))
...
----
2020-08-07 08:59:24 -04:00
.Kotlin
[source,kotlin,role="secondary"]
----
webClient.get()
.headers { it.setBearerAuth(token) }
...
----
====
2018-09-17 23:26:33 -04:00
Spring Security builds on this support to provide additional benefits:
* Spring Security will automatically refresh expired tokens (if a refresh token is present)
* If an access token is requested and not present, Spring Security will automatically request the access token.
** For authorization_code this involves performing the redirect and then replaying the original request
** For client_credentials the token is simply requested and saved
* Support for the ability to transparently include the current OAuth token or explicitly select which token should be used.
[[webclient-setup]]
== WebClient OAuth2 Setup
The first step is ensuring to setup the `WebClient` correctly.
An example of setting up `WebClient` in a fully reactive environment can be found below:
2020-08-07 08:59:24 -04:00
====
.Java
[source,java,role="primary"]
2018-09-17 23:26:33 -04:00
----
@Bean
WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations,
ServerOAuth2AuthorizedClientRepository authorizedClients) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
2018-09-19 12:27:56 -04:00
// oauth.setDefaultOAuth2AuthorizedClient(true);
// (optional) set a default ClientRegistration.registrationId
// oauth.setDefaultClientRegistrationId("client-registration-id");
2018-09-17 23:26:33 -04:00
return WebClient.builder()
.filter(oauth)
.build();
}
----
2020-08-07 08:59:24 -04:00
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
fun webClient(clientRegistrations: ReactiveClientRegistrationRepository,
authorizedClients: ServerOAuth2AuthorizedClientRepository): WebClient {
val oauth = ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients)
// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
// oauth.setDefaultOAuth2AuthorizedClient(true)
// (optional) set a default ClientRegistration.registrationId
// oauth.setDefaultClientRegistrationId("client-registration-id")
return WebClient.builder()
.filter(oauth)
.build()
}
----
====
2018-09-17 23:26:33 -04:00
[[webclient-implicit]]
== Implicit OAuth2AuthorizedClient
2019-01-18 10:37:39 -05:00
If we set `defaultOAuth2AuthorizedClient` to `true` in our setup and the user authenticated with oauth2Login (i.e. OIDC), then the current authentication is used to automatically provide the access token.
2018-09-19 12:27:56 -04:00
Alternatively, if we set `defaultClientRegistrationId` to a valid `ClientRegistration` id, that registration is used to provide the access token.
2018-09-17 23:26:33 -04:00
This is convenient, but in environments where not all endpoints should get the access token, it is dangerous (you might provide the wrong access token to an endpoint).
2020-08-07 08:59:24 -04:00
====
.Java
[source,java,role="primary"]
2018-09-17 23:26:33 -04:00
----
Mono<String> body = this.webClient
.get()
.uri(this.uri)
.retrieve()
.bodyToMono(String.class);
----
2020-08-07 08:59:24 -04:00
.Kotlin
[source,kotlin,role="secondary"]
----
val body: Mono<String> = webClient
.get()
.uri(this.uri)
.retrieve()
.bodyToMono()
----
====
2018-09-17 23:26:33 -04:00
[[webclient-explicit]]
== Explicit OAuth2AuthorizedClient
The `OAuth2AuthorizedClient` can be explicitly provided by setting it on the requests attributes.
In the example below we resolve the `OAuth2AuthorizedClient` using Spring WebFlux or Spring MVC argument resolver support.
However, it does not matter how the `OAuth2AuthorizedClient` is resolved.
2020-08-07 08:59:24 -04:00
====
.Java
[source,java,role="primary"]
2018-09-17 23:26:33 -04:00
----
@GetMapping("/explicit")
Mono<String> explicit(@RegisteredOAuth2AuthorizedClient("client-id") OAuth2AuthorizedClient authorizedClient) {
return this.webClient
.get()
.uri(this.uri)
.attributes(oauth2AuthorizedClient(authorizedClient))
.retrieve()
.bodyToMono(String.class);
}
----
2020-08-07 08:59:24 -04:00
.Kotlin
[source,kotlin,role="secondary"]
----
@GetMapping("/explicit")
fun explicit(@RegisteredOAuth2AuthorizedClient("client-id") authorizedClient: OAuth2AuthorizedClient?): Mono<String> {
return this.webClient
.get()
.uri(uri)
.attributes(oauth2AuthorizedClient(authorizedClient))
.retrieve()
.bodyToMono()
}
----
====
2018-09-17 23:26:33 -04:00
[[webclient-clientregistrationid]]
== clientRegistrationId
Alternatively, it is possible to specify the `clientRegistrationId` on the request attributes and the `WebClient` will attempt to lookup the `OAuth2AuthorizedClient`.
If it is not found, one will automatically be acquired.
2020-08-07 08:59:24 -04:00
====
.Java
[source,java,role="primary"]
2018-09-17 23:26:33 -04:00
----
Mono<String> body = this.webClient
.get()
.uri(this.uri)
.attributes(clientRegistrationId("client-id"))
.retrieve()
.bodyToMono(String.class);
----
2020-08-07 08:59:24 -04:00
.Kotlin
[source,kotlin,role="secondary"]
----
val body: Mono<String> = this.webClient
.get()
.uri(uri)
.attributes(clientRegistrationId("client-id"))
.retrieve()
.bodyToMono()
----
====