2

I am trying to implement an Oauth2 Authorization Server with SpringBoot using this guide as a reference.

My keystore has a single key. I have successfully managed to create a JWToken (I can check it at jwt.io).

I have also a test Resource Server. When I try to access any endpoint I receive the following message:

{
  "error": "invalid_token",
  "error_description": "Invalid JWT/JWS: kid is a required JOSE Header"
}

The token really does not have a kid header but I can not figure out how to add it. I can only add data to its payload, using a TokenEnchancer. It also seems that I am not the first one with this issue.

Is there any way to add this header or, at least, ignore it at the resource server?

marcellorvalle
  • 1,631
  • 3
  • 17
  • 30
  • 1
    There is an open issue on the official spring-security-oauth Github repository: https://github.com/spring-projects/spring-security-oauth/issues/1649 – Ortomala Lokni Apr 13 '19 at 16:02

2 Answers2

3

I've been working on an article that might help you out here: https://www.baeldung.com/spring-security-oauth2-jws-jwk

So, to configure a Spring Security OAuth Authorization Server to add a JWT kid header, you can follow the steps of section 4.9:

  1. create a new class extending the JwtAccessTokenConverter
  2. In the constructor:
    1. configure the parent class using the same approach you've been using
    2. obtain a Signer object using the signing key you're using
  3. override the encode method. The implementation will be the same as the parent one, with the only difference that you’ll also pass the custom headers when creating the String token
public class JwtCustomHeadersAccessTokenConverter extends JwtAccessTokenConverter {

    private JsonParser objectMapper = JsonParserFactory.create();
    final RsaSigner signer;

    public JwtCustomHeadersAccessTokenConverter(KeyPair keyPair) {
        super();
        super.setKeyPair(keyPair);
        this.signer = new RsaSigner((RSAPrivateKey) keyPair.getPrivate());
    }

    @Override
    protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        String content;
        try {
            content = this.objectMapper.formatMap(getAccessTokenConverter().convertAccessToken(accessToken, authentication));
        } catch (Exception ex) {
            throw new IllegalStateException("Cannot convert access token to JSON", ex);
        }
        Map<String, String> customHeaders = Collections.singletonMap("kid", "my_kid");
        String token = JwtHelper.encode(content, this.signer, this.customHeaders)
            .getEncoded();
        return token;
    }
}
  1. Then, of course, create a bean using this converter:
@Bean
public JwtAccessTokenConverter accessTokenConverter(KeyPair keyPair) {
    return new JwtCustomHeadersAccessTokenConverter(keyPair);
}

Here I used a KeyPair instance to obtain the signing key and configure the converter (based on the example of the article), but you might adapt that to your configuration.

In the article I also explain the relevant endpoints provided by the Spring Security OAuth Authentication Server.

Also, regarding @Ortomala Lokni's comment, I wouldn't expect Spring Security OAuth to add any new features at this point. As an alternative, you probably can wait to have a look at Spring Security's Authorization Server features, planned to be released in 5.3.0

Gerardo Roza
  • 2,746
  • 2
  • 24
  • 33
  • My need is different : I'm programming the Spring Boot CLIENT that will present a signed JWT to the authorization server to get the access token it will use later to access the resource server [RFC-7523 art 2.1](https://datatracker.ietf.org/doc/html/rfc7523#section-2.1). In such a scenario how to configure the WebClient filter to automatically call the authorization server and reuse the token if it is not expired. No tutorial seems to exist for such a client. – Pierre C Jul 25 '23 at 01:05
2

I managed to solve it by changing the parameter used to identify the URL where the clients will retrieve the pubkey.

On application.properties, instead of:

security.oauth2.resource.jwk.key-set-uri=http://{auth_server}/.well-known/jwks.json

I used:

security.oauth2.resource.jwt.key-uri=http://{auth_server}/oauth/token_key

If I understood correctly, the key-set-uri config points to an endpoint that presents a set of keys and there is the need for a kid. On the other side key-uri config points to an endpoint with a single key.

Jeff Tian
  • 5,210
  • 3
  • 51
  • 71
marcellorvalle
  • 1,631
  • 3
  • 17
  • 30