Writing an okta OCID/oauth2 integration with our application and I've had to upgrade spring and spring security -

springVersion = '5.3.18'
springSecurityVersion = '5.7.5'

Describe the bug When I hit my application it correctly redirects off to the Okta login page (cloud) and I can log in there however when it gets redirected back in to our app, spring security runs in to an issue with what I assume is a dependency in there.

<23-Nov-2022 13:20:57,973 o'clock GMT> <Error> <HTTP> <BEA-101020> <[ServletContext@1470845587[app:Gradle___application_ear module:/myvhi path:null spec-version:3.1]] Servlet failed with an Exception
java.lang.NoSuchMethodError: com.nimbusds.jose.proc.JWSVerificationKeySelector.<init>(Ljava/util/Set;Lcom/nimbusds/jose/jwk/source/JWKSource;)V
                at org.springframework.security.oauth2.jwt.NimbusJwtDecoder$JwkSetUriJwtDecoderBuilder.jwsKeySelector(NimbusJwtDecoder.java:334)
                at org.springframework.security.oauth2.jwt.NimbusJwtDecoder$JwkSetUriJwtDecoderBuilder.processor(NimbusJwtDecoder.java:349)
                at org.springframework.security.oauth2.jwt.NimbusJwtDecoder$JwkSetUriJwtDecoderBuilder.build(NimbusJwtDecoder.java:362)
                at org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory.buildDecoder(OidcIdTokenDecoderFactory.java:167)
                at org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory.lambda$createDecoder$3(OidcIdTokenDecoderFactory.java:129)
                at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660)
                at org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory.createDecoder(OidcIdTokenDecoderFactory.java:128)
                at org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory.createDecoder(OidcIdTokenDecoderFactory.java:66)
                at org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider.createOidcToken(OidcAuthorizationCodeAuthenticationProvider.java:235)
                at org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider.authenticate(OidcAuthorizationCodeAuthenticationProvider.java:154)
                at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182)
                at org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.attemptAuthentication(OAuth2LoginAuthenticationFilter.java:195)
                at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:227)
                at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:217)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
                at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:178)
                at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
                at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
                at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
                at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:117)
                at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
                at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
                at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
                at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
                at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:112)
                at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:82)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
                at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
                at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
                at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42)
                at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
                at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
                at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221)
                at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
                at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
                at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
                at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:78)
                at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3701)
                at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3667)
                at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:326)
                at weblogic.security.service.SecurityManager.runAsForUserCode(SecurityManager.java:197)
                at weblogic.servlet.provider.WlsSecurityProvider.runAsForUserCode(WlsSecurityProvider.java:203)
                at weblogic.servlet.provider.WlsSubjectHandle.run(WlsSubjectHandle.java:71)
                at weblogic.servlet.internal.WebAppServletContext.doSecuredExecute(WebAppServletContext.java:2443)
                at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2291)
                at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2269)
                at weblogic.servlet.internal.ServletRequestImpl.runInternal(ServletRequestImpl.java:1703)
                at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1663)
                at weblogic.servlet.provider.ContainerSupportProviderImpl$WlsRequestExecutor.run(ContainerSupportProviderImpl.java:272)
                at weblogic.invocation.ComponentInvocationContextManager._runAs(ComponentInvocationContextManager.java:352)
                at weblogic.invocation.ComponentInvocationContextManager.runAs(ComponentInvocationContextManager.java:337)
                at weblogic.work.LivePartitionUtility.doRunWorkUnderContext(LivePartitionUtility.java:57)
                at weblogic.work.PartitionUtility.runWorkUnderContext(PartitionUtility.java:41)
                at weblogic.work.SelfTuningWorkManagerImpl.runWorkUnderContext(SelfTuningWorkManagerImpl.java:644)
                at weblogic.work.ExecuteThread.execute(ExecuteThread.java:415)
                at weblogic.work.ExecuteThread.run(ExecuteThread.java:355)

To Reproduce

