I am running on a version 2.0.6 oauth2 with jwt , with separate auth and resource server , I am getting InvalidTokenException with an error message Cannot convert access token to JSON. If I include the private keystore on the resource server and set the key pair with private key alias and password , everything works . I really dont want to include private keystore on the resource server .
protected Map<String, Object> decode(String token) {
try {
Jwt jwt = JwtHelper.decodeAndVerify(token, verifier);
String content = jwt.getClaims();
@SuppressWarnings("unchecked")
Map<String, Object> map = objectMapper.readValue(content, Map.class);
if (map.containsKey(EXP) && map.get(EXP) instanceof Integer) {
Integer intValue = (Integer) map.get(EXP);
map.put(EXP, new Long(intValue));
}
return map;
}
catch (Exception e) {
throw new InvalidTokenException("Cannot convert access token to JSON", e);
}
}
My jwt client config
-- loading the key factory from private keystore works and setting the key pair , then all good
@Bean
@Qualifier("jwtAccessTokenConverter")
protected JwtAccessTokenConverter jwtAccessTokenConverter()
{
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("test.jks"), "assurant".toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("test","testpassword".toCharArray()));
Resource resource = new ClassPathResource("test.pem");
String publicKey = null;
try {
publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
} catch (IOException e) {
throw new RuntimeException(e);
}
converter.setVerifierKey(publicKey);
return converter;
}
Any ideas greatly appreciated .
You don't need a signing key for a resource server. What's the exception?
Are you sure your JwtAccessTokenConverter is a Spring bean? I can see the @Bean annotation, but that only works if it is in a @Configuration (for instance).
That makes sense , I had my code without @Configuration like I mentioned below, didnt work, but when I added @Autowired JwtAccessTokenConverter jwtAccessTokenConverter; to the class , it worked .
@Bean
@Qualifier("tokenStore")
public TokenStore tokenStore() throws Exception{
System.out.println("Created JwtTokenStore");
return new JwtTokenStore(jwtTokenEnhancer());--didn't work
return new JwtTokenStore(jwtAccessTokenConverter);(worked with autowired)
}
@Bean
@Qualifier("jwtAccessTokenConverter")
protected JwtAccessTokenConverter jwtTokenEnhancer() throws Exception{
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource("mypublic.pem");
String publicKey = null;
try {
publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
} catch (IOException e) {
throw new RuntimeException(e);
}
converter.setVerifierKey(publicKey);
return converter;
}
I get the same issue every now and then, It's on-off behavior
{ "error": "invalid_token", "error_description": "Cannot convert access token to JSON" }
It works in the morning when I do fresh checkout of the project, again it fails after doing gradle clean build of the project. Sometimes, it works when I checkout a new branch with the same code copy.? What is the exact root cause for this? When failed with above error, I decode the token, and it's a valid JWT Token. How to troubleshoot this?. I thought it's something to do with gradle cache. After refreshing, sometimes it works and it doesn't. If there was a jar file version issue, how did it work the first time. This happens when I enable @ResourceServer annotation and want to make my resource protected
Found the same issue! It is really actual thatjwtAccessTokenConverter.setVerifierKey(); doesn't really set verifier that is used in -
...protected Map<String, Object> decode(String token) {
try {
Jwt jwt = JwtHelper.decodeAndVerify(token, verifier);
as workaround helped:
private JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new CustomTokenEnhancer();
jwtAccessTokenConverter.setSigningKey(jwtSigningKey);
jwtAccessTokenConverter.setVerifier(new RsaVerifier(jwtPublicKey));
log.info("Set JWT signing key to: {}", jwtAccessTokenConverter.getKey());
return jwtAccessTokenConverter;
}
Thanks a ton Alex for response and the help. Helped to fix the issue
Alex, I'm struggling with the same issue here. Could you pad out your example code some more so I can see how you fixed the problem? My issue seems to be that my auth / resource server work perfectly well until I apply SSL to the configuration.
Then the key verifier gets lost and is no longer an instance of RsaKeyVerifier, which subsequently throws the annoying 'cannot decode JSON' exception. I've been stuck on this for days now!
Thanks for any feedback!
Had the same issue, the solution seems to be marking the token converter as @Bean. I guess somewhere in the framework a token converter is injected from the context, and created new if none is present.
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
...
}
@sniederb @alexandr-efimov There is an interface on the JwtAccessTokenConverter object called InitializingBean.
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
This is likely the magic code being called "somewhere in the framework". If you don't want this to be a @Bean call the method manually after setup, for example:
private JwtAccessTokenConverter accessTokenConverter() {
val converter = new JwtAccessTokenConverter()
converter.setSigningKey("123")
converter.afterPropertiesSet()
return converter
}
Note: If you trace the code inside the afterPropertiesSet method, you will see it sets the verifier property.
Most helpful comment
@sniederb @alexandr-efimov There is an interface on the JwtAccessTokenConverter object called
InitializingBean.This is likely the magic code being called "somewhere in the framework". If you don't want this to be a
@Beancall the method manually after setup, for example:Note: If you trace the code inside the afterPropertiesSet method, you will see it sets the
verifierproperty.