TokenRelay bug when using different oauth2 client registration
spring-cloud-starter-gateway v4.1.5
When using a different client registration for the TokenRelay filter (like TokenRelay=someClientRegistrationId and not the one used for logging in the user), the Bearer auth header is not set. I think this is because the client used for the TokenRelay does not get an authorizedClient.
Example application security config:
security:
oauth2:
client:
provider:
myAuthProvider:
issuer-uri: ${issuerUri}
user-name-attribute: name
registration:
loginClient:
provider: myAuthProvider
authorization-grant-type: authorization_code
client-id: ${clientId}
client-secret: ${clientSecret}
scope: openid,profile,email,offline_access
resourceClient:
provider: myAuthProvider
authorization-grant-type: client_credentials
client-id: ${clientId}
client-secret: ${clientSecret}
scope: /.default
cloud:
gateway:
routes:
- id: resourceServerRoute
uri: ${resouceServerUri}
predicates:
- Path=/resource/**
filters:
- TokenRelay=resourceClient
Suggested solution (inspired by https://docs.spring.io/spring-security/reference/reactive/oauth2/client/authorization-grants.html#_using_the_access_token):
In function TokenRelayGatewayFilterFactory.authorizationRequest add .attribute(ServerWebExchange.class.getName(), exchange) to the builder like so:
private Mono<OAuth2AuthorizeRequest> authorizationRequest(String defaultClientRegistrationId,
Authentication principal,
ServerWebExchange exchange) {
String clientRegistrationId = defaultClientRegistrationId;
if (clientRegistrationId == null && principal instanceof OAuth2AuthenticationToken) {
clientRegistrationId = ((OAuth2AuthenticationToken) principal).getAuthorizedClientRegistrationId();
}
return Mono.justOrEmpty(clientRegistrationId).map(OAuth2AuthorizeRequest::withClientRegistrationId)
.map(builder -> builder.principal(principal).attribute(ServerWebExchange.class.getName(), exchange).build());
}
So I was having multiple moving parts when I was trying to solve this issue, and now looking into it a bit further after a good night sleep the suggested solution in my first post is actually not what solved the issue. The other thing I changed was adding explicit security config and adding the clientCredentials as provider, see code below:
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.clientCredentials()
.build();
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
}