spring-security icon indicating copy to clipboard operation
spring-security copied to clipboard

Document configuration to support JWT Header TYP as "at+jwt"

Open andifalk opened this issue 4 years ago • 10 comments

Expected Behavior

Currently, if the JWT is having typ as "at+jwt", the token is rejected with message "Failed to authenticate since the JWT was invalid". I am aware that this has already been addressed in gh-9900 (which was declined for good reason) As this validation is done in Nimbus library and according to https://bitbucket.org/connect2id/nimbus-jose-jwt/issues/366 will never validate "jwt+at" by default (Starting with v8.0 the acceptable typ header values must be set explicitly), at least it should be documented in Spring Security reference docs how to reconfigure Nimbus for validating "jwt+at" like this:

@Bean 
JwtDecoder jwtDecoder() {
	DefaultJOSEObjectTypeVerifier<SecurityContext> verifier =
		new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("at+jwt"));
	NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(this.uri)
		.jwtProcessorCustomizer((processor) -> processor.setJWSTypeVerifier(verifier))
		.build()
        // ... any other decoder settings
	return decoder;
}

Current Behavior

Currently, such token is rejected with the message "Failed to authenticate since the JWT was invalid" and the documentation does not have any section for configuring this as referenced in gh-9900. To solve this currently, developers have to search spring security issues to find a solution because this is not documented in the spring security reference documentation.

Context

As the corresponding IETF specification for JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens has already been approved by the IESG and will be published as final RFC soon I expect more authorization server implementations issuing JWT tokens that are compliant to this new RFC.

I could also help to provide a PR for extending the documentation (maybe as part of section 12.3.10. Configuring Validation)

andifalk avatar Sep 15 '21 14:09 andifalk

Agreed, @andifalk. Would you be able to submit a PR adding the documentation both for servlet and reactive?

jzheaux avatar Sep 15 '21 15:09 jzheaux

@jzheaux Yes, I think I should be able to submit a PR for both web stacks.

andifalk avatar Sep 15 '21 16:09 andifalk

Hi, @andifalk! Are you still able to contribute a PR?

jzheaux avatar Nov 15 '21 23:11 jzheaux

Hi @jzheaux unfortunately I had to undergo an unplanned knee surgery. I will Not be able to start working on this before Dec 13th.

andifalk avatar Nov 29 '21 13:11 andifalk

Sorry to hear about your knee - I hope all goes well for you. Thanks for the update and good luck.

jzheaux avatar Dec 10 '21 18:12 jzheaux

So this is a really significant regression. The Nimbus Library went from not checking types at all, to requiring users to explicitly set the type, not even including valid default types. However, it sets the type "JWT" (and only this type) as valid, which is probably why this is not a bigger issue, because that type is the most popular. (probably?).

So we just updated our Spring dependencies, and everything broke :)

Anyway, the Spring Util classes (e.g. JwtDecoders) totally ignore this (of course, because it didn't exist as an issue) and do not allow the user to set the JWSTypeVerifier at all.

So lets assume we want to adapt JwtDecoders. (See here: https://github.com/spring-projects/spring-security/blob/main/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JwtDecoders.java)

We need to allow the user to set the JWSTypeVerifier from outside the class. As all methods are static, this can only be done using an extra parameter in all methods. Alternatively, we would have to make the methods non-static and allow the user to set the JWSTypeVerifier in the constructor (or Builder Pattern?) maybe even by defining a Bean somehwere.

I personally prefer the constructor/builder approach, as most people use this helper class to create a Bean JwtDecoder (see below). Defining multiple beans is just an overhead.

Anyway, we copied the JwtDecoders class and did this:

private static JwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {
        JwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
        OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
        String jwkSetUri = configuration.get("jwks_uri").toString();
        NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
            .jwtProcessorCustomizer((processor) -> {
                JwtDecoderProviderConfigurationUtils.addJWSAlgorithms(processor);
                processor.setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier(new JOSEObjectType("at+jwt")));
            }).build();
        jwtDecoder.setJwtValidator(jwtValidator);
        return jwtDecoder;
    }

This code did not change in our SecurityConfiguration (however it should, allowing us to set allowed types from here)

@Bean
    JwtDecoder jwtDecoder() {
        NimbusJwtDecoder jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuerUri);

        OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(jHipsterProperties.getSecurity().getOauth2().getAudience());
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

        jwtDecoder.setJwtValidator(withAudience);

        return jwtDecoder;
    }

youlabi avatar May 09 '22 16:05 youlabi

The RFC that defines at+jwt has been published as RFC 9068 by now.

As youlabi already pointed out. This is currently hard to configure since one has to replicate all the other decoder configuration from Spring.

shartte avatar Apr 05 '23 11:04 shartte

@jzheaux , hey josh. Anything new on this ?

youlabi avatar Jun 01 '23 12:06 youlabi

Any updates ?

burl21 avatar Feb 23 '24 14:02 burl21

I tried copying infrastructure code with the latest 6.2.2 in the manner described by @youlabi but ran into a case where I couldn't extend a package-private class. Instead, I'm going to create the NimbusJwtDecoder directly from the JWKS URI, which can be found as an attribute in the openid connect data at https://yourdomain.oktapreview.com/.well-known/openid-configuration:

..., "jwks_uri":"https://yourdomain.oktapreview.com/oauth2/v1/keys", ...

No framework code copying needed, I hope. @andifalk has the code snippet above to do this.

cbarry-semler avatar Mar 15 '24 14:03 cbarry-semler