Spring-security: Improve OAuth2LoginSpec with more configuration options

Created on 28 Jul 2018  路  16Comments  路  Source: spring-projects/spring-security

Summary

Improve org.springframework.security.config.web.server.ServerHttpSecurity.OAuth2LoginSpec
for more configuration options.

Actual Behavior

  1. Chaining of OAuth2LoginSpec not available, missing public ServerHttpSecurity and() { return ServerHttpSecurity.this; }
  2. Static routes for
    > /oauth2/authorization/{registrationId}
    > /login/oauth2/code/{registrationId}

Expected Behavior

  1. allow chaining

    1. allow to override/configure oauthRedirectFilter (or authorizationRequestBaseUri in particular)
    2. allow to override/configure AuthenticationWebFilter.requiresAuthenticationMatcher (or PathPatternParserServerWebExchangeMatcher -> pattern => requestBaseUri in particular)

Configuration

http.oauth2Login();

Version

Boot 2.1.0.SNAPSHOT
Spring Security 5.1.0.M2

config oauth2 enhancement

All 16 comments

The first part was easy so I fixed it in a separate issue. The next pieces are going to need to wait until we get #5610 resolved

I am writing Service with WebFlux Framework Below are Technologies i am trying to use
Spring Boot 2.1.0.M3
Spring Security 5.1.0.RC2
In Spring Security It's trying to match /oauth2/authorization/{registrationId} with Path i configured which is different and it's not matching and throwing 401.
Is there any way to override /oauth2/authorization/{registrationId} this with value i am using

Have the static routes:

/oauth2/authorization/{registrationId}
/login/oauth2/code/{registrationId}

Been made overridable or configurable yet?

@nickbr23 @VijaySidhu The configuration points for overriding the authorization request and authorization response base uri's is not available as of yet. Is anyone interested in submitting a PR for this?

At the moment I'm think we need the following:

OAuth2LoginSpec.authenticationMatcher(ServerWebExchangeMatcher matcher)

OAuth2LoginSpec.authorizationRequestResolver(ServerOAuth2AuthorizationRequestResolver resolver)

I succeeded getting it working, see following snippet:

    private fun oauth2Login(
            http: ServerHttpSecurity,
            authBaseUri: String = "/api/auth/oauth2/authorization",
            loginBaseUri: String = "/api/auth/oauth2/code") {
        val clientRegistrationRepository = getClientRegistrationRepository()
        val oauthRedirectFilter = OAuth2AuthorizationRequestRedirectWebFilter(
                DefaultServerOAuth2AuthorizationRequestResolver(
                        clientRegistrationRepository,
                        PathPatternParserServerWebExchangeMatcher("$authBaseUri/{registrationId}")))

        val client = WebClientReactiveAuthorizationCodeTokenResponseClient()
        val userService = DefaultReactiveOAuth2UserService()
        var manager: ReactiveAuthenticationManager = MyOAuth2LoginReactiveAuthenticationManager(client, userService,
                accountService)

        val oidcAuthenticationProviderEnabled = ClassUtils.isPresent(
                "org.springframework.security.oauth2.jwt.JwtDecoder", this.javaClass.classLoader)
        if (oidcAuthenticationProviderEnabled) {
            val oidc = OidcAuthorizationCodeReactiveAuthenticationManager(client, OidcReactiveOAuth2UserService())
            manager = DelegatingReactiveAuthenticationManager(oidc, manager)
        }

        val authenticationFilter = AuthenticationWebFilter(manager)
        authenticationFilter.setRequiresAuthenticationMatcher(PathPatternParserServerWebExchangeMatcher("$loginBaseUri/{registrationId}"))
        authenticationFilter.setServerAuthenticationConverter(ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(clientRegistrationRepository))

        authenticationFilter.setAuthenticationSuccessHandler(OAuth2CodeAuthenticationSuccessHandler())
        authenticationFilter.setAuthenticationFailureHandler { _, exception -> Mono.error(exception) } // TODO: metrics
        authenticationFilter.setSecurityContextRepository(WebSessionServerSecurityContextRepository())

        http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC)
        http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION)
    }

Please note MyOAuth2LoginReactiveAuthenticationManager and accountService are custom..

var manager: ReactiveAuthenticationManager = MyOAuth2LoginReactiveAuthenticationManager(client, userService,
                accountService)

PS: that's on spring boot version 2.1.1.RELEASE, not SNAPSHOT as above in the original issue description.

At the moment I'm think we need the following:

OAuth2LoginSpec.authenticationMatcher(ServerWebExchangeMatcher matcher)

OAuth2LoginSpec.authorizationRequestResolver(ServerOAuth2AuthorizationRequestResolver resolver)

That would work.

For just overriding the uri's, it should be as simple as providing:

OAuth2LoginSpec.authorizationRequestBaseUri(String authorizationRequestBaseUri)
OAuth2LoginSpec.loginProcessingUrl(String loginProcessingUrl)

