Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

Setting BearerToken using OAuth2AuthorizedClientManager when accessing spring security OAuth2 5.3 programmatically

Ask Question

I have set up a spring web app that uses keycloak as an authorization server. The app is configured to use oauth2Login that requires the user to login using keycloak. The web app also is configured as an oauth2Resourceserver so that its URLs can be protected with roles that have been defined in keycloak and that are custom-converted from a JWT to JwtAuthenticationToken. Configuration looks like this:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests(authorizeRequests ->
                authorizeRequests.antMatchers("/test*").hasAnyRole("Dev", "QA")
                                 .anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(this.keycloakJwtAuthenticationConverter())))
            .oauth2Login(oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.userAuthoritiesMapper(this.userAuthoritiesMapper())))
            .oauth2Client();

The app also acts as a oauth2Client that uses open Feign to call other apps that are set up as resource servers. I use a feign request interceptor that tries to place a JWT token into the HTTP Bearer header as follows:

@Autowired
private OAuth2AuthorizedClientManager authorizedClientManager;
@Bean
public RequestInterceptor requestInterceptor() {
    return requestTemplate -> {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("keycloak")
                                                                        .principal(authentication)
                                                                        .build();
        OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);
        OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
        requestTemplate.header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken.getTokenValue());

When a user logs into the app using the keycloak front end then the above code works ok.

However when I do not log into the app manually, but - using keycloak-auth-client, log in programmatically & try to access the URLs using a rest template populated with the resulting JWT token set in the HTTP Bearer header, the authorizedClientManager.authorize(authorizeRequest) method in the request interceptor throws the following:

org.springframework.security.oauth2.client.ClientAuthorizationRequiredException: [client_authorization_required] Authorization required for Client Registration Id: keycloak

This results in the login html being returned to the RestTemplate instead of relevant data that was expected..

On reflection, this is probably not an appropriate way to handle an authentication that comes from a bearer token that was created in an entirely different app - I think that app should be responsible for reauthorizing its own access token.

If a user does login through keycloak then the authentication object that is retrieved from the SecurityContext here is a OAuth2AuthenticationToken. If the invocation is from an external app with a JWT token that it acquired and set as "Bearer" in the http header, then the authentication object is a JwtAuthenticationToken.

Hopefully, this is an appropriate way to differentiate between these 2 use cases. If the authentication is an OAuth2AuthenticationToken, then it can be successfully reauthorized using the above code. If it is a JwtAuthenticationToken, then I can just add the token to the Bearer http header and let the resource server validate it, without re-authorizing the token, since that should be done by the source app.

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.