Skip to content

[Enahncement request] [spring-boot-starter-oauth2-client] ability to provide custom RestClient instead of using RestTemplate for provider get token observablity #16389

Closed
@patpatpat123

Description

@patpatpat123

Context

How has this issue affected you?
Without this enhancement request, we are blind in production for issues getting the token using spring-boot-starter-oauth2-client

What are you trying to accomplish?
Observability for the call to get the token

What other alternatives have you considered?
Are you aware of any workarounds?
Dropping spring-boot-starter-oauth2-client in favor of writing our own two calls with the RestClient, which we would like to avoid (we like spring-boot-starter-oauth2-client)

Current Behavior

Here is our code:

@Configuration
public class RestClientConfig {

    @Bean
    public RestClient restClient(OAuth2AuthorizedClientManager authorizedClientManager, RestClient.Builder restClientBuilder) {
        OAuth2ClientHttpRequestInterceptor interceptor = new OAuth2ClientHttpRequestInterceptor(authorizedClientManager);
        return restClientBuilder
                .requestInterceptor(interceptor)
                .build();
    }
@RestController
public class LessonsController {

    private final RestClient restClient;

    public LessonsController(RestClient restClient) {
        this.restClient = restClient;
    }

    @GetMapping("/lessons")
    public String fetchLessons() {
        return restClient.get()
                .uri("https://someprotectedresourceneedingtoken")
                .attributes(clientRegistrationId("my-client"))
                .retrieve()
                .body(String.class);
    }
}
spring:
  application:
    name: client-application
  security:
    oauth2:
      client:
        registration:
          my-client:
            provider: the-provider
            client-id: someusername
            client-secret: somepassword
            authorization-grant-type: client_credentials
            scope:
              - resolve
              - download
        provider:
          ssa-provider:
            token-uri: https://somethirdparty.com/token
logging:
  level:
    root: DEBUG

With this code, we would get the following observation. (the get part is important)
Screenshot 2025-01-10 at 00 10 13

2025-01-09T23:59:01.308+08:00 DEBUG 4250 --- [client-application] [nio-8080-exec-2] [29e0fac8ee9435452ddc0e91ac67b652-9c95bddc85f879bb] s.n.www.protocol.http.HttpURLConnection  : sun.net.www.MessageHeader@10ce6bfd 8 pairs: {POST /token HTTP/1.1: null}{Accept: application/json;charset=UTF-8}{Content-Type: application/x-www-form-urlencoded;charset=UTF-8}{...5}{User-Agent: Java/23}{Host: ....com}{Connection: keep-alive}{Content-Length: 76}
2025-01-09T23:59:01.420+08:00 DEBUG 4250 --- [client-application] [nio-8080-exec-2] [29e0fac8ee9435452ddc0e91ac67b652-9c95bddc85f879bb] s.n.www.protocol.http.HttpURLConnection  : sun.net.www.MessageHeader@67f01113 13 pairs: {null: HTTP/1.1 200}{Date: Thu, 09 Jan 2025 15:59:01 GMT}{Content-Type: application/json;charset=UTF-8}{Transfer-Encoding: chunked}{Connection: keep-alive}{Vary: origin,access-control-request-method,access-control-request-headers,accept-encoding}{X-Content-Type-Options: nosniff}{X-XSS-Protection: 0}{Cache-Control: no-cache, no-store, max-age=0, must-revalidate}{Pragma: no-cache}{Expires: 0}{Strict-Transport-Security: max-age=31536000 ; includeSubDomains}{X-Frame-Options: DENY}
2025-01-09T23:59:01.420+08:00 DEBUG 4250 --- [client-application] [nio-8080-exec-2] [29e0fac8ee9435452ddc0e91ac67b652-9c95bddc85f879bb] o.s.web.client.RestTemplate              : Response 200 OK
2025-01-09T23:59:01.421+08:00 DEBUG 4250 --- [client-application] [nio-8080-exec-2] [29e0fac8ee9435452ddc0e91ac67b652-9c95bddc85f879bb] o.s.web.client.RestTemplate              : Reading to [org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse] as "application/json;charset=UTF-8"

Unfortunately, the first call to get the token is missing. We can only see the call to get the protected resource.

This is a drawback, the service providing the token is sometimes flaky. Overall, it is a fair requirement to have observability on the call to get the token as well.

Expected Behavior

We are hoping to see something like this:

Screenshot 2025-01-10 at 00 06 19

This is achievable by not using spring-boot-starter-oauth2-client and writing our own code:

 @GetMapping("/lessons")
    public String fetchLessons() {
        Map ssa = restClient.post()
                .uri("https://service.com/token?scope=resolve+download&grant_type=client_credentials")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .headers(httpHeaders -> httpHeaders.setBasicAuth("aaa", "bbb"))
                .retrieve()
                .body(Map.class);

        final MultiValueMap<String, String> params2 = new LinkedMultiValueMap<>();
        params2.add("Authorization", "Bearer " + ssa.get("access_token"));
        Consumer<HttpHeaders> consumer2 = it -> it.addAll(params2);

        String result = restClient.get()
                .uri("https://protectedresource...")
                .headers(consumer2)
                .retrieve()
                .body(String.class);
        return result;
    }

But we like spring-boot-starter-oauth2-client and would like to avoid this route.

Would it be possible to enhance spring-boot-starter-oauth2-client, allowing it to pass in the custom RestClient? (not RestTemplate, not WebClient)

Rationale:

Many users already have a custom RestClient in their class path. It is properly configured with observability, SSL certificate, and so on.

Instead of defaulting the call to get the token with RestTemplate (which does not show any observability), it would be great if the user could pass the existing restClient.

Thank you for your time

Metadata

Metadata

Assignees

Labels

for: stackoverflowA question that's better suited to stackoverflow.comin: oauth2An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions