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

Support custom form parameters for client_credentials Access Token Request #6569

Closed
fritzdj opened this issue Feb 28, 2019 · 11 comments
Closed
Assignees
Labels
status: invalid An issue that we don't feel is valid

Comments

@fritzdj
Copy link
Contributor

fritzdj commented Feb 28, 2019

Summary

There is no way currently to pass custom form parameters as part of OAuth2ClientCredentialsGrantRequestEntityConverter:
https://github.com/spring-projects/spring-security/blob/master/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/OAuth2ClientCredentialsGrantRequestEntityConverter.java

Some IDPs (Auth0 for example) require an "audience" value.

@jgrandja
Copy link
Contributor

jgrandja commented Mar 1, 2019

@fritzdj This is actually possible. You would need to supply DefaultClientCredentialsTokenResponseClient.setRequestEntityConverter() with an implementation of Converter<OAuth2ClientCredentialsGrantRequest, RequestEntity<?>>, similar to how OAuth2ClientCredentialsGrantRequestEntityConverter is implemented.

I'm going to close this issue as answered.

@jgrandja jgrandja closed this as completed Mar 1, 2019
@jgrandja jgrandja self-assigned this Mar 1, 2019
@jgrandja jgrandja added the status: invalid An issue that we don't feel is valid label Mar 1, 2019
@fritzdj
Copy link
Contributor Author

fritzdj commented Mar 1, 2019

@jgrandja, thanks for the quick response. How is it possible to do that though? We are not seeing a way to update that / the class that uses it is not autowired.

@jgrandja
Copy link
Contributor

jgrandja commented Mar 1, 2019

Here is a sample config

@Configuration
public class WebClientConfig {

	@Bean
	WebClient webClient(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) {
		// TODO Obtain your custom Converter
		Converter<OAuth2ClientCredentialsGrantRequest, RequestEntity<?>> customRequestEntityConverter = null;
		
		DefaultClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient = 
				new DefaultClientCredentialsTokenResponseClient();
		clientCredentialsTokenResponseClient.setRequestEntityConverter(customRequestEntityConverter);
		
		ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2 = 
				new ServletOAuth2AuthorizedClientExchangeFilterFunction(
						clientRegistrationRepository, authorizedClientRepository);
		oauth2.setClientCredentialsTokenResponseClient(clientCredentialsTokenResponseClient);

		return WebClient.builder()
				.apply(oauth2.oauth2Configuration())
				.build();
	}
}
@fritzdj
Copy link
Contributor Author

fritzdj commented Mar 1, 2019

Thanks again @jgrandja, this is great. What about if we are not using WebFlux?

@fritzdj
Copy link
Contributor Author

fritzdj commented Mar 1, 2019

To be more specific, we want to use the @RegisteredOAuth2AuthorizedClient annotation.

@jgrandja
Copy link
Contributor

jgrandja commented Mar 1, 2019

@fritzdj In that case, you can use this @Configuration

@Configuration
public class WebConfig implements WebMvcConfigurer {
	private final ClientRegistrationRepository clientRegistrationRepository;
	private final OAuth2AuthorizedClientRepository authorizedClientRepository;

	public WebConfig(ClientRegistrationRepository clientRegistrationRepository,
						OAuth2AuthorizedClientRepository authorizedClientRepository) {
		this.clientRegistrationRepository = clientRegistrationRepository;
		this.authorizedClientRepository = authorizedClientRepository;
	}

	@Override
	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
		DefaultClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient =
				new DefaultClientCredentialsTokenResponseClient();
		clientCredentialsTokenResponseClient.setRequestEntityConverter(
				new CustomClientCredentialsGrantRequestEntityConverter());

		OAuth2AuthorizedClientArgumentResolver authorizedClientArgumentResolver =
				new OAuth2AuthorizedClientArgumentResolver(this.clientRegistrationRepository, this.authorizedClientRepository);
		authorizedClientArgumentResolver.setClientCredentialsTokenResponseClient(clientCredentialsTokenResponseClient);
		argumentResolvers.add(authorizedClientArgumentResolver);
	}

	private static class CustomClientCredentialsGrantRequestEntityConverter implements Converter<OAuth2ClientCredentialsGrantRequest, RequestEntity<?>> {
		private final Converter<OAuth2ClientCredentialsGrantRequest, RequestEntity<?>> defaultRequestEntityConverter =
				new OAuth2ClientCredentialsGrantRequestEntityConverter();

		@Override
		public RequestEntity<?> convert(OAuth2ClientCredentialsGrantRequest source) {
			return this.defaultRequestEntityConverter.convert(source);
		}
	}
}
@jgrandja
Copy link
Contributor

jgrandja commented Mar 1, 2019

@fritzdj I just added issue #6572 to allow for an easier configuration from the one I provided. If you're interested in submitting a PR for this I'd be happy to guide you through the process?

@fritzdj
Copy link
Contributor Author

fritzdj commented Mar 1, 2019

@jgrandja, I would love to give that a shot. I see the contributor guidelines for the project so I will try this out.

@wangyue82lf
Copy link

wangyue82lf commented Aug 2, 2023

Hi @jgrandja ,
I have same the issue, but we need to use HttpSecurity.oauth2Login() custom header for client_credential.
But I found TokenEndpointConfig.accessTokenResponseClient() must use OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest>.

I want some like the one below. Do you have any suggest?

`

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/getToken")
        .permitAll()
        .anyRequest()
        .authenticated()
        .and()
        .oauth2Login(oauth2 ->
                             oauth2.authorizationEndpoint(authorization -> authorization.authorizationRequestResolver(
                                           new CustomAuthorizationRequestResolver(this.clientRegistrationRepository(), "/getToken")))
                                   .redirectionEndpoint(redirection -> redirection.baseUri("/getToken"))
                                   .tokenEndpoint(token -> token.accessTokenResponseClient(this.accessTokenResponseClient()))
                                   .userInfoEndpoint(userInfo -> System.out.println(userInfo)));
    return http.build();
}

@Bean
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient() {
    DefaultClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient = new DefaultClientCredentialsTokenResponseClient();
    clientCredentialsTokenResponseClient.setRequestEntityConverter(new CustomRequestEntityConverter());
    OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
    tokenResponseHttpMessageConverter.setAccessTokenResponseConverter(new CustomTokenResponseConverter());
    RestTemplate restTemplate = new RestTemplate(Arrays.asList(new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
    restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
    clientCredentialsTokenResponseClient.setRestOperations(restTemplate);

    OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient = clientCredentialsTokenResponseClient;
    return accessTokenResponseClient;
}`
@jgrandja
Copy link
Contributor

jgrandja commented Aug 2, 2023

@wangyue82lf See the following references on how to customize DefaultClientCredentialsTokenResponseClient:

@wangyue82lf
Copy link

wangyue82lf commented Aug 6, 2023

@wangyue82lf See the following references on how to customize DefaultClientCredentialsTokenResponseClient:

@jgrandja
Thank you for your help.
I'm using JDBC implementation for saving token. But after getting token response.
The database did not save refresh token, although I returned refresh token.
I need to use refresh as below. but it's not working.
I also see the document.
How do I make refresh work?

`

@Bean
public ClientRegistration clientRegistration() {
    return ClientRegistration
            .withRegistrationId("Custom")
            .tokenUri("XXXX")
            .clientId("Custom")
            .clientSecret("XXXX")
            .registrationId("Custom")
            .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
            .build();
}

@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
    // keep save token in memory
    return new InMemoryClientRegistrationRepository(this.clientRegistration());
}

@Bean
public OAuth2AuthorizedClientService oAuth2AuthorizedClientService(JdbcOperations jdbcOperations) {
    // keep save token in DB
    return new JdbcOAuth2AuthorizedClientService(jdbcOperations, this.clientRegistrationRepository());
}

@Bean
protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests(authorizeRequests -> authorizeRequests.antMatchers(HttpMethod.POST, "/getToken").authenticated())
        .headers()
        .httpStrictTransportSecurity()
        .disable()
        .and()
        .oauth2Login();
    return http.build();
}


@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrationRepository,
                                                             OAuth2AuthorizedClientRepository authorizedClientRepository,
                                                             OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
                                                             CustomOAuth2ClientCredentialsTokenResponseClient credentialsClient,
                                                             CustomOAuth2ClientRefreshTokenResponseClient refreshClient) {

    OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder
            .builder()
            .clientCredentials(clientCredentials -> clientCredentials.accessTokenResponseClient(credentialsClient))
            .refreshToken(refreshToken -> refreshToken.accessTokenResponseClient(refreshClient))
            .build();

    AuthorizedClientServiceOAuth2AuthorizedClientManager manager = new AuthorizedClientServiceOAuth2AuthorizedClientManager(
            clientRegistrationRepository,
            oAuth2AuthorizedClientService);
    manager.setAuthorizedClientProvider(authorizedClientProvider);
    return manager;
}



@Bean
public CustomOAuth2ClientCredentialsTokenResponseClient credentialsClient(client client) {
    return new CustomOAuth2ClientCredentialsTokenResponseClient(client);
}

@Bean
public CustomOAuth2ClientRefreshTokenResponseClient refreshClient(client client) {
    return new CustomOAuth2ClientRefreshTokenResponseClient(client);
}

`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: invalid An issue that we don't feel is valid
3 participants