Skip to content

NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder holds a reference to JWSVerificationKeySelector before ConfigurableJWTProcessor.setJWSKeySelector is executed #12960

@meverden

Description

@meverden

The ConfigurableJWTProcessor.setJWSKeySelector(JWSKeySelector) is unable to update the reference used by NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder.processor(). The processor holds a reference to the default JWSKeySelector created within NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder.jwsKeySelector() via a method reference within JwkSetUriReactiveJwtDecoderBuilder.getExpectedJwsAlgorithms() before the ConfigurableJWTProcessorsetJWSKeySelector(JWSKeySelector) is executed.

The following unit test will show that the processor is executing the wrong JWSVerificationKeySelector.isAllowed(JWSAlgorithm) reference.

@ExtendWith(MockitoExtension.class)
class NimbusReactiveJwtDecoderTest {

    @Mock private WebClient webClient;
    @Mock private RequestHeadersUriSpec<?> requestHeadersUriSpec;
    @Mock private ResponseSpec responseSpec;

    @Test
    void customizeJWSKeySelector() throws Exception {

        var jwkSet = new JWKSet(
                new RSAKeyGenerator(RSAKeyGenerator.MIN_KEY_SIZE_BITS)
                .algorithm(JWSAlgorithm.RS512)
                .keyID(UUID.randomUUID().toString())
                .keyUse(KeyUse.SIGNATURE)
                .generate());

        var jwkSource = new ImmutableJWKSet<>(jwkSet);

        var jwt = new NimbusJwtEncoder(jwkSource).encode(
                JwtEncoderParameters.from(JwsHeader.with(SignatureAlgorithm.RS512).build(), 
                        JwtClaimsSet.builder().issuer("issuer").build()));

        // stub WebClient response for ReactiveRemoteJWKSource
        Mockito.doReturn(requestHeadersUriSpec).when(webClient).get();
        Mockito.doReturn(requestHeadersUriSpec).when(requestHeadersUriSpec).uri(Mockito.anyString());
        Mockito.when(requestHeadersUriSpec.retrieve()).thenReturn(responseSpec);
        Mockito.when(responseSpec.bodyToMono(String.class)).thenReturn(Mono.just(jwkSet.toString()));
        
        var decoder = NimbusReactiveJwtDecoder
                .withJwkSetUri("http://localhost/oauth2/jwks") // use NimbusReactiveJwtDecoder.JwkSetUriReactiveJwtDecoderBuilder
                .webClient(webClient) // stub remote call
                .jwtProcessorCustomizer(processor -> {

                    // this reference will not be used by the "Function<JWSAlgorithm, Boolean> expectedJwsAlgorithms" within JwkSetUriReactiveJwtDecoderBuilder.processor()
                    var jwsKeySelector = new JWSVerificationKeySelector<>(Set.of(JWSAlgorithm.RS512),  jwkSource);

                    // the JwkSetUriReactiveJwtDecoderBuilder.processor() still holds a reference to the JWSVerificationKeySelector instantiated
                    // within JwkSetUriReactiveJwtDecoderBuilder.jwsKeySelector(), via the method reference created by JwkSetUriReactiveJwtDecoderBuilder.getExpectedJwsAlgorithms(), held before the customizer is executed.
                    processor.setJWSKeySelector((JWSVerificationKeySelector) jwsKeySelector);
                })
                .build();

        // execute the processor
        decoder.decode(jwt.getTokenValue())
            .block();
    }

}

Metadata

Metadata

Assignees

Labels

in: oauth2An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose)type: bugA general bug

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions