Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OAuth + WebClient Support #4921

Closed
rwinch opened this Issue Dec 20, 2017 · 7 comments

Comments

Projects
None yet
4 participants
@rwinch
Copy link
Member

commented Dec 20, 2017

WebClient Support

This is now resolved. Demo usage can be found in oauth2webclient and oauth2webclient-webflux samples. A quick tour of the support:

Built In WebClient Support

WebClient has built in support for easily adding a Bearer token. For example:

webClient.get()
    .headers(h -> h.setBearerAuth(token))
    ...

Why use Spring Security Extensions?

Spring Security provides first class support for OAuth2. A few advantages of using this support are:

  • 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
  • Spring Security will automatically refresh expired tokens (if a refresh token is present)
  • Builds on other Spring Security OAuth2 support making things like using discovery of endpoints very simple
  • Users can choose to transparently include the current OAuth token or explicitly select which token should be used.

Setup

The first step is ensuring to setup the WebClient correctly.

For a Servlet environment this looks like:

@Bean
WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) {
	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository);
	// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
	oauth2.setDefaultOAuth2AuthorizedClient(true);
	return WebClient.builder()
			.apply(oauth2.oauth2Configuration())
			.build();
}

For other (i.e. WebFlux) environments it looks like:

@Bean
WebClient webClient(ReactiveClientRegistrationRepository clientRegistrationRepository,
		ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
	ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
			new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository, authorizedClientRepository);
	// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
	oauth.setDefaultOAuth2AuthorizedClient(true);
	return WebClient.builder()
			.filter(oauth)
			.build();
}

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).

Mono<String> body = this.webClient
		.get()
		.uri(this.uri)
		.retrieve()
		.bodyToMono(String.class);

Explicit OAuth2AuthorizedClient

You can also explicitly provide an OAuth2AuthorizedClient 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, the user can choose to resolve the OAuth2AuthorizedClient however they wish.

@GetMapping("/explicit")
Mono<String> explicit(@RegisteredOAuth2AuthorizedClient("client-id") OAuth2AuthorizedClient authorizedClient) {
	return this.webClient
			.get()
			.uri(this.uri)
			.attributes(oauth2AuthorizedClient(authorizedClient))
			.retrieve()
			.bodyToMono(String.class);
}

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.

Mono<String> body = this.webClient
		.get()
		.uri(this.uri)
		.attributes(clientRegistrationId("client-id"))
		.retrieve()
		.bodyToMono(String.class);

Summary (original)

We should add OAuth + WebClient support. The support for using WebClient in a Servlet environment and WebFlux must be separate because:

  • The default values will be obtained from different types of contexts. For example, Spring Security's Authentication will be obtained from the SecurityContextHolder in a Servlet environment but from the ReactiveSecurityContextHolder in WebFlux.
  • In the Servlet world we must be able to save to the HttpSession which is a blocking API. The rest of the OAuth support in the Servlet world is also using a blocking API

This is going to be broken up into multiple issues:

  • #5389 - Add Bearer Token Support
  • #5545 - Add ServletOAuth2AuthorizedClientExchangeFilterFunction
  • #5386 - Add ServerOAuth2AuthorizedClientExchangeFilterFunction
    • #5413 - Add a globally applicable token
    • #4371 - Provide support for refresh_token grant
    • #5639 - Provide support for client_credentials
@daniellavoie

This comment has been minimized.

Copy link

commented Dec 20, 2017

Here is some context :

Spring Security OAuth 2 had support for OAuth2RestTemplate that allowed clients to contact a Resource server by providing a ResourceDetail containing OAuth2 info.

Using WebClient requires the client to handle the token retrieval. Would be nice if that feature from OAuth2RestTemplate could be ported to Spring Security 5.

@daniellavoie

This comment has been minimized.

Copy link

commented Dec 21, 2017

@rwinch @jgrandja

I have an important request regarding that OAuth 2 support. Could it be packaged as a separate lib without the server related stuff? The main reason is that our SCS client using boot picks up Oauth2 dependencies transitively. This results in boot auto-configuring OAuth2 endpoints because the current jar has boot client and server classes altogether.

@jgrandja

This comment has been minimized.

Copy link
Collaborator

commented Dec 21, 2017

The client and server(s) will be in separate modules. The client module is in spring-security-oauth2-client and when we start the Resource Server work in Jan it will be in a separate module, for example, spring-security-oauth2-resource-server. Authorization server will also going into a separate module.

@daniellavoie

This comment has been minimized.

Copy link

commented Dec 21, 2017

Awesome !

@jgrandja jgrandja modified the milestones: 5.1.0.RC1, 5.1.0.M1 Dec 22, 2017

@jgrandja jgrandja modified the milestones: 5.1.0.M1, 5.1.0.M2 Mar 16, 2018

@rwinch rwinch self-assigned this May 17, 2018

@rwinch rwinch added the in progress label May 17, 2018

@rwinch rwinch modified the milestones: 5.1.0.M2, 5.1.0.RC1 May 25, 2018

@jgrandja jgrandja modified the milestones: General Backlog, 5.1.0.RC2 Aug 21, 2018

@rwinch rwinch added this to rwinch in Security Team Aug 22, 2018

rwinch added a commit that referenced this issue Sep 7, 2018

rwinch added a commit that referenced this issue Sep 7, 2018

ServerOAuth2AuthorizedClientExchangeFilterFunction clientRegistrationId
You can now provide the clientRegistrationId and
ServerOAuth2AuthorizedClientExchangeFilterFunction will look up the authorized client automatically.

Issue: gh-4921

rwinch added a commit that referenced this issue Sep 7, 2018

ServerOAuth2AuthorizedClientExchangeFilterFunction defaultOAuth2Autho…
…rizedClient

Defaults to use the OAuth2AuthenticationToken to resolve the authorized client

Issue: gh-4921

rwinch added a commit that referenced this issue Sep 7, 2018

ServerOAuth2AuthorizedClientExchangeFilterFunction default ServerWebE…
…xchange

Leverage ServerWebExchange established by ServerWebExchangeReactorContextWebFilter

Issue: gh-4921

rwinch added a commit that referenced this issue Sep 7, 2018

OAuth2AuthorizedClientResolver
Extract out a private API for shared code between the argument resolver
and WebClient support. This makes it easier to make changes in both
locations. Later we will extract this out so it is not a copy/paste
effort.

Issue: gh-4921

rwinch added a commit that referenced this issue Sep 7, 2018

authcodegrant samples->oauth2webclient samples
The authcodegrant samples were initially meant to be very simple
demonstration of authorization code flow. However, it has become
obvious since then that the real intent of the demo is how to use
the WebClient with OAuth (there is no other reason to do authorization
code flow unless you use the token to make a request).

The samples have been migrated to oauth2webclient and oauth2webclient-webflux
respectively. They have been improved:

* The sample demonstrates usage with annotations, webclient directly, form login
  oauth2Login, and public APIs
* The samples externalize the endpoint that is requested in the sample
  making it easier to try other endpoints
* The UI no longer relies on a data structure for the result of the
  endpoint also making it easier to try other endpoints

Issue: gh-4921
@rwinch

This comment has been minimized.

Copy link
Member Author

commented Sep 7, 2018

This is now resolved in master

@rwinch rwinch closed this Sep 7, 2018

@rwinch rwinch removed this from rwinch in Security Team Sep 7, 2018

@idc101

This comment has been minimized.

Copy link

commented Sep 11, 2018

Does this support authorization grant type of password?

Looking in oauth2-core/src/main/java/org/springframework/security/oauth2/core/AuthorizationGrantType.java it doesn't seem to, but I might be looking in the wrong place!

@rwinch

This comment has been minimized.

Copy link
Member Author

commented Sep 11, 2018

@idc101 Thanks for the feedback.

You are correct. We do not yet support password grant types. Please log an issue for support if you would like to see it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.