I'm working on an application written in Spring Boot (3.2.3) with Spring-Security (6.2.2) which connects to an external service over REST (implemented with Open Feign Client) and authenticated with a Bearer JWT Token.
The JWT token used for the external service has to be obtained using OAuth2.
In the documentation of the external service it is explained how to obtain the jwt token using Postman, as follows:
- grant type: authorization code
- callback url: ...
- client id: ...
- client secret: ...
- auth url: https://provider-name/authorize
- access token url: https://provider-name/token
- from advanced, on Auth Request section, set "token_content_type": "jwt" (this translates to a query parameter for auth link)
- from advanced, on Token Request section, set "token_content_type": "jwt", Send in: "Request body".
Using Postman I managed to retrieve the JWT token successfully. However, I am not sure how to fully achieve this with Spring.
My setup is as follows: application.yaml
spring:
security:
oauth2:
client:
registration:
provider-name:
client-id: ...
client-secret: ...
authorization-grant-type: authorization_code
redirect-uri: "https://localhost:8080/a/token"
provider:
provider-name:
authorization-uri: "https://provider-name/authorize"
token-uri: "https://provider-name/token"
My Security config looks something like this:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> {
auth.requestMatchers("/a/token").permitAll();
})
.oauth2Login(oauth2 ->
oauth2.authorizationEndpoint(endpoint ->
endpoint.authorizationRequestResolver(new CustomAuthorizationRequestResolver(clientRegistrationRepository))))
.formLogin(withDefaults())
.build();
}
where CustomAuthorizationRequestResolver is:
@Component
public class CustomAuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {
private final OAuth2AuthorizationRequestResolver defaultResolver;
public CustomAuthorizationRequestResolver(ClientRegistrationRepository clientRegistrationRepository) {
this.defaultResolver = new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, "/oauth2/authorize");
}
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
OAuth2AuthorizationRequest authorizationRequest = this.defaultResolver.resolve(request);
if (authorizationRequest != null) {
authorizationRequest = customizeAuthorizationRequest(authorizationRequest);
}
return authorizationRequest;
}
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
OAuth2AuthorizationRequest authorizationRequest = this.defaultResolver.resolve(request, clientRegistrationId);
if (authorizationRequest != null) {
authorizationRequest = customizeAuthorizationRequest(authorizationRequest);
}
return authorizationRequest;
}
private OAuth2AuthorizationRequest customizeAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest) {
if (!authorizationRequest.getAuthorizationUri().contains("provider-name")) {
return authorizationRequest;
}
return OAuth2AuthorizationRequest.from(authorizationRequest)
.additionalParameters(Map.of("token_content_type", "jwt"))
.build();
}
}
which works fine for setting the query parameter for the Auth Request.
When I call my application endpoint: https://localhost:8080/oauth2/authorize/provider-name the request is forwarded properly towards my provider and I get a response to /a/token with a code which, from my understandings, I have to send it back towards the /token endpoint.
However, I do not know how to send the same key-value pair (token_conent_type/jwt) on the request body of the Token Request. Isn't Spring Security supposed to do this? How to intercept and append my custom request body parameter?
Any suggestions?
Thank you!!
First of all it seems that Spring expects the redirect-uri to be defined as:
"{baseUrl}/login/oauth2/code/provider-name".Because I got that different the framework did not manage to intercept the auth code received and call the token endpoint.
Secondly, I did the following improvements:
where CustomRequestEntityConverter is:
and linked this to the security config, as such:
And that's about it. Happy coding!