So that it mirrors the servlets builder.

If you're happy with just providing the uri's then feel free to assign it to me and I'll handle the PR.

Thanks for the feedback @nickbr23

So that it mirrors the servlets builder.

OAuth2LoginSpec.authorizationRequestBaseUri(String authorizationRequestBaseUri)
OAuth2LoginSpec.loginProcessingUrl(String loginProcessingUrl)

This does not actually mirror the DSL in OAuth2LoginConfigurer. Check out the full DSL here

The preference is to use ServerOAuth2AuthorizationRequestResolver over just configuring the authorization request URI as it provides more flexibility. This is how http.oauth2Client().authorizationCodeGrant().authorizationRequestResolver() has been designed as well. Also, we are considering deprecating authorizationEndpoint.baseUri().

The new configuration API's we would want to introduce are as follows:

OAuth2LoginSpec.authorizationRequestResolver(ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver)

OAuth2LoginSpec.authenticationMatcher(ServerWebExchangeMatcher authenticationMatcher)

Let me know if you're still interested in submitting a PR for this improvement.

Let me know if you're still interested in submitting a PR for this improvement.

Sure, I'm happy to do it.

If we introduce
OAuth2LoginSpec.authorizationRequestResolver(ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver) won't there still be an issue with changing the path from method OAuth2LoginSpec.getLinks as this should match the path in the new request resolver?

OAuth2LoginSpec.getLinks is used for the default login page. If the user provides a customAuthorizationRequestResolver, it is also their responsibility to provide a custom login page that uses the custom authorization request base uri used by customAuthorizationRequestResolver. So this is not an issue as OAuth2LoginSpec.getLinks does not need to be updated as part of the changes required for this PR. Also keep in mind that the default login page is generally used for development/testing/samples as a convenience. Production applications typically provide a custom login page.

The PR is yours @nickbr23. Let me know if you have any other questions. Thanks for taking this on!

OAuth2LoginSpec.getLinks is used for the default login page. If the user provides a customAuthorizationRequestResolver, it is also their responsibility to provide a custom login page that uses the custom authorization request base uri used by customAuthorizationRequestResolver. So this is not an issue as OAuth2LoginSpec.getLinks does not need to be updated as part of the changes required for this PR. Also keep in mind that the default login page is generally used for development/testing/samples as a convenience. Production applications typically provide a custom login page.

@jgrandja Is there a way to customize the login page and override the behavior OAuth2LoginSpec.getLinks provides?

@nickbr23 @jgrandja

I still could'nt figure out how to customize the baseUri in the reactive stack. For the servlet stack I can customize it as follows:

@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(authorize -> authorize .anyRequest().authenticated() ) .oauth2Login() .redirectionEndpoint() .baseUri("/auth/custom/sso"); }
Could you please provide a sample of the equivalent code for the reactive stack ?

@nickbr23 @jgrandja can you please provide a documentation link, thanks!

@saurabhgour @luqmanulkhair Here is a sample configuration:

@EnableWebFluxSecurity
public class SecurityConfig {

    @Autowired
    private ReactiveClientRegistrationRepository clientRegistrationRepository;

    @Bean
    SecurityWebFilterChain configure(ServerHttpSecurity http) {
        http
            .authorizeExchange(exchanges ->
                exchanges
                    .anyExchange().authenticated()
            )
            .oauth2Login(oauth2Login ->
                oauth2Login
                    .authorizationRequestResolver(getAuthorizationRequestResolver()));
        return http.build();
    }

    private ServerOAuth2AuthorizationRequestResolver getAuthorizationRequestResolver() {
        return new DefaultServerOAuth2AuthorizationRequestResolver(
                this.clientRegistrationRepository,
                new PathPatternParserServerWebExchangeMatcher(
                        "/auth/custom/sso/{registrationId}"));

    }
}

Given the above configuration, the URL http://localhost:8080/auth/custom/sso/google would trigger the authentication for Google.

@jgrandja thanks for your response, Is this code for .authoriationEndpoint().baseurl() or for .redirectionEndpoint() .baseUri(""), my problem is to handle the redirect from authorization-server after login with code, I think I need to use authenticationMatcher(), I have implemented my custom logic but it not standardized.

Example:
redirect_url: custom/login instead of {baseUrl}/login/oauth2/code/{registrationId} (does not work)
if i use {baseUrl}/login/oauth2/code/{registrationId} as a redirect uri everything works.

@luqmanulkhair The sample code provided is for .authoriationEndpoint().baseurl().

For redirectionEndpoint() .baseUri(), that is correct, you need to configure authenticationMatcher(). The default authenticationMatcher is:

new PathPatternParserServerWebExchangeMatcher("/login/oauth2/code/{registrationId}")

Configure this, with whatever path you require.

Was this page helpful?
0 / 5 - 0 ratings