Skip to content

Commit

Permalink
Don't set logoutHandler with Back-Channel Logout handler in reactive …
Browse files Browse the repository at this point in the history
…apps
  • Loading branch information
ch4mpy committed Oct 23, 2024
1 parent 0747f4d commit b7cb155
Showing 1 changed file with 99 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.util.ArrayList;
import java.util.Optional;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
Expand Down Expand Up @@ -33,7 +32,6 @@
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.server.WebFilter;

import com.c4_soft.springaddons.security.oidc.starter.ClaimSetAuthoritiesConverter;
import com.c4_soft.springaddons.security.oidc.starter.ConfigurableClaimSetAuthoritiesConverter;
import com.c4_soft.springaddons.security.oidc.starter.LogoutRequestUriBuilder;
Expand All @@ -50,109 +48,118 @@
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsNotServlet;
import com.c4_soft.springaddons.security.oidc.starter.reactive.ReactiveConfigurationSupport;
import com.c4_soft.springaddons.security.oidc.starter.reactive.ReactiveSpringAddonsOidcBeans;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

/**
* The following {@link ConditionalOnMissingBean @ConditionalOnMissingBeans} are auto-configured
* <ul>
* <li>springAddonsClientFilterChain: a {@link SecurityWebFilterChain}. Instantiated only if "com.c4-soft.springaddons.oidc.client.security-matchers" property
* has at least one entry. If defined, it is with a high precedence, to ensure that all routes defined in this security matcher property are intercepted by this
* filter-chain.</li>
* <li>logoutRequestUriBuilder: builder for <a href= "https://openid.net/specs/openid-connect-rpinitiated-1_0.html">RP-Initiated Logout</a> queries, taking
* configuration from properties for OIDC providers which do not strictly comply with the spec: logout URI not provided by OIDC conf or non standard parameter
* names (Auth0 and Cognito are samples of such OPs)</li>
* <li>logoutSuccessHandler: a {@link ServerLogoutSuccessHandler}. Default instance is a {@link SpringAddonsServerLogoutSuccessHandler} which logs a user out
* from the last authorization server he logged on</li>
* <li>authoritiesConverter: an {@link ClaimSetAuthoritiesConverter}. Default instance is a {@link ConfigurableClaimSetAuthoritiesConverter} which reads
* spring-addons {@link SpringAddonsOidcProperties}</li>
* <li>csrfCookieWebFilter: a {@link WebFilter} to set the CSRF cookie if "com.c4-soft.springaddons.oidc.client.csrf" is set to cookie</li>
* <li>clientAuthorizePostProcessor: a {@link ClientAuthorizeExchangeSpecPostProcessor} post processor to fine tune access control from java configuration. It
* applies to all routes not listed in "permit-all" property configuration. Default requires users to be authenticated.</li>
* <li>clientHttpPostProcessor: a {@link ClientReactiveHttpSecurityPostProcessor} to override anything from above auto-configuration. It is called just before
* the security filter-chain is returned. Default is a no-op.</li>
* <li>authorizationRequestResolver: a {@link ServerOAuth2AuthorizationRequestResolver} to add custom parameters (from application properties) to authorization
* code request</li>
* <li>springAddonsClientFilterChain: a {@link SecurityWebFilterChain}. Instantiated only if
* "com.c4-soft.springaddons.oidc.client.security-matchers" property has at least one entry. If
* defined, it is with a high precedence, to ensure that all routes defined in this security matcher
* property are intercepted by this filter-chain.</li>
* <li>logoutRequestUriBuilder: builder for
* <a href= "https://openid.net/specs/openid-connect-rpinitiated-1_0.html">RP-Initiated Logout</a>
* queries, taking configuration from properties for OIDC providers which do not strictly comply
* with the spec: logout URI not provided by OIDC conf or non standard parameter names (Auth0 and
* Cognito are samples of such OPs)</li>
* <li>logoutSuccessHandler: a {@link ServerLogoutSuccessHandler}. Default instance is a
* {@link SpringAddonsServerLogoutSuccessHandler} which logs a user out from the last authorization
* server he logged on</li>
* <li>authoritiesConverter: an {@link ClaimSetAuthoritiesConverter}. Default instance is a
* {@link ConfigurableClaimSetAuthoritiesConverter} which reads spring-addons
* {@link SpringAddonsOidcProperties}</li>
* <li>csrfCookieWebFilter: a {@link WebFilter} to set the CSRF cookie if
* "com.c4-soft.springaddons.oidc.client.csrf" is set to cookie</li>
* <li>clientAuthorizePostProcessor: a {@link ClientAuthorizeExchangeSpecPostProcessor} post
* processor to fine tune access control from java configuration. It applies to all routes not
* listed in "permit-all" property configuration. Default requires users to be authenticated.</li>
* <li>clientHttpPostProcessor: a {@link ClientReactiveHttpSecurityPostProcessor} to override
* anything from above auto-configuration. It is called just before the security filter-chain is
* returned. Default is a no-op.</li>
* <li>authorizationRequestResolver: a {@link ServerOAuth2AuthorizationRequestResolver} to add
* custom parameters (from application properties) to authorization code request</li>
* </ul>
*
* @author Jerome Wacongne ch4mp&#64;c4-soft.com
*/
@Conditional({ IsClientWithLoginCondition.class, IsNotServlet.class })
@Conditional({IsClientWithLoginCondition.class, IsNotServlet.class})
@EnableWebFluxSecurity
@AutoConfiguration
@ImportAutoConfiguration(ReactiveSpringAddonsOidcBeans.class)
@Slf4j
public class ReactiveSpringAddonsOidcClientWithLoginBeans {

/**
* <p>
* Instantiated only if "com.c4-soft.springaddons.oidc.client.security-matchers" property has at least one entry. If defined, it is with higher precedence
* than resource server one.
* </p>
* It defines:
* <ul>
* <li>If the path to login page was provided in conf, a &#64;Controller must be provided to handle it. Otherwise Spring Boot default generated one is
* used</li>
* <li>logout (using {@link SpringAddonsServerLogoutSuccessHandler} by default)</li>
* <li>forces SSL usage if it is enabled</li> properties</li>
* <li>CSRF protection as defined in spring-addons <b>client</b> properties (enabled by default in this filter-chain).</li>
* <li>allow access to unauthorized requests to path matchers listed in spring-security <b>client</b> "permit-all" property</li>
* <li>as usual, apply {@link ClientAuthorizeExchangeSpecPostProcessor} for access control configuration from Java conf and
* {@link ClientReactiveHttpSecurityPostProcessor} to override anything from the auto-configuration listed above</li>
* </ul>
*
* @param http the security filter-chain builder to configure
* @param serverProperties Spring Boot standard server properties
* @param authorizationRequestResolver the authorization request resolver to use. By default {@link ServerOAuth2AuthorizationRequestResolver} (adds
* authorization request parameters defined in properties and builds absolutes callback URI). By default, a
* {@link SpringAddonsServerOAuth2AuthorizationRequestResolver} is used
* @param preAuthorizationCodeRedirectStrategy the redirection strategy to use for authorization-code request
* @param authenticationSuccessHandler the authentication success handler to use. By default, a {@link SpringAddonsOauth2ServerAuthenticationSuccessHandler}
* is used.
* @param authenticationFailureHandler the authentication failure handler to use. By default, a {@link SpringAddonsOauth2ServerAuthenticationFailureHandler}
* is used.
* @param logoutSuccessHandler Defaulted to {@link SpringAddonsServerLogoutSuccessHandler} which can handle "almost" RP Initiated Logout conformant OPs
* (like Auth0 and Cognito)
* @param addonsProperties {@link SpringAddonsOAuth2ClientProperties spring-addons client properties}
* @param authorizePostProcessor post process authorization after "permit-all" configuration was applied (default is "isAuthenticated()" to everything that
* was not matched)
* @param httpPostProcessor post process the "http" builder just before it is returned (enables to override anything from the auto-configuration)
* spring-addons client properties}
* @param oidcBackChannelLogoutHandler if present, Back-Channel Logout is enabled. A default {@link OidcBackChannelServerLogoutHandler} is provided if
* com.c4-soft.springaddons.oidc.client.back-channel-logout.enabled is true
* @return a security filter-chain scoped to specified security-matchers and adapted to OAuth2 clients
* @throws Exception in case of miss-configuration
*/
@Order(Ordered.LOWEST_PRECEDENCE - 1)
@Bean
SecurityWebFilterChain clientFilterChain(
ServerHttpSecurity http,
ServerProperties serverProperties,
SpringAddonsOidcProperties addonsProperties,
ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver,
PreAuthorizationCodeServerRedirectStrategy preAuthorizationCodeRedirectStrategy,
ServerAuthenticationEntryPoint authenticationEntryPoint,
ServerAuthenticationSuccessHandler authenticationSuccessHandler,
ServerAuthenticationFailureHandler authenticationFailureHandler,
ServerLogoutSuccessHandler logoutSuccessHandler,
ClientAuthorizeExchangeSpecPostProcessor authorizePostProcessor,
ClientReactiveHttpSecurityPostProcessor httpPostProcessor,
Optional<ServerLogoutHandler> logoutHandler,
Optional<OidcBackChannelServerLogoutHandler> oidcBackChannelLogoutHandler)
throws Exception {
/**
* <p>
* Instantiated only if "com.c4-soft.springaddons.oidc.client.security-matchers" property has at
* least one entry. If defined, it is with higher precedence than resource server one.
* </p>
* It defines:
* <ul>
* <li>If the path to login page was provided in conf, a &#64;Controller must be provided to
* handle it. Otherwise Spring Boot default generated one is used</li>
* <li>logout (using {@link SpringAddonsServerLogoutSuccessHandler} by default)</li>
* <li>forces SSL usage if it is enabled</li> properties</li>
* <li>CSRF protection as defined in spring-addons <b>client</b> properties (enabled by default in
* this filter-chain).</li>
* <li>allow access to unauthorized requests to path matchers listed in spring-security
* <b>client</b> "permit-all" property</li>
* <li>as usual, apply {@link ClientAuthorizeExchangeSpecPostProcessor} for access control
* configuration from Java conf and {@link ClientReactiveHttpSecurityPostProcessor} to override
* anything from the auto-configuration listed above</li>
* </ul>
*
* @param http the security filter-chain builder to configure
* @param serverProperties Spring Boot standard server properties
* @param authorizationRequestResolver the authorization request resolver to use. By default
* {@link ServerOAuth2AuthorizationRequestResolver} (adds authorization request parameters
* defined in properties and builds absolutes callback URI). By default, a
* {@link SpringAddonsServerOAuth2AuthorizationRequestResolver} is used
* @param preAuthorizationCodeRedirectStrategy the redirection strategy to use for
* authorization-code request
* @param authenticationSuccessHandler the authentication success handler to use. By default, a
* {@link SpringAddonsOauth2ServerAuthenticationSuccessHandler} is used.
* @param authenticationFailureHandler the authentication failure handler to use. By default, a
* {@link SpringAddonsOauth2ServerAuthenticationFailureHandler} is used.
* @param logoutSuccessHandler Defaulted to {@link SpringAddonsServerLogoutSuccessHandler} which
* can handle "almost" RP Initiated Logout conformant OPs (like Auth0 and Cognito)
* @param addonsProperties {@link SpringAddonsOAuth2ClientProperties spring-addons client
* properties}
* @param authorizePostProcessor post process authorization after "permit-all" configuration was
* applied (default is "isAuthenticated()" to everything that was not matched)
* @param httpPostProcessor post process the "http" builder just before it is returned (enables to
* override anything from the auto-configuration) spring-addons client properties}
* @param oidcBackChannelLogoutHandler if present, Back-Channel Logout is enabled. A default
* {@link OidcBackChannelServerLogoutHandler} is provided if
* com.c4-soft.springaddons.oidc.client.back-channel-logout.enabled is true
* @return a security filter-chain scoped to specified security-matchers and adapted to OAuth2
* clients
* @throws Exception in case of miss-configuration
*/
@Order(Ordered.LOWEST_PRECEDENCE - 1)
@Bean
SecurityWebFilterChain clientFilterChain(ServerHttpSecurity http,
ServerProperties serverProperties, SpringAddonsOidcProperties addonsProperties,
ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver,
PreAuthorizationCodeServerRedirectStrategy preAuthorizationCodeRedirectStrategy,
ServerAuthenticationEntryPoint authenticationEntryPoint,
ServerAuthenticationSuccessHandler authenticationSuccessHandler,
ServerAuthenticationFailureHandler authenticationFailureHandler,
ServerLogoutSuccessHandler logoutSuccessHandler,
ClientAuthorizeExchangeSpecPostProcessor authorizePostProcessor,
ClientReactiveHttpSecurityPostProcessor httpPostProcessor,
Optional<ServerLogoutHandler> logoutHandler,
Optional<OidcBackChannelServerLogoutHandler> oidcBackChannelLogoutHandler) throws Exception {

final var clientRoutes = addonsProperties
.getClient()
.getSecurityMatchers()
.stream()
.map(PathPatternParserServerWebExchangeMatcher::new)
.map(ServerWebExchangeMatcher.class::cast)
.toList();
log.info("Applying client OAuth2 configuration for: {}", addonsProperties.getClient().getSecurityMatchers());
http.securityMatcher(new OrServerWebExchangeMatcher(clientRoutes));
final var clientRoutes = addonsProperties.getClient().getSecurityMatchers().stream()
.map(PathPatternParserServerWebExchangeMatcher::new)
.map(ServerWebExchangeMatcher.class::cast).toList();
log.info("Applying client OAuth2 configuration for: {}",
addonsProperties.getClient().getSecurityMatchers());
http.securityMatcher(new OrServerWebExchangeMatcher(clientRoutes));

// @formatter:off
// @formatter:off
http.exceptionHandling(exceptions -> {
exceptions.authenticationEntryPoint(authenticationEntryPoint);
});
Expand All @@ -165,7 +172,11 @@ SecurityWebFilterChain clientFilterChain(
});

http.logout((logout) -> {
logoutHandler.ifPresent(logout::logoutHandler);
logoutHandler.ifPresent(handler -> {
if(!(handler instanceof OidcBackChannelServerLogoutHandler)) {
logout.logoutHandler(handler);
}
});
logout.logoutSuccessHandler(logoutSuccessHandler);
});

Expand Down

0 comments on commit b7cb155

Please sign in to comment.