Spring Boot 2.3.x.RELEASE applies additional non-standard validation on JWT tokens due to upgrade to NimbusDS 8.x.
With Spring Boot 2.2.2.RELEASE a JWT token with header field typ: "JWS" validates fine. In Spring Boot 2.3.0.RELEASE such a token is rejected due to a breaking change in NimbusDS 8.x, which requires typ to be set to either JWT or omitted.
As per RFC 7515 there's no dedicated typ mandated.
The issue is that the builder design in NimbusJwtDecoder (in my case NimbusJwtDecoder.withPublicKey(publicKey).build()) does not allow to go back to the old behavior, nor does it allow to change the JWSTypeVerifier/JWETypeVerifier set in NimbusDS's DefaultJWTProcessor. Therefore, it is currently necessary to duplicate the complete builder code, which is making me uneasy considering it's a security relevant part and a future change in Spring Security/Nimbus might render my code insecure.
Current Behavior
JWT tokens with typ: JWS are refused since Spring Boot 2.3.x
Expected Behavior
Either one of: * unusual, but not forbidden types are allowed * Builder allows to specify custom allowed types
Context
Dependency: org.springframework.security:spring-security-oauth2-jose:5.3.3.RELEASE
Class: org.springframework.security.oauth2.jwt.NimbusJwtDecoder
Unfortunately, I am not in control of the authorization server, which generates these custom JWTs. At the same time, I do not want to copy security relevant code.
Possible solutions:
1. restore previous behavior by setting JWSTypeVerifier/JWETypeVerifier to no-op verifiers in the builder(s)
2. allow to configure allowed types via config/builder
4. keep as is and require users to duplicate the builder code to apply either 1. or 2.
Stacktrace
{
"cause":null,
"stackTrace":[
{
"classLoaderName":"app",
"moduleName":null,
"moduleVersion":null,
"methodName":"authenticate",
"fileName":"JwtAuthenticationProvider.java",
"lineNumber":86,
"nativeMethod":false,
"className":"org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider"
},
{
"classLoaderName":"app",
"moduleName":null,
"moduleVersion":null,
"methodName":"authenticate",
"fileName":"ProviderManager.java",
"lineNumber":199,
"nativeMethod":false,
"className":"org.springframework.security.authentication.ProviderManager"
},
{
"classLoaderName":"app",
"moduleName":null,
"moduleVersion":null,
"methodName":"doFilterInternal",
"fileName":"BearerTokenAuthenticationFilter.java",
"lineNumber":124,
"nativeMethod":false,
"className":"org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter"
}
// ... [snip]
],
"title":"Unauthorized",
"status":"UNAUTHORIZED",
"detail":"An error occurred while attempting to decode the Jwt: JOSE header \"typ\" (type) \"JWS\" not allowed"
}
Comment From: mrguamos
This is my problem too. Since the IdentityServer4 uses “at+jwt” JWT typ. Token validation always fail. Good thing I have control in the IS4 and set its typ to “jwt”
Comment From: jzheaux
Thanks for the heads up, @jrehwaldt and @materia2021.
While this could be solved by manually constructing a DefaultJWTProcessor and passing it to the NimbusJwtDecoder constructor, I think it'd be nice for the builders to be equipped with a post-processor to save some of that boilerplate.
This isn't the first time that a desire to post-process the JWTProcessor has come up, so I think this could potentially address more than one issue.
At that point, you could do:
NimbusJwtDecoder jwtDecoder = withPublicKey(key)
.jwtProcessor(jwtProcessor -> jwtProcessor.setJWSTypeVerifier(...))
.build();
Can you confirm that this would resolve your issue?
If so, would one of you be able to put together a PR to add a post-processor to each of the builders?
Comment From: jrehwaldt
@jzheaux I had the same thought. There is a catch, though: JwtProcessor Does not implement the required methods. We‘d need to pass the DefaultJwtProcessor to the post-processor. If that’s okay I am happy to draft a PR.
On a side note why I did not propose it: I was concerned one could somehow break the security by misconfiguring Nimbus, making it a risky extension. On the other hand, it is for advanced usage and people should have an idea what that are doing anyway 🤷♂️
Comment From: jzheaux
@jrehwaldt I think if you have it pass a ConfigurableJWTProcessor<SecurityContext>, then that will do the trick.
I was concerned one could somehow break the security by misconfiguring Nimbus, making it a risky extension
It certainly does give the user more power, but it's no more power than they already have with the NimbusJwtDecoder constructor (which takes a fully-configured JWTProcessor).
If that’s okay I am happy to draft a PR.
Sounds great, @jrehwaldt.
Comment From: jrehwaldt
@jzheaux I created a draft PR in #8745 for PublicKeyJwtDecoderBuilder only. If that's the way to go I'll go ahead and add the same functionality for the other two builders. Please leave a review and comment on naming preferences and Javadoc improvements you'd like to see.