RMorrisey
RMorrisey

Reputation: 7739

Spring Security (OAuth): How can I add a request header when restTemplate is not exposed?

I am trying to set up a Resource server using the built-in OAuth support in Spring Security. I need to inject a new request header into every request that is sent from the Resource server to the Auth service. How can I do that?

Use case

The Auth service sits behind a corporate API Gateway forwarding service, like so:

Resource API > API Gateway > Auth API

Every request sent through the API Gateway requires an additional header to authenticate the client to the Gateway service. How can I inject this header? The Auth API implementation is otherwise a standard OIDC setup.

My setup

I have added spring security oauth2 to my Gradle build:

    implementation 'org.springframework.security:spring-security-config'
    implementation 'org.springframework.security:spring-security-oauth2-resource-server'
    implementation 'org.springframework.security:spring-security-oauth2-jose'

I have configured the issuer URI in Spring Security, as follows:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth/api/url

The problem

On app startup, I get the following exception:

java.lang.IllegalArgumentException: Unable to resolve the Configuration with the provided Issuer of "https://auth/api/url"
    at org.springframework.security.oauth2.jwt.JwtDecoderProviderConfigurationUtils.getConfiguration(JwtDecoderProviderConfigurationUtils.java:99) ~[spring-security-oauth2-jose-5.4.2.jar:5.4.2]

Debugging the code for JwtDecoderProviderConfigurationUtils, I see that it's failing because the API Gateway header is missing from the configuration request.

The problem is, I don't know how to modify the configuration utils to inject the API Gateway header. The request is built inline, using the URI only:

                RequestEntity<Void> request = RequestEntity.get(uri).build();
                ResponseEntity<Map<String, Object>> response = rest.exchange(request, STRING_OBJECT_MAP);
                Map<String, Object> configuration = response.getBody();

The RestTemplate is hidden from my app code, so I don't see a way to add a client interceptor:

    private static final RestTemplate rest = new RestTemplate();

In fact, the entire config utils class is package-level access, so I really can't do anything with it:

final class JwtDecoderProviderConfigurationUtils {

Everything else about the OIDC solution should be standard. How can I inject the required header without reinventing the entire Spring Security Oauth solution and all of the automagical configuration that it does?

Upvotes: 0

Views: 742

Answers (1)

Steve Riesenberg
Steve Riesenberg

Reputation: 6168

All of the configurations enabled by setting any of the following properties:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri:
          jwk-set-uri:
          jws-algorithm:
          public-key-location:

ultimately end up causing a JwtDecoder bean to be created and automatically configured by Spring Boot (see OAuth2ResourceServerJwtConfiguration.JwtDecoderConfiguration in the org.springframework.boot:spring-boot-autoconfigure jar).

The easiest way to take control of this process and avoid the error would be to provide your own bean:

@Bean
JwtDecoder jwtDecoder() {
    // ...
}

You could use the code in JwtDecoderProviderConfigurationUtils as a starting point and add your own header to your own RestTemplate, or you can simply provide an implementation directly and skip calling out to your auth server.

Upvotes: 0

Related Questions