Summary

I configured a Spring Boot app to use OIDC login. The (minimalistic) security confguration is provided below. Logging in work fine, but I notice that the pricipal I get back is an instance of DefaultOauthUser, instead of the expected OidcUser (given that I use a OidcUserService). It backfires during the logout process since OidcClientInitiatedLogoutSuccessHandler is expecting a principal instance of OidcUser (if not it just skips the OIDC logout). The strange thing is that I placed a breakpoint at the first line of OidcUserService.loadUser and it never stop there, confirming that this userService is never used. So there is definitely something fishy here...

Configuration

Here is the security config. There is no other beans.

@Configuration
public class SecurityConfig {

    @Autowired
    private ClientRegistrationRepository clientRegistrationRepository;

    private LogoutSuccessHandler oidcLogoutSuccessHandler() {
        OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler =
                new OidcClientInitiatedLogoutSuccessHandler(
                        this.clientRegistrationRepository);
        oidcLogoutSuccessHandler.setPostLogoutRedirectUri("/uio");
        return oidcLogoutSuccessHandler;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        OidcUserService oidcUserService = new OidcUserService();
        http.authorizeRequests(authorizeRequests ->
                        authorizeRequests.anyRequest().authenticated()
                )
                .csrf().disable()
                .oauth2Login(oauthLogin ->
                        oauthLogin.userInfoEndpoint().oidcUserService(oidcUserService))
                .logout(logout -> logout
                        .logoutSuccessHandler(oidcLogoutSuccessHandler()));
        return http.build();
    }
}

Version

Spring boot 2.7.5

Comment From: mmarcadella

I dug a bit further: OAuth2LoginConfigurer.init() adds the following AuthenticationProviders to the ProviderManager; - Oauth2LoginAuthenticationProvider - OidcAuthorizationCodeAuthenticationProvider Since the ProviderManager attempts authentication in order and stops at the first successful provider, it returns an Oauth2User and skips the OidcProvider.

That doesn't look like the intended behavior, does it?? Shouldn't it add the OidcAuthorizationCodeAuthenticationProvider before Oauth2LoginAuthenticationProvider??

Comment From: mmarcadella

Further debugging: In ClientRegistration.oidc(), the line ClientRegistration.Builder builder = withProviderConfiguration(metadata, issuer.toASCIIString()) .jwkSetUri(metadata.getJWKSetURI().toASCIIString()); generates a Builder which has scopes sets to null instead of the content of metadeta.scope (it looks like the parser ignores metadeta.scope). The result down the line is an AuthorizationRequest which contains an empty set of scopes and as a result, the OAuth2LoginAuthorizationProvider runs since the set of scopes contained by the authorization object does not contain "openid". A quick fix is to add spring.security.oauth2.client.registration.[registrationId].scope=openid to the config. But the fact that withProviderConfiguration() ignores the scopes still looks like a bug, doesn't it?

Comment From: sjohnr

@mmarcadella sorry for the delay on this, and apologies but I'm having a hard time following the comments. Would you be able to provide a minimal, reproducible sample with full steps to reproduce the issue? If your setup involves an authorization server, please also describe the setup required for the authorization server.

Comment From: mmarcadella

When re-reading my last comment, I have to admit I am confused too... Why would withProviderConfiguration() get any scopes from the .well-known file?? For anyone experiencing the same symptoms, the solution is simple: make sure you add openid to the scope list
spring.security.oauth2.client.registration.[registrationId].scope in your .properties file.