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

Setting clock-skew according to documentation disables security features

Open RainerGanss opened this issue 1 month ago • 0 comments

Expected Behavior

I want to be able to set the clock-skew (ideally per property) and not change anything else.

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          clock-skew: 5m  # does not exist - would have been great

Current Behavior

According to OAuth 2.0 Resource Server JWT the correct way to do so is:

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

     OAuth2TokenValidator<Jwt> withClockSkew = new DelegatingOAuth2TokenValidator<>(
            new JwtTimestampValidator(Duration.ofSeconds(60)),
            new JwtIssuerValidator(issuerUri));

     jwtDecoder.setJwtValidator(withClockSkew);

     return jwtDecoder;
}

This works - but it is dangerous in my opinion. I also set the two (very important) properties

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: xxx
          jwk-set-uri: xxx

which triggers the auto configuration OAuth2ResourceServerJwtConfiguration

@Bean
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri")
JwtDecoder jwtDecoderByJwkKeySetUri(ObjectProvider<JwkSetUriJwtDecoderBuilderCustomizer> customizers) {
	JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withJwkSetUri(this.properties.getJwkSetUri())
		.jwsAlgorithms(this::jwsAlgorithms);
	customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
	NimbusJwtDecoder nimbusJwtDecoder = builder.build();
	String issuerUri = this.properties.getIssuerUri();
	OAuth2TokenValidator<Jwt> defaultValidator = (issuerUri != null)
			? JwtValidators.createDefaultWithIssuer(issuerUri) : JwtValidators.createDefault();
	nimbusJwtDecoder.setJwtValidator(getValidators(defaultValidator));
	return nimbusJwtDecoder;
}

If I understand it correctly, if I follow the documentation the JwtDecoder will NOT use the JwkSetUri property, the jwsAlgorithms and also not use the default validators. Both methods JwtValidators.createDefaultWithIssuer(issuerUri) : JwtValidators.createDefault(); will not only create the timestamp validator (and issuer validator if available) but also the X509CertificateThumbprintValidator (which is package visible btw). This one will also be missing if I configure the clock skew according to the documentation.

Context

What I implemented now is the following:

@Value("${xxx.jwt.clock-skew:PT5M}")
private Duration clockSkew;

@Bean
public BeanPostProcessor jwtDecoderClockSkewPostProcessor(final OAuth2ResourceServerProperties properties) {
    return new BeanPostProcessor() {
        @Override
        public Object postProcessAfterInitialization(@Nonnull final Object bean, @Nonnull final String beanName) {
            if (bean instanceof final NimbusJwtDecoder jwtDecoder) {
                jwtDecoder.setJwtValidator(createValidators(properties));
            }
            return bean;
        }
    };
}

private OAuth2TokenValidator<Jwt> createValidators(final OAuth2ResourceServerProperties properties) {
    final List<OAuth2TokenValidator<Jwt>> validators = new ArrayList<>();
    validators.add(new JwtTimestampValidator(clockSkew));

    final String issuerUri = properties.getJwt().getIssuerUri();
    if (issuerUri != null) {
        validators.add(new JwtIssuerValidator(issuerUri));
    }

    return JwtValidators.createDefaultWithValidators(validators);
}

My opinion:

  • The static methods at JwtValidators should maybe be replaced by a builder? That would have helped.
  • The auto configuration of the JwtDecoder seems hard to combine with other properties (like the clock-skew). Tapping into this configuration like I did was pretty tricky (until I arrived at the solution above).

Cheers, Rainer

RainerGanss avatar Nov 27 '25 09:11 RainerGanss