diff --git a/docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc b/docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc index 886c6d78c9..0731a8b757 100644 --- a/docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc @@ -39,7 +39,7 @@ For example, `@WithMockUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)` wi ** Support for CORS was added for WebFlux (already supported in Servlets) * Redirecting to HTTPS ** Support for HTTPS redirect was added -* Web Client +* WebClient + OAuth2 Support for <> and <> environments * <> - added support for setting up an `LdapContext` from custom environment variables * <> - added support for deriving the X.509 principal via a strategy * The Look and Feel for the default login and logout pages was modernized diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc index 5fd4260b68..0c6425a254 100644 --- a/docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc @@ -2,6 +2,8 @@ include::webflux.adoc[leveloffset=+1] +include::webclient.adoc[leveloffset=+1] + include::method.adoc[leveloffset=+1] include::test.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/webclient.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/webclient.adoc new file mode 100644 index 0000000000..e55fa91ded --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/reactive/webclient.adoc @@ -0,0 +1,97 @@ += WebClient + +[NOTE] +==== +The following documentation is for use within Reactive environments. +For Servlet environments, refer to <> environments. +==== + + +Spring Framework has built in support for setting a Bearer token. + +[source,java] +---- +webClient.get() + .headers(h -> h.setBearerAuth(token)) + ... +---- + +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: + +[source,java] +---- +@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 + oauth.setDefaultOAuth2AuthorizedClient(true); + return WebClient.builder() + .filter(oauth) + .build(); +} +---- + +[[webclient-implicit]] +== Implicit OAuth2AuthorizedClient + +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. +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). + +[source,java] +---- +Mono body = this.webClient + .get() + .uri(this.uri) + .retrieve() + .bodyToMono(String.class); +---- + +[[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. + +[source,java] +---- +@GetMapping("/explicit") +Mono explicit(@RegisteredOAuth2AuthorizedClient("client-id") OAuth2AuthorizedClient authorizedClient) { + return this.webClient + .get() + .uri(this.uri) + .attributes(oauth2AuthorizedClient(authorizedClient)) + .retrieve() + .bodyToMono(String.class); +} +---- + +[[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. + +[source,java] +---- +Mono body = this.webClient + .get() + .uri(this.uri) + .attributes(clientRegistrationId("client-id")) + .retrieve() + .bodyToMono(String.class); +---- diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/index.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/index.adoc index 7568abdbd5..ee2f52094d 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/index.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/index.adoc @@ -11,6 +11,8 @@ include::ldap.adoc[] include::oauth2.adoc[] +include::webclient.adoc[] + include::jsp-taglibs.adoc[] include::jaas.adoc[] diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/webclient.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/webclient.adoc new file mode 100644 index 0000000000..fceb8f1b66 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/webclient.adoc @@ -0,0 +1,99 @@ +[[servlet-webclient]] += WebClient for Servlet Environments + +[NOTE] +==== +The following documentation is for use within Servlet environments. +For all other environments, refer to <> environments. +==== + + +Spring Framework has built in support for setting a Bearer token. + +[source,java] +---- +webClient.get() + .headers(h -> h.setBearerAuth(token)) + ... +---- + +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. + +[[servlet-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: + +[source,java] +---- +@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 + oauth.setDefaultOAuth2AuthorizedClient(true); + return WebClient.builder() + .filter(oauth) + .build(); +} +---- + +[[servlet-webclient-implicit]] +== Implicit OAuth2AuthorizedClient + +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. +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). + +[source,java] +---- +Mono body = this.webClient + .get() + .uri(this.uri) + .retrieve() + .bodyToMono(String.class); +---- + +[[servlet-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. + +[source,java] +---- +@GetMapping("/explicit") +Mono explicit(@RegisteredOAuth2AuthorizedClient("client-id") OAuth2AuthorizedClient authorizedClient) { + return this.webClient + .get() + .uri(this.uri) + .attributes(oauth2AuthorizedClient(authorizedClient)) + .retrieve() + .bodyToMono(String.class); +} +---- + + +[[servlet-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. + +[source,java] +---- +Mono body = this.webClient + .get() + .uri(this.uri) + .attributes(clientRegistrationId("client-id")) + .retrieve() + .bodyToMono(String.class); +----