Skip to content

Provide customization hooks for reactive oauth2 token client requests #9171

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

Closed
jjstreet opened this issue Oct 30, 2020 · 5 comments
Closed
Assignees
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) status: duplicate A duplicate of another issue

Comments

@jjstreet
Copy link

jjstreet commented Oct 30, 2020

Enhancement

  • Allow extension of AbstractWebClientReactiveOAuth2AccessTokenResponseClient by class outside the package.
  • Allow extension of BodyInserters.FormInserter<String> populateTokenRequestBody(T grantRequest, BodyInserters.FormInserter<String> body) by classes outside the package.

OR

Provide some other way to customize the request made by token response clients.

Context

In servlet, we have the ability to provide a Converter such as Converter<OAuth2ClientCredentialsGrantRequest, RequestEntity<?>> to customize the creation of the request ultimately sent to an authorization server for a token.

Such customization is not yet available in the Reactive arena. Token response clients are all abstracted from a package-protected abstract class. There is no method available to extend in a way that allows me to do the same as above. All potential methods are located in the abstract class and are themselves package-private.

This customization is especially useful when dealing with different OAuth2 authorization servers that want additional data. One such provider is Active Directory where you may provide a resource field in the request.

Looking at the token response clients, I see there is no request object to customize, so the next best option would be customizing the token client. This looks to be possible if classes were made extensible. This allow us to customize in a pretty straightforward way:

public class WebClientReactiveAdfsClientCredentialsTokenResponseClient
        extends WebClientReactiveClientCredentialsTokenResponseClient {

    String resource;

    @Override
    protected BodyInserters.FormInserter<String> populateTokenRequestBody(
            OAuth2ClientCredentialsGrantRequest grantRequest, BodyInserters.FormInserter<String> body) {
        // Populate request body as normal
        super.populateTokenRequestBody(grantRequest, body)

        // populate my "resource" value for Active Directory
        body.with("resource", resource);
        return body;
    }
}
@jjstreet jjstreet added status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement labels Oct 30, 2020
@jzheaux jzheaux removed the status: waiting-for-triage An issue we've not yet triaged label Oct 30, 2020
@jjstreet
Copy link
Author

jjstreet commented Nov 2, 2020

Barring any concerns over the proposed solution, I can put a PR together with the above changes.

@jgrandja
Copy link
Contributor

jgrandja commented Nov 3, 2020

@jjstreet WebClient allows you to modify the ClientRequest and/or ClientResponse via an ExchangeFilterFunction. See ExchangeFilterFunction.ofRequestProcessor() for processing the outgoing ClientRequest and ExchangeFilterFunction.ofResponseProcessor for processing the incoming ClientResponse. You can then configure WebClient with the custom ExchangeFilterFunction and supply it to the ReactiveOAuth2AccessTokenResponseClient implementation. Please see this comment for additional details.

Closing this as duplicate of #8612

@jgrandja jgrandja closed this as completed Nov 3, 2020
@jgrandja jgrandja added in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) status: duplicate A duplicate of another issue and removed type: enhancement A general enhancement labels Nov 3, 2020
@jjstreet
Copy link
Author

jjstreet commented Nov 3, 2020

I read through #8612 and this is totally a duplicate of that. I didn't search thoroughly enough it seems.

I am in the same situation as #8612. I need to use client registration id to look up a resource value and apply to the request. Much like Auth0 requires additional fields, so does active directory. In my example I show the token client not being reusable for different registrations, but it would be by passing along a repository of resource registrations (like I plan to). I left it out of the example for the sake of brevity. I should have left it in.

Just so I understand your recommended approach:

  • Use a delegate token client after setting up a context:
@Override
public Mono<OAuth2AccessTokenResponse> getTokenResponse(OAuth2ClientCredentialsGrantRequest authorizationGrantRequest) {
    return Mono.subscriberContext()
            .map(ctx -> {
                ctx.put(AdfsResourceReactorContext.RESOURCE, "resource");
                return ctx;
            })
            .flatMap(ctx -> delegate.getTokenResponse(authorizationGrantRequest));
}

Not sure if this correct, but I can read up on reactor context to understand it a bit more.

  • Create an ExchangeFilterFunction to use the context values in a way to alter the request body.

This is where I am now struggling to understand your recommended approach. ExchangeFilterFunction.ofRequestProcesssor() takes a Function and gives me a ClientHttpRequest but what do I do with it? Can I use ClientHttpRequest.body() to alter the request to my needs? Would I have to cast the inserter somehow? Just really confused on this stage of the approach.

  • Add the filter to a new webclient and set delegate token client to use this webclient

Pretty straight forward here. I can do that.

@jgrandja
Copy link
Contributor

@jjstreet

Can I use ClientHttpRequest.body() to alter the request to my needs

Yes.

Assuming you want to modify the default token request for a client_credentials client, you would configure as follows:

private ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient() {
	WebClient webClient = WebClient.builder().filter(clientCredentialsTokenRequestProcessor()).build();
	WebClientReactiveClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient =
			new WebClientReactiveClientCredentialsTokenResponseClient();
	clientCredentialsTokenResponseClient.setWebClient(webClient);
	return clientCredentialsTokenResponseClient;
}

private static ExchangeFilterFunction clientCredentialsTokenRequestProcessor() {
	return ExchangeFilterFunction.ofRequestProcessor(request ->
			Mono.just(ClientRequest.from(request)
					.body(((BodyInserters.FormInserter<String>) request.body())
							.with("resource", "resource1"))
					.build()
			)
	);
}

@jjstreet
Copy link
Author

The casting is kind of a bummer, but I'm definitely gonna give this a shot and see how it works.

Thanks for the followup advice. Really appreciate it.

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) status: duplicate A duplicate of another issue
Projects
None yet
Development

No branches or pull requests

3 participants