Summary

Before my final logout success redirect, I am attempting to make an additional call to a logout endpoint given by my OpenID provider.

The provider (login.gov) states that my logout request should have this form:

https://idp.int.identitysandbox.gov/openid_connect/logout?
id_token_hint=eyJ0eX...&
post_logout_redirect_uri=${REDIRECT_URI}&
state=abcdefghijklmnopabcdefghijklmnop`

Where id_token_hint is:

An id_token value from the token endpoint response.

Their doc: https://developers.login.gov/oidc/#logout

Actual Behavior

1) I login via my /login endpoint. 2) I verify I can obtain my user info by hitting my /login_profile endpoint. 3) I navigate to /logout. 4) Upon reaching the onLogoutSuccess callback of SimpleUrlLogoutSuccessHandler, I receive a cast error:

java.lang.ClassCastException: org.springframework.security.oauth2.core.user.DefaultOAuth2User cannot be cast to org.springframework.security.oauth2.core.oidc.user.OidcUser

I attempt this by casting my Authentication object as a OAuth2AuthenticationToken. That cast is successful, but I cannot cast the principal of that new object as an OidcUser. It is assuming the principal is an OAuth2User which does not have an idToken property.

Expected Behavior

I expect to be able to retrieve the id_token (a signed JWT) that was originally returned from my token request from my onLogoutSuccess method.

override fun onLogoutSuccess(request: HttpServletRequest?, response: HttpServletResponse?, authentication: Authentication?) {
        val authenticationToken: OAuth2AuthenticationToken = authentication as OAuth2AuthenticationToken
        val user: OidcUser = authenticationToken.principal as OidcUser
        val idToken: OidcIdToken = user.idToken

Configuration

override fun configure(http: HttpSecurity) {
        http.authorizeRequests()
            // login, login failure, and index are allowed by anyone
            .antMatchers(
                    LOGIN_ENDPOINT,
                    LOGIN_SUCCESS_ENDPOINT,
                    LOGIN_PROFILE_ENDPOINT,
                    LOGIN_FAILURE_ENDPOINT,
                    LOGOUT_ENDPOINT,
                    LOGOUT_SUCCESS_ENDPOINT,
                    "/"
            )
                .permitAll()
            // any other requests are allowed by an authenticated user
            .anyRequest()
                .authenticated()
            .and()
            // custom logout behavior
            .logout()
                .logoutRequestMatcher(AntPathRequestMatcher(LOGOUT_ENDPOINT))
                .logoutSuccessUrl(LOGOUT_SUCCESS_ENDPOINT)
                .deleteCookies("JSESSIONID")
                .invalidateHttpSession(true)
                .logoutSuccessHandler(LoginGovLogoutSuccessHandler(authorizedClientService))
            .and()
            // configure authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider
            .oauth2Login()
                .authorizationEndpoint()
                .authorizationRequestResolver(LoginGovAuthorizationRequestResolver(clientRegistrationRepository))
                .authorizationRequestRepository(authorizationRequestRepository())
                .and()
                .tokenEndpoint()
                .accessTokenResponseClient(accessTokenResponseClient())
                .and()
                .failureUrl(LOGIN_FAILURE_ENDPOINT)
                .successHandler(LoginGovAuthenticationSuccessHandler())
    }

Version

springBootVersion = '2.1.0.RELEASE'

Sample

Here is my project on GitHub.

Please refer to the class LoginGovLogoutSuccessHandler for the code in question.

Comment From: jgrandja

@forgo I believe the issue you are having is related to your scope: openid email configuration. The scope(s) should be comma separated so scope: openid, email - the YAML reader will read this into 2 elements instead of 1 element containing both scopes, as in your case, which results in you NOT going through the OpenID Connect flow - hence no ID Token and your Principal is DefaultOAuth2User.

Try that out and let me know how it goes.

Comment From: forgo

@jgrandja

Like a boss, dude! I think I originally had a "+" between "openid" and "email", and that was causing problems until I added the space. I would have been lost without this suggestion. That comma allowed my cast to succeed so I could extract the id_token!

Thanks again!

Comment From: IrfanOjas

Hi i am also facing the same issue i,e java.lang.ClassCastException: org.springframework.security.oauth2.core.user.DefaultOAuth2User cannot be cast to org.springframework.security.oauth2.core.oidc.user.OidcUser

Here i am only using an offline scope in my client application. Can someone please help to resolve the issue.