Summary
Cannot integrate Spring Cloud Gateway (which uses Spring Security 5.2) with OAuth2 Authorization Server using Resource Owner Password Flow.
Actual Behavior
During handling any request below exception occurs:
java.lang.IllegalArgumentException: URI must not be null
at org.springframework.util.Assert.notNull(Assert.java:198) ~[spring-core-5.2.4.RELEASE.jar:5.2.4.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.security.oauth2.client.web.server.OAuth2AuthorizationRequestRedirectWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.csrf.CsrfWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
|_ checkpoint ⇢ HTTP GET "/oauth2/authorization/gateway" [ExceptionHandlingWebHandler]
Stack trace:
at org.springframework.util.Assert.notNull(Assert.java:198) ~[spring-core-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.util.UriComponentsBuilder.fromUriString(UriComponentsBuilder.java:212) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver.expandRedirectUri(DefaultServerOAuth2AuthorizationRequestResolver.java:214) ~[spring-security-oauth2-client-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver.authorizationRequest(DefaultServerOAuth2AuthorizationRequestResolver.java:131) ~[spring-security-oauth2-client-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.security.oauth2.client.web.server.DefaultServerOAuth2AuthorizationRequestResolver.lambda$resolve$3(DefaultServerOAuth2AuthorizationRequestResolver.java:121) ~[spring-security-oauth2-client-5.2.2.RELEASE.jar:5.2.2.RELEASE]
Checking implementation of DefaultServerOAuth2AuthorizationRequestResolver class, on lines 137-157 there is no case supporting Password Grant type:
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
[...]
} else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
[...]
} else {
throw new IllegalArgumentException("Invalid Authorization Grant Type (" + clientRegistration.getAuthorizationGrantType().getValue()
+ ") for Client Registration with Id: " + clientRegistration.getRegistrationId());
}
Is above some lack of implementation? Is Spring Security team are going to support OAuth2 Password Flow out-of-box like Authorization Code Flow ?
I know that Spring Security 5.2 introduced support for OAuth2 Password Flow, but documentation and any other references or samples* are not giving any clear information about such integration.
*Other references: * https://stackoverflow.com/questions/59383562/spring-security-5-2-password-flow * https://github.com/spring-projects/spring-security/issues/7715
Configuration
security:
oauth2:
client:
registration:
gateway:
provider: uaa
client-id: XXX
client-secret: XXX
authorization-grant-type: password
client-authentication-method: basic
provider:
uaa:
token-uri: http://localhost:8444/oauth/token
user-info-uri: http://localhost:8444/oauth/user
user-name-attribute: user_name
user-info-authentication-method: header
Version
Spring Security 5.2.2.RELEASE (provided by Spring Boot 2.2.5.RELEASE)
Many thanks for any response to this issue!
Comment From: jgrandja
@mario45211
This is the expected behaviour. DefaultServerOAuth2AuthorizationRequestResolver only supports authorization_code. The password grant flow does not participate in the Authorization Request/Response flow like the way authorization_code does. Please review the spec for password grant.
Issuing a request to /oauth2/authorization/gateway for a password client will fail as expected. The password grant needs to be initiated either by WebClient composed of ServerOAuth2AuthorizedClientExchangeFilterFunction OR using DefaultReactiveOAuth2AuthorizedClientManager that is configured with an ReactiveOAuth2AuthorizedClientProvider by ReactiveOAuth2AuthorizedClientProviderBuilder.builder().password().build().
Please see this Servlet sample for a complete configuration and usage for password grant.
I would encourage you to review the reference doc to understand how to configure and use an OAuth 2.0 Client with the password grant:
(NOTE: The references below are for the Servlet docs as the Reactive docs need to get revamped. Either way, the approach is the same just different implementation classes.)
- OAuth 2.0 Client
- OAuth2AuthorizedClientManager / OAuth2AuthorizedClientProvider
- Resource Owner Password Credentials
- WebClient integration for Servlet Environments
My guess is that Spring Cloud Gateway has not integrated the password grant as of yet. Please log an issue there to request the enhancement.
Comment From: mario45211
@jgrandja Thanks for this comprehensive answer.
I will do some more review how new Spring Security mechanism integrates with OAuth2 and try ask SCG team about such integration.
Best regards!