Login on Okta page and when it redirects back to my app the spring security runs in to the exception above. When I try debug it with IntelliJ (which is what I'm using to run weblogic) the NimbusJwtDecoder class seems to have everything it needs - a Set and JWKSource. I can click on this in the stack trace and it shows class and I can click in the line to open the com.nimbusds.jose.proc.JWSVerificationKeySelector class and it is there in IntelliJ's external libraries and the constructor with the Set and JWKSource is in it. However the debugger can not attach a break point in it.

The package that the class is in is com.nimbusds:nimbus-jose-jwt:9.22. There is no mention of this in our build/gradle files so its pulled down by spring security as a dependency I assume. I have read something about spring versions not being compatible with it on some forums and some solutions suggested changing to version 8.20 but that gives the same result and I also changed to the latest version and again the result is the same.

Expected behavior After login on the Okta page I expect it to redirect back to our application where we can decipher the OIDC/JWT token and extract user data from it.

Sample

I have a class with my configuration which is annotated with @EnableWebSecuriuty. It contains the following:

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository(
            @Value("${okta.client.id}") String clientId,
            @Value("${okta.client.secret}") String clientSecret,
            @Value("${okta.sign.in.uri}") String signInUri,
            @Value("${okta.token.endpoint.uri}") String tokenEndpointUri,
            @Value("${okta.user.info.endpoint.uri}") String userInfoEndpointUri,
            @Value("${okta.redirect.uri}") String redirectUri,
            @Value("${okta.issuer}") String issuer,
            @Value("${okta.jwk.set.uri}") String jwkSetUri,
            @Value("${okta.scopes}") String[] scopes
    ) {
    ClientRegistration registration = CommonOAuth2Provider.OKTA.getBuilder("okta")
        .clientId(clientId)
        .clientSecret(clientSecret)
        .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
        .authorizationUri(signInUri)
        .redirectUri(redirectUri)
        .tokenUri(tokenEndpointUri)
        .userInfoUri(userInfoEndpointUri)
        .issuerUri(issuer)
        .jwkSetUri(jwkSetUri)
        .scope(scopes)
        .userNameAttributeName(MyvhiOktaUser.USER_ID_CLAIM)
        .build();
    return new InMemoryClientRegistrationRepository(registration);
}
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        logger.debug("Okta Login Configuration.");

        http.authorizeRequests()
                .antMatchers("/404", "/500")
                .permitAll()
                // html assets
                .antMatchers(HttpMethod.GET, "/img/**", "/styles/**", "/scripts/**")
                .permitAll()
                // MyVhi pages
                .antMatchers("/dashboard")
                .permitAll()
                // Everything else.
                .anyRequest()
                .hasRole("USER");

        http.oauth2Login(oauth2 -> oauth2
                .defaultSuccessUrl("/dashboard", true)
                .failureHandler(new SimpleUrlAuthenticationFailureHandler("/404"))
                .userInfoEndpoint(userInfo -> userInfo
                        .oidcUserService(this.oidcUserService())
                )
        ).sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);

        http.csrf().ignoringAntMatchers("/dwr/**", "/ws/**", "/authenticate");

        return http.build();
    }

Comment From: sjohnr

Hi @kevindoyleie, thanks for reaching out!

Based on the description you provided, it seems that your build is not actually using com.nimbusds:nimbus-jose-jwt:9.22, even though it seems like it should be since that is the version of the dependency that Spring Security 5.7.x is depending on.

At this point, it would be very helpful if you could provide a minimal, reproducible sample which removes weblogic (and ideally uses Spring Boot instead), as well as any additional dependencies (if any) and the extra configuration in your @EnableWebSecurity class. A zip file or standalone github repository would be ideal. I'm familiar with Okta, so if you omit the client-id and secret for Okta, I can fill those in on my own.

I'm hopeful that this process will reveal where the other version of Nimbus is coming from (such as two versions of Spring Security on the classpath at the same time), but if not then we will have a minimal sample that reproduces the issue to look into.

Comment From: kevindoyleie

Thanks @sjohnr. Will get back to you when I have a working example outside our network.

Comment From: kevindoyleie

Ok so I've done a spring boot app with the same config and its working as expected. The nimbusds jar is not an issue. Okta returns to the app with all the expected user data on the token. On further investigation it looks like a really old version of that jar is in the weblogic common modules folder and this does not contain the constructor that the updated version that spring security uses and it looks like it is finding the old one first so could be a class path issue or could be that I'll have to try force the version in the app somehow but either way its not a spring security issue so this thread can be closed.

Comment From: sjohnr

Thanks for the update, @kevindoyleie!