Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid JWS header 'kid' (Key ID) value: 7. Unsupported value type. Expected: java.lang.String, found: java.lang.Integer #958

Open
charles6078 opened this issue Jul 24, 2024 · 5 comments

Comments

@charles6078
Copy link

Caused by: java.lang.IllegalArgumentException: Invalid JWS header 'kid' (Key ID) value: 7. Unsupported value type. Expected: java.lang.String, found: java.lang.Integer
at io.jsonwebtoken.impl.ParameterMap.apply(ParameterMap.java:193)
at io.jsonwebtoken.impl.ParameterMap.put(ParameterMap.java:139)
at io.jsonwebtoken.impl.ParameterMap.put(ParameterMap.java:149)
at io.jsonwebtoken.impl.ParameterMap.putAll(ParameterMap.java:213)
at io.jsonwebtoken.impl.ParameterMap.(ParameterMap.java:71)
at io.jsonwebtoken.impl.ParameterMap.(ParameterMap.java:61)
at io.jsonwebtoken.impl.DefaultHeader.(DefaultHeader.java:49)
at io.jsonwebtoken.impl.DefaultProtectedHeader.(DefaultProtectedHeader.java:78)
at io.jsonwebtoken.impl.DefaultJwsHeader.(DefaultJwsHeader.java:36)
at io.jsonwebtoken.impl.DefaultTokenizedJwt.createHeader(DefaultTokenizedJwt.java:54)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:385)
... 9 common frames omitted
Caused by: java.lang.IllegalArgumentException: Unsupported value type. Expected: java.lang.String, found: java.lang.Integer
at io.jsonwebtoken.impl.lang.RequiredTypeConverter.applyFrom(RequiredTypeConverter.java:44)
at io.jsonwebtoken.impl.lang.DefaultParameter.applyFrom(DefaultParameter.java:124)
at io.jsonwebtoken.impl.ParameterMap.apply(ParameterMap.java:176)
... 19 common frames omitted

@charles6078
Copy link
Author

io.jsonwebtoken.MalformedJwtException: Invalid protected header: Invalid JWS header 'kid' (Key ID) value: 7. Unsupported value type. Expected: java.lang.String, found: java.lang.Integer
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:388)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:364)
at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:94)
at io.jsonwebtoken.impl.io.AbstractParser.parse(AbstractParser.java:36)
at io.jsonwebtoken.impl.io.AbstractParser.parse(AbstractParser.java:29)
at io.jsonwebtoken.impl.DefaultJwtParser.parseSignedClaims(DefaultJwtParser.java:830)

@bdemers
Copy link
Member

bdemers commented Jul 24, 2024

Per RFC-7515 section 4.1.4, the kid header value is a String

Its value MUST be a case-sensitive string.

Are you in control of the end creating the token? Or is this from a popular 3rd party service?

@charles6078
Copy link
Author

the token is from a third party service

@bdemers
Copy link
Member

bdemers commented Jul 25, 2024

@charles6078 any chance you can tell us which one?

@lhazlewood
Copy link
Contributor

As @bdemers said, the RFC specification indicates this MUST be a String, not an Integer. Whoever is issued the token is using a value that violates the RFC requirements.

Because most of our efforts are on RFC compliance, there is little incentive for us to support value types other than what the RFC mandates, so I'm not entirely sure it is something we should change in the codebase.

However, in this case since the issuer is violating the RFC, I think the best solution is to introduce your own/custom JSON Deserializer as described here to 'fix' the input:

https://github.com/jwtk/jjwt?tab=readme-ov-file#json-support

You can just wrap an existing Deserializer implementation, and then, before returning the deserialized Map<String,?> instance, replace the kid value in the map with a String value, effectively normalizing / fixing the erroneous input to ensure it is correct per the RFC.

Here's a quick example implementation I threw together:

package whatever;

import io.jsonwebtoken.io.DeserializationException;
import io.jsonwebtoken.io.Deserializer;

import java.io.Reader;
import java.util.Map;

public class FixedKidDeserializer implements Deserializer<Map<String, ?>> {

    private static final String KID_ID = "kid";

    Deserializer<Map<String, ?>> delegate;

    public FixedKidDeserializer(Deserializer<Map<String, ?>> delegate) {
        this.delegate = delegate;
    }

    private Map<String, ?> normalize(Map<String, ?> deserialized) {
        @SuppressWarnings("unchecked") Map<String, Object> map = (Map<String, Object>) deserialized;
        Object value = map.get(KID_ID);
        if (value != null && !(value instanceof String)) {
            value = "" + value;
            map.put(KID_ID, value);
        }
        return map;
    }

    @Override
    public Map<String, ?> deserialize(byte[] bytes) throws DeserializationException {
        Map<String, ?> deserialized = delegate.deserialize(bytes);
        return normalize(deserialized);
    }

    @Override
    public Map<String, ?> deserialize(Reader reader) throws DeserializationException {
        Map<String, ?> deserialized = delegate.deserialize(reader);
        return normalize(deserialized);
    }
}

Then you'd configure an instance of this on your JwtParserBuilder, e.g.:

Deserializer<Map<String,?>> normalizing = new FixedKidDeserializer(new JacksonDeserializer());
Jwts.parser().json(normalizing) // ... etc ...

(new JacksonDeserializer() is just an example if you use the jjwt-jackson .jar with compile scope instead of runtime scope as would normally be the case. Any other Deserializer implementation would be fine as well - gson, org.json, etc).

Then when JJWT processes the deserialized Map<String,?>, it will see valid input, and everything will work as expected.

Another approach, if you didn't want to implement JJWT's Deserializer API, you could instead customize the Jackson ObjectMapper to perform this translation for you using Jackson-specific APIs and customizations. Then. you can pass this custom ObjectMapper to JJWT as documented here:

https://github.com/jwtk/jjwt?tab=readme-ov-file#jackson-json-processor

There could be similar approaches for Gson or org.json to customize the result before handed off to JJWT.

I hope that helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants