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.