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.)

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!