spring-projects / spring-security

Spring Security
http://spring.io/projects/spring-security
Apache License 2.0
8.84k stars 5.91k forks source link

Add support for requesting protected resources with `RestClient` similar to `ServletBearerExchangeFilterFunction` #15820

Open azizabah opened 2 months ago

azizabah commented 2 months ago

Expected Behavior It would be nice if the RestClient supported an equivalent of ServletBearerExchangeFilterFunction. This would allow us to easily grab a user's bearer token and pass that on to subsequent client calls without having to explicitly grab the header and token etc.

Current Behavior Currently I can implement this very easily for a WebClient like this:

@Bean
  public WebClient profileServiceWebClient(WebClient.Builder webClientBuilder) {
    return webClientBuilder.filter(new ServletBearerExchangeFilterFunction()).baseUrl(profileServiceBaseUrl)
        .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).build();
  }

As far as I know there is no equivalent implementation for the new RestClient. Context This issue is seen as a pretty large blocker for code bases that have to pull a dependency on Spring Boot Starter Webflux (or equivalent) to use WebClient when they are not using a reactive code base. It would be much more preferable to not have to pull that dependency and not have to use reactive code inside a non-reactive code base.

sjohnr commented 1 month ago

@ch4mpy thanks for providing your thoughts! Most of the features you're sketching out here would be applicable to Spring Boot, not Spring Security. You are welcome to open an issue there. I agree that configuration properties are nice, but this style of feature is akin to programming with yaml, which I don't believe is specifically the purpose of Spring Boot's auto-configuration features. You would need to clarify that with the Boot team however, as I can't speak for them. Of course, it's fine for a 3rd party library such as yours to provide them though.

Maybe could the framework provide a few implementations for each of these two strategies?

Let's stay focused on this ticket which is aimed at providing a ClientHttpRequestInterceptor for OAuth2 Resource Server. If you have other ideas, please open a new issue with those.

ch4mpy commented 3 weeks ago

In a starter of mine, what I use as ClientHttpRequestInterceptor to forward an access token in the security context of a resource server is the following:

ClientHttpRequestInterceptor forwardingClientHttpRequestInterceptor() {
  return (HttpRequest request, byte[] body, ClientHttpRequestExecution execution) -> {
    final var auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth != null && auth.getPrincipal() instanceof OAuth2Token oauth2Token) {
      request.getHeaders().setBearerAuth(oauth2Token.getTokenValue());
    }
    return execution.execute(request, body);
  };
}

This should work on any resource server - using a JWT decoder or introspection - but those using a custom authentication converter injecting as principal something that isn't implementing OAuth2Token (which wouldn't be wise).

I have samples in this other repo:

@sjohnr I just moved auto-configuration requests to this Boot ticket.