Describe the bug Scope mapping handling changed with https://github.com/spring-projects/spring-security/issues/12112.

https://github.com/spring-projects/spring-security/commit/2915a70bf786e2bd0780d686d432b9ba85617522#diff-73bd44f873d78e3d71e6a0fa18644a304d562a4a9fd2e303e913f6ed20a0ad16R78-R83

Spring Security Incorrect scope map fix

  • OidcAuthorizationCodeAuthenticationProvider.authenticate() calls OidcAuthorizationCodeAuthenticationProvider.getResponse())
  • DefaultAuthorizationCodeTokenResponseClient.getTokenResponse does NOT add the scopes anymore. If no scopes are returned by default by the IdP, the scopes list is empty.
  • The comment If AccessTokenResponse.scope is empty, then we assume all requested scopes were granted. seems to say something completely different.
  • Back in OidcAuthorizationCodeAuthenticationProvider.authenticate() the user info has to be loaded: this.userService.loadUser().
  • OidcUserService.loadUser calls this.shouldRetrieveUserInfo(userRequest) which returns false now, because the scopes (userRequest.getAccessToken().getScopes()) is empty.
  • Because of this the userInfo is not loaded (it is null) and can't be used for example in the userAuthoritiesMapper.

To Reproduce Our scopes are configured like this:

spring.security.oauth2.client.registration.default.scope=openid,profile,entitlements

Use the userAuthoritiesMapper with a token-uri endpoint that doesn't return a list of scopes.

        http.oauth2Login()
                .userInfoEndpoint().userAuthoritiesMapper(this.userAuthoritiesMapper());

In the authorities mapper try to use the oidcUserAuthority.getUserInfo() (which is now null).

Expected behavior oidcUserAuthority.getUserInfo() should not be null.

Sample No sample yet.

Ping @sjohnr

Comment From: sjohnr

Thanks @DamianFekete. Please see the blog post on cve-2022-31690 and related advisory for details about the fix and why it was applied.

The comment If AccessTokenResponse.scope is empty, then we assume all requested scopes were granted. seems to say something completely different.

See RFC 6749, Section 5.1 for details on why we assume that all scopes were granted. The unfortunate reality is that we cannot rely on that assumption in all cases, hence the fix.

I'll look into a fix for OidcUserService. In the meantime, you can apply the following workaround to consume the latest version of Spring Security:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // ...
            .oauth2Login(Customizer.withDefaults());
        return http.build();
    }

    @Bean
    public OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
        OidcUserService oidcUserService = new OidcUserService();
        oidcUserService.setAccessibleScopes(Collections.emptySet());
        return oidcUserService;
    }

}

You can apply this workaround if it is acceptable to query the User Info endpoint on every login. If that's not acceptable in your case, let me know and I'll see about a different workaround.

Comment From: tobiaskrauss

Thanks @sjohnr for your fast and helpful response. I was working with @DamianFekete on this issue. The workaround works fine for our application. As we do not have too many users on this application, it should be fine for us that the user info endpoint is queried on every login.

Comment From: shresthaujjwal

FYI, seeing same issue with 5.7.5 version too. Workaround works for me

Comment From: Clemens-E

I'm seeing the same behaviour on 5.7.8 while integrating a login with Azure B2C IDM. Spring was supposed to call the UserInfo endpoint to get the users' email/name after login, but UserInfo is always null, and I can't see any call to the endpoint in the debug logs. I can't exclude with certainty that it's not caused by some (miss-) configuration on either spring or azure b2c.

So, either way, I just wanted to put my fix here after hours of debugging in case anyone has a similar issue: add the above-mentioned workaround (I had to remove the security filter chain as it interfered with my WebSecurityConfigurerAdapter and you can't have both)

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
    // see https://github.com/spring-projects/spring-security/issues/12144
    @Bean
    public OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
        OidcUserService oidcUserService = new OidcUserService();
        oidcUserService.setAccessibleScopes(Collections.emptySet());
        return oidcUserService;
    }
}

However, spring now validates the UserInfo and will complain if you do not have a "sub" property, so make sure your UserInfo endpoint includes a sub property (mine didn't) If you are using the Azure B2C Identity Experience Framework, you have to configure this: https://github.com/azure-ad-b2c/samples/blob/master/policies/user-info-endpoint/policy/UserInfo_TrustFrameworkExtensions.xml#L34 and additionally add the mentioned sub claim with: <InputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/> This way the sub property will be included in the UserInfo Response and spring won't complain.

I apologize if this seems off-topic, but I thought it would be valuable to share my solution here for others experiencing the same problem, even if it's a niche topic.

Comment From: asinghania71

@sjohnr the issue still exists if we use reactive spring boot application I have raised a PR for same, would be great if you can take a look and help me get this fixed