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 RestClient interceptor #15437

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

sjohnr
Copy link
Member

@sjohnr sjohnr commented Jul 17, 2024

To Do:

  • Add unit tests
  • Add integration tests
  • Add reference documentation
  • Add sample(s)

Closes gh-13588

@sjohnr sjohnr added type: enhancement A general enhancement in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) labels Jul 17, 2024
@sjohnr sjohnr added this to the 6.4.0-M2 milestone Jul 17, 2024
@sjohnr sjohnr self-assigned this Jul 17, 2024
@sjohnr sjohnr marked this pull request as draft July 17, 2024 21:24
@azdanov
Copy link

azdanov commented Jul 18, 2024

Hey. If using an HTTP Interface, how would it be possible to dynamically change clientRegistrationId since there is no access to the restClient after the proxy is created?

In webclient I could use ServletOAuth2AuthorizedClientExchangeFilterFunction:

@Bean
public WebClient webClient(
        WebClient.Builder builder,
        HttpClient httpClient,
        OAuth2AuthorizedClientManager authorizedClientManager
) {
    ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 =
            new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);

    oauth2.setDefaultOAuth2AuthorizedClient(true);

    return builder
            .apply(oauth2.oauth2Configuration())
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .build();
}

@sjohnr
Copy link
Member Author

sjohnr commented Jul 18, 2024

Thanks for asking, @azdanov. See the per-request arrangement in this comment. This aspect of the interceptor is still in need of feedback so feel free to weigh in on it. The other option would be to use RestClient#attributes but I am not sure if using attributes to dynamically configure on a per-request basis is intuitive for users, so I'm not yet leaning towards using them.

However, I'm not clear how you would dynamically change the clientRegistrationId in your example with WebClient since I don't believe you can use attributes there. Using setDefaultOAuth2AuthorizedClient(true) would allow detecting the id from the currently logged in user (if they logged in using OAuth2 or OIDC), but you couldn't specify it dynamically. Can you please provide an example of how you would incorporate the WebClient from your example into an HTTP interface while dynamically specifying the clientRegistrationId?

throw ex;
}
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey bro, I've tried to use ur interceptor in my production code, then I got an exception: IllegalArgumentException: URI with undefined scheme.
After some debugging attempts, I realized that the baseUrl, which I set here:
return RestClient.builder () .requestFactory (requestFactory) .baseUrl (commandCenterBaseUrl) .requestInterceptor (requestInterceptor) .build ();
...didn't get passed to the interceptor... well Ok, I added an additional setter for the baseUrl and then...

Suggested change
@Override
public ClientHttpResponse intercept (
HttpRequest request, byte[] body, ClientHttpRequestExecution execution
) throws IOException {
if (!isNullOrEmpty (baseUrl)){
request = prependBaseUrl (request);
}
authorizeClient (request);
try {
ClientHttpResponse response = execution.execute (request, body);
handleAuthorizationFailure (response.getHeaders (), response.getStatusCode ());
return response;
}
catch (RestClientResponseException ex){
handleAuthorizationFailure (ex.getResponseHeaders (), ex.getStatusCode ());
throw ex;
}
catch (OAuth2AuthorizationException ex){
handleAuthorizationFailure (ex);
throw ex;
}
}
private HttpRequest prependBaseUrl (HttpRequest request){
URI completeUri = fromHttpUrl (baseUrl)
.path (request.getURI ().getPath ())
.query (request.getURI ().getQuery ())
.build (true)
.toUri ();
return new HttpRequestWrapper (request){
@Override
public @NonNull URI getURI (){
return completeUri;
}
};
}

I hope this suggestion helps!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion @Lonelysoul-HayashiNoMirai! However, I'm not understanding your issue. This class should not be responsible for setting the baseUrl. Can you help me understand your issue better by providing a sample? Note: I have a TODO to add samples, so this will help immensely.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion @Lonelysoul-HayashiNoMirai! However, I'm not understanding your issue. This class should not be responsible for setting the baseUrl. Can you help me understand your issue better by providing a sample? Note: I have a TODO to add samples, so this will help immensely.

I know that this class has nothing to do with "setting the baseUrl", then I should set it in here, right?
image
Even after setting it in the rest client, I'm still getting this exception:
image
When I was debugging, just before the exception was thrown, I clearly saw that the URL was missing the "baseUrl":
Debug-Screenshot
With my modifications in the suggestion, everything is Ok again... that's all, bro

@azdanov
Copy link

azdanov commented Jul 18, 2024

Using setDefaultOAuth2AuthorizedClient(true) would allow detecting the id from the currently logged in user

This is what I meant, thank you for clarifying things 🙂. That's what I considered dynamic, that the interceptor will do it, and not me. It would be cool to see a setDefaultOAuth2AuthorizedClient option RestClient interceptor too.

@sjohnr
Copy link
Member Author

sjohnr commented Jul 18, 2024

This is what I meant, thank you for clarifying things 🙂. That's what I considered dynamic, that the interceptor will do it, and not me. It would be cool to see a setDefaultOAuth2AuthorizedClient option RestClient interceptor too.

Thanks @azdanov, I suspected so but didn't want to assume. I have not yet determined whether it is truly necessary to introduce this functionality into the interceptor. I consider it a convenience feature, and it creates more ways to use the interceptor which can confuse users as much as help them. That's something I'd like to avoid. However, this is good feedback so I will think about it some more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

Add support for requesting protected resources with RestClient
3 participants