springspring-boot

Spring Boot 3.4.0 An error occurred while attempting to decode the Jwt: Unable to obtain X509Certificate from current request


I'm doing migration from Spring Boot 3.2.8 to 3.4.0 and Spring Cloud 2023.0.3 to 2024.0.0. My application uses OAuth2 with Spring Security in quite a normal way:

@Slf4j
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@EnableFeignClients
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(JwtDecoder decoder, HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(httpRequests -> httpRequests
                        .requestMatchers("/actuator/**", "/v3/api-docs/**", "/swagger-ui/**").permitAll()
                        .anyRequest().authenticated())
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .csrf(AbstractHttpConfigurer::disable)
                .oauth2ResourceServer(jwtConf -> jwtConf.jwt(jwtConfigurer -> jwtConfigurer.decoder(decoder)));
        return http.build();
    }
}

Here's my application.yml:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jws-algorithms: ${JWS_ALGORITHMS}
          issuer-uri: ${ISSUER_URI}
          jwk-set-uri: ${JWK_SET_URI}

With Spring Boot 3.2.8 everything works fine, but after I switch the version to 3.4.0 incoming requests start failing with

org.springframework.security.oauth2.server.resource.InvalidBearerTokenException: An error occurred while attempting to decode the Jwt: Unable to obtain X509Certificate from current request.
    at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.getJwt(JwtAuthenticationProvider.java:103) ~[spring-security-oauth2-resource-server-6.4.1.jar:6.4.1]
    at org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider.authenticate(JwtAuthenticationProvider.java:88) ~[spring-security-oauth2-resource-server-6.4.1.jar:6.4.1]
    at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182) ~[spring-security-core-6.4.1.jar:6.4.1]
    at org.springframework.security.authentication.ObservationAuthenticationManager.lambda$authenticate$1(ObservationAuthenticationManager.java:54) ~[spring-security-core-6.4.1.jar:6.4.1]
    at io.micrometer.observation.Observation.observe(Observation.java:565) ~[micrometer-observation-1.14.1.jar:1.14.1]
    at org.springframework.security.authentication.ObservationAuthenticationManager.authenticate(ObservationAuthenticationManager.java:53) ~[spring-security-core-6.4.1.jar:6.4.1]
    at org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter.doFilterInternal(BearerTokenAuthenticationFilter.java:137) ~[spring-security-oauth2-resource-server-6.4.1.jar:6.4.1]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.2.0.jar:6.2.0]

Here is how my jwks.json looks like:

 "keys" : [ {
    "kty" : "RSA",
    "use" : "sig",
    "alg" : "PS256",
    "kid" : "kid",
    "x5c" : [ "lost-of-chars==" ],
    "x5t#S256" : "lost-of-chars",
    "e" : "AQAB",
    "n" : "lost-of-chars"
  }

I've looked into migration guide but haven't found any clue. My question is how could I fix this.

P.S. The same happens with Spring Boot 3.3.0


Solution

  • Currently I work this around with this declaration

    @Bean
    JwtDecoder jwtDecoderByJwkKeySetUri(
            ObjectProvider<JwkSetUriJwtDecoderBuilderCustomizer> customizers,
            OAuth2ResourceServerProperties properties
    ) {
        var jwt = properties.getJwt();
        var builder = NimbusJwtDecoder
                .withJwkSetUri(jwt.getJwkSetUri())
                .jwsAlgorithms(signatureAlgorithms ->
                        jwt.getJwsAlgorithms().stream().map(SignatureAlgorithm::from).forEach(signatureAlgorithms::add)
                );
        customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
    
        var issuerUri = jwt.getIssuerUri();
        var validators = List.of(new JwtIssuerValidator(issuerUri), new JwtTimestampValidator());
        var defaultValidator = new DelegatingOAuth2TokenValidator<>(validators);
    
        var nimbusJwtDecoder = builder.build();
        nimbusJwtDecoder.setJwtValidator(defaultValidator);
        return nimbusJwtDecoder;
    }
    

    However, I suspect to do migration in a proper way I need to supply certificate somehow.