Describe the bug We use Spring Security for OAuth2 login using Keycloak. Until Spring Security 5.7.5 the JWT Token validation has worked perfectly. Since the update at 5.8.0, but also using the newer version, 6.0.0, it stopped working, producing an odd format.
Spring Security 5.7.5:
jwt.getClaimAsString("resource_access") produces {"notification-entry-service":{"roles":["disease-notification-sender"]}}
Spring Security 5.8.0+:
jwt.getClaimAsString("resource_access") produces {notification-entry-service={roles=[disease-notification-sender]}}
This happens by simply updating the dependency in the project, without touching/modifying the existing code.
To Reproduce Setup Spring Security 5.8. 0with OAuth2 and JWT.
Expected behavior The JWT parsing should not change behaviour (not even reported in the Changelog here - https://docs.spring.io/spring-security/reference/5.8/whats-new.html)
Sample
Could not be provided
Comment From: jzheaux
@madduci, thanks for the report. This feels like it could be a regression. I'll take a closer look shortly and prioritize any needed fixes for the next maintenance release.
Comment From: michaelweidmann
Hi @jzheaux,
we had a look on it and we found out that the issue is caused by a dependency upgrade from the com.nimbusds:nimbus-jose-jwt library (see this commit). The library switched the JSON parser from json-smart to GSON (see here).
The call to parse the token looks like this and from there on the library does its job (see here):
JWTClaimsSet jwtClaimsSet = this.jwtProcessor.process(parsedJwt, null);
The stacktrace looks like this:
With json-smart a complex claim value was parsed to a JSONObject and with GSON the result is a LinkedTreeMap.
Up to 5.7:
From 5.8 on:
This leads to the wrong conversion of the value from an object to the string in the class ObjectToStringConverter (see here):
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return (source != null) ? source.toString() : null;
}
- in Spring Security 5.7: input is a
JSONObjectwhich gets converted to a JSON object string - in Spring Security 5.8: input is a
LinkedTreeMapwhich gets converted to a key-value string
How to proceed with this?
Comment From: adase11
If it helps I noticed this as well in this issue https://github.com/spring-projects/spring-security/issues/12108#issuecomment-1343430667
Comment From: jzheaux
The JWT parsing should not change behaviour
Since JSONObject and LinkedTreeMap both implement the Map interface, I guess I'm not clear yet on what the problem is. Could you clarify? It sounds like your code could be relying on Nimbus implementation details. I'll leave it to the Nimbus team to say whether they support that.
Either way, Spring Security merely wraps Nimbus, so I'd recommend filing an issue with them if you'd like to see changes in its parsing behavior. The only parsing that we have dedicated code for are standard claims. Since resource_access is a non-standard claim, there is not much that Spring Security can do in the way of adapting changes in third-party code.
If you end up filing an issue with Nimbus, please consider posting the link here so that folks can follow that discussion.
In the meantime, you can use a custom claim converter to convert any given claim to the representation that you need:
@Bean
JwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {
OidcIdTokenDecoderFactory factory = new OidcIdTokenDecoderFactory();
Map<String, Converter<Object, ?>> converters = OidcIdTokenDecoderFactory
.createDefaultClaimTypeConverters();
converters.put("resource_access", (object) -> {
// ... reformulate as a JSONObject
});
ClaimTypeConverter claimTypeConverter = new ClaimTypeConverter(converters);
factory.setClaimTypeConverterFactory((registration) -> claimTypeConverter);
return factory;
}
For completeness, I'll also mention that getClaimAsMap is preferred given that the value is a Map. If the string representation is needed for serialization, note that Jwt#getTokenValue has the original serialized value.
not even reported in the Changelog here
I'll take this as feedback for the future, thank you. I believe the release notes do say that Nimbus was updated, though our release notes aren't linked in the What's New section.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: madduci
Hi
Thank you very much for the support and help debugging this. I will report the problem to Nimbus developers and post here updates if I'll receive any.
For sure, this was a weird one.
Comment From: jzheaux
Sounds great, @madduci. Given that, I'll close the issue for now. If it becomes clear that Spring Security should somehow change, we can reopen.