Expected Behavior I want to control which scopes are requested by the DefaultOAuth2AuthorizationRequestResolver from the frontend application. The setAuthorizationRequestCustomizer is not sufficient as I don't know beforehand which scopes I want to use when instantiating the resolver.

Current Behavior I can't control which scopes are requested without creating my own implementation of OAuth2AuthorizationRequestResolver.

Context The use case is as follows: The user logs in with SSO and are redirected to the third party authorization server that we use with the scopes openid and profile. Later, we want to have a button in the frontend which redirects to the third party authorization server with a request containing openid, profile and foo. An example behavior could be the following: redirecting to /oauth2/authorization/{clientRegistrationId} from the frontend should give us the basic scopes, and any extra scopes could be added by /oauth2/authorization/{clientRegistrationId}?scopes=foo.

Workaround Since I essentially want to use the DefaultOAuth2AuthorizationRequestResolver, I am now creating a class implementing the interface OAuth2AuthorizationRequestResolverwhich creates a new instance of DefaultOAuth2AuthorizationRequestResolver for every request, modifying the builder through the setAuthorizationRequestCustomizer. Then this instance is used further to handle that specific request. I.e it looks like this:

    override fun resolve(request: HttpServletRequest?, clientRegistrationId: String?): OAuth2AuthorizationRequest? {
        if (request == null) {
            return null
        }
        //customizedResolver instantiates DefaultOAuth2AuthorizationRequestResolver with a builder that is
        // dependent on the  request
        return customizedResolver(request).resolve(request, clientRegistrationId)
    }

Possible solution An idea I have is that the DefaultOAuth2AuthorizationRequestResolver exposes a setter for defining a function that will handle the case where request parameters are present. It should then always ensure that the request parameters is equal to the scopes defined for that client or a subset of these scopes.

Comment From: jgrandja

@apamildner If you need access to the HttpServletRequest to determine which scope's the client needs to request, then you need to provide a custom OAuth2AuthorizationRequestResolver. The setAuthorizationRequestCustomizer() would not work for your use case since you don't have access to HttpServletRequest (NOTE: You can also access HttpServletRequest via RequestContextHolder).

The following configuration is the recommended way to customize the OAuth2AuthorizationRequest using input from the HttpServletRequest:

@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private ClientRegistrationRepository clientRegistrationRepository;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(authorize -> authorize
                .anyRequest().authenticated()
            )
            .oauth2Login(oauth2 -> oauth2
                .authorizationEndpoint(authorization -> authorization
                    .authorizationRequestResolver(
                        authorizationRequestResolver(this.clientRegistrationRepository)
                    )
                )
            );
    }

    private OAuth2AuthorizationRequestResolver authorizationRequestResolver(
            ClientRegistrationRepository clientRegistrationRepository) {

        final DefaultOAuth2AuthorizationRequestResolver delegate =
                new DefaultOAuth2AuthorizationRequestResolver(
                        clientRegistrationRepository, "/oauth2/authorization");

        return new OAuth2AuthorizationRequestResolver() {

            @Override
            public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
                OAuth2AuthorizationRequest authorizationRequest = delegate.resolve(request);
                return authorizationRequest != null ?
                        customize(authorizationRequest, request) :
                        null;
            }

            @Override
            public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId) {
                OAuth2AuthorizationRequest authorizationRequest = delegate.resolve(request, clientRegistrationId);
                return authorizationRequest != null ?
                        customize(authorizationRequest, request) :
                        null;
            }

            private OAuth2AuthorizationRequest customize(OAuth2AuthorizationRequest authorizationRequest,
                    HttpServletRequest request) {
                return OAuth2AuthorizationRequest.from(authorizationRequest)
                        .scopes(extractScopes(request))     // Override
                        .build();
            }

            private Set<String> extractScopes(HttpServletRequest request) {
                Set<String> scopes = Collections.emptySet();

                // TODO Extract `scopes` parameter from request

                return scopes;
            }
        };
    }
}

I'm going to close this as the solution provided will work for your use case.

Comment From: apamildner

Thank you for answering, this seems like a better solution.