diff --git a/samples/webflux-jwt-oauthentication/src/test/java/com/c4_soft/springaddons/samples/webflux_oidcauthentication/MessageServiceTests.java b/samples/webflux-jwt-oauthentication/src/test/java/com/c4_soft/springaddons/samples/webflux_oidcauthentication/MessageServiceTests.java
index c2423ff62..7c9d5cbee 100644
--- a/samples/webflux-jwt-oauthentication/src/test/java/com/c4_soft/springaddons/samples/webflux_oidcauthentication/MessageServiceTests.java
+++ b/samples/webflux-jwt-oauthentication/src/test/java/com/c4_soft/springaddons/samples/webflux_oidcauthentication/MessageServiceTests.java
@@ -42,8 +42,10 @@
import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties;
import com.c4_soft.springaddons.security.oidc.starter.reactive.ReactiveSpringAddonsOidcBeans;
import com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientBeans;
+import com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientWithLoginBeans;
import com.c4_soft.springaddons.security.oidc.starter.reactive.resourceserver.ReactiveSpringAddonsOidcResourceServerBeans;
import com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsOidcClientBeans;
+import com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsOidcClientWithLoginBeans;
import com.c4_soft.springaddons.security.oidc.starter.synchronised.resourceserver.SpringAddonsOidcResourceServerBeans;
import reactor.core.publisher.Mono;
@@ -59,8 +61,10 @@
exclude = {
ReactiveSpringAddonsOidcResourceServerBeans.class,
ReactiveSpringAddonsOidcClientBeans.class,
+ ReactiveSpringAddonsOidcClientWithLoginBeans.class,
SpringAddonsOidcResourceServerBeans.class,
- SpringAddonsOidcClientBeans.class })
+ SpringAddonsOidcClientBeans.class,
+ SpringAddonsOidcClientWithLoginBeans.class })
@SpringBootTest(classes = { WebfluxJwtOauthentication.class, MessageService.class })
@Import({ AddonsWebfluxTestConf.class })
@ImportAutoConfiguration({ SpringAddonsOidcProperties.class, ReactiveSpringAddonsOidcBeans.class, AuthenticationFactoriesTestConf.class })
diff --git a/spring-addons-starter-oidc-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/webflux/AutoConfigureAddonsWebfluxClientSecurity.java b/spring-addons-starter-oidc-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/webflux/AutoConfigureAddonsWebfluxClientSecurity.java
index 3b4d22451..7233d866d 100644
--- a/spring-addons-starter-oidc-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/webflux/AutoConfigureAddonsWebfluxClientSecurity.java
+++ b/spring-addons-starter-oidc-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/webflux/AutoConfigureAddonsWebfluxClientSecurity.java
@@ -10,7 +10,7 @@
import com.c4_soft.springaddons.security.oauth2.test.webmvc.AddonsWebmvcComponentTest;
import com.c4_soft.springaddons.security.oauth2.test.webmvc.AutoConfigureAddonsWebmvcMinimalSecurity;
import com.c4_soft.springaddons.security.oauth2.test.webmvc.AutoConfigureAddonsWebmvcResourceServerSecurity;
-import com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientBeans;
+import com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientWithLoginBeans;
import com.c4_soft.springaddons.security.oidc.starter.reactive.resourceserver.ReactiveSpringAddonsOidcResourceServerBeans;
/**
@@ -27,6 +27,6 @@
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@AutoConfigureAddonsWebfluxMinimalSecurity
-@ImportAutoConfiguration({ ReactiveSpringAddonsOidcClientBeans.class, AddonsWebfluxTestConf.class })
+@ImportAutoConfiguration({ ReactiveSpringAddonsOidcClientWithLoginBeans.class, AddonsWebfluxTestConf.class })
public @interface AutoConfigureAddonsWebfluxClientSecurity {
}
diff --git a/spring-addons-starter-oidc-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/webmvc/AutoConfigureAddonsWebmvcClientSecurity.java b/spring-addons-starter-oidc-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/webmvc/AutoConfigureAddonsWebmvcClientSecurity.java
index 7c6fe9691..657db481c 100644
--- a/spring-addons-starter-oidc-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/webmvc/AutoConfigureAddonsWebmvcClientSecurity.java
+++ b/spring-addons-starter-oidc-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/webmvc/AutoConfigureAddonsWebmvcClientSecurity.java
@@ -8,11 +8,11 @@
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import com.c4_soft.springaddons.security.oauth2.test.webflux.AutoConfigureAddonsWebfluxClientSecurity;
-import com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsOidcClientBeans;
+import com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsOidcClientWithLoginBeans;
/**
*
- * Auto-configures {@link SpringAddonsOidcClientBeans} as well as what is already configured by {@link AutoConfigureAddonsWebmvcMinimalSecurity}. To be used to
+ * Auto-configures {@link SpringAddonsOidcClientWithLoginBeans} as well as what is already configured by {@link AutoConfigureAddonsWebmvcMinimalSecurity}. To be used to
* test controllers but not services or repositories (web context is not desired in that case).
*
*
@@ -23,6 +23,6 @@
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@AutoConfigureAddonsWebmvcMinimalSecurity
-@ImportAutoConfiguration({ SpringAddonsOidcClientBeans.class })
+@ImportAutoConfiguration({ SpringAddonsOidcClientWithLoginBeans.class })
public @interface AutoConfigureAddonsWebmvcClientSecurity {
}
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/SpringAddonsOidcClientProperties.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/SpringAddonsOidcClientProperties.java
index b2ca94d94..1302567f5 100644
--- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/SpringAddonsOidcClientProperties.java
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/SpringAddonsOidcClientProperties.java
@@ -163,6 +163,11 @@ public URI getPostLogoutRedirectUri() {
*/
private Map> authorizationRequestParams = new HashMap<>();
+ /**
+ * Additional parameters to send with token request, mapped by client registration IDs
+ */
+ private Map> tokenRequestParams = new HashMap<>();
+
/**
* Logout properties for OpenID Providers which do not implement the RP-Initiated Logout spec
*
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/condition/bean/DefaultGrantedAuthoritiesMapperCondition.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/condition/bean/DefaultGrantedAuthoritiesMapperCondition.java
index 38c125b31..3a2d79359 100644
--- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/condition/bean/DefaultGrantedAuthoritiesMapperCondition.java
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/condition/bean/DefaultGrantedAuthoritiesMapperCondition.java
@@ -5,14 +5,14 @@
import org.springframework.context.annotation.Conditional;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
-import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsOidcClientCondition;
+import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientWithLoginCondition;
public class DefaultGrantedAuthoritiesMapperCondition extends AllNestedConditions {
DefaultGrantedAuthoritiesMapperCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
- @Conditional(IsOidcClientCondition.class)
+ @Conditional(IsClientWithLoginCondition.class)
static class SpringAddonsOidcClientEnabled {
}
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/condition/configuration/IsOidcClientCondition.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/condition/configuration/IsClientWithLoginCondition.java
similarity index 88%
rename from spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/condition/configuration/IsOidcClientCondition.java
rename to spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/condition/configuration/IsClientWithLoginCondition.java
index 729532918..f25445efb 100644
--- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/condition/configuration/IsOidcClientCondition.java
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/condition/configuration/IsClientWithLoginCondition.java
@@ -4,9 +4,9 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-public class IsOidcClientCondition extends AnyNestedCondition {
+public class IsClientWithLoginCondition extends AnyNestedCondition {
- public IsOidcClientCondition() {
+ public IsClientWithLoginCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/PerRegistrationReactiveOAuth2AuthorizedClientProvider.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/PerRegistrationReactiveOAuth2AuthorizedClientProvider.java
new file mode 100644
index 000000000..fa7e6098b
--- /dev/null
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/PerRegistrationReactiveOAuth2AuthorizedClientProvider.java
@@ -0,0 +1,125 @@
+package com.c4_soft.springaddons.security.oidc.starter.reactive.client;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.StreamSupport;
+
+import org.springframework.security.oauth2.client.AuthorizationCodeReactiveOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.ClientCredentialsReactiveOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.DelegatingReactiveOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.OAuth2AuthorizationContext;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.RefreshTokenReactiveOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.endpoint.WebClientReactiveClientCredentialsTokenResponseClient;
+import org.springframework.security.oauth2.client.endpoint.WebClientReactiveRefreshTokenTokenResponseClient;
+import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.util.Assert;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties;
+
+import reactor.core.publisher.Mono;
+
+/**
+ *
+ * An alternative {@link ReactiveOAuth2AuthorizedClientProvider} to {@link DelegatingReactiveOAuth2AuthorizedClientProvider} keeping a different provider for
+ * each client registration. This allows to define for each a set of extra parameters to add to token requests.
+ *
+ *
+ * @author Jerome Wacongne ch4mp@c4-soft.com
+ */
+public final class PerRegistrationReactiveOAuth2AuthorizedClientProvider implements ReactiveOAuth2AuthorizedClientProvider {
+
+ private final Map providers;
+
+ public PerRegistrationReactiveOAuth2AuthorizedClientProvider(
+ SpringAddonsOidcProperties addonsProperties,
+ Map providers) {
+ this.providers = new HashMap<>(providers);
+ }
+
+ public PerRegistrationReactiveOAuth2AuthorizedClientProvider(
+ InMemoryReactiveClientRegistrationRepository clientRegistrationRepo,
+ SpringAddonsOidcProperties addonsProperties,
+ Map customProviders) {
+ this.providers = new HashMap<>(customProviders);
+ StreamSupport.stream(clientRegistrationRepo.spliterator(), false).forEach(reg -> {
+ if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(reg.getAuthorizationGrantType())) {
+ this.providers.putIfAbsent(reg.getRegistrationId(), new AuthorizationCodeReactiveOAuth2AuthorizedClientProvider());
+ } else if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(reg.getAuthorizationGrantType())) {
+ this.providers.putIfAbsent(reg.getRegistrationId(), new ClientCredentialsReactiveOAuth2AuthorizedClientProvider());
+ } else if (AuthorizationGrantType.REFRESH_TOKEN.equals(reg.getAuthorizationGrantType())) {
+ this.providers.putIfAbsent(reg.getRegistrationId(), new RefreshTokenReactiveOAuth2AuthorizedClientProvider());
+ } else {
+ throw new UnsupportedGrantTypeException(reg.getAuthorizationGrantType());
+ }
+
+ final var tokenParams = addonsProperties.getClient().getTokenRequestParams().getOrDefault(reg.getRegistrationId(), List.of());
+ if (tokenParams.isEmpty()) {
+ return;
+ }
+ final MultiValueMap extraParameters = new LinkedMultiValueMap<>(tokenParams.size());
+ for (final var param : tokenParams) {
+ extraParameters.add(param.getName(), param.getValue());
+ }
+
+ final var delegate = this.providers.get(reg.getRegistrationId());
+ if (delegate instanceof ClientCredentialsReactiveOAuth2AuthorizedClientProvider clientCredentialsProvider) {
+ final var clientCredentialsResponseClient = new WebClientReactiveClientCredentialsTokenResponseClient();
+ clientCredentialsResponseClient.addParametersConverter(source -> extraParameters);
+
+ clientCredentialsProvider.setAccessTokenResponseClient(clientCredentialsResponseClient);
+
+ } else if (delegate instanceof RefreshTokenReactiveOAuth2AuthorizedClientProvider refreshTokenProvider) {
+ final var refreshTokenResponseClient = new WebClientReactiveRefreshTokenTokenResponseClient();
+ refreshTokenResponseClient.addParametersConverter(source -> extraParameters);
+
+ refreshTokenProvider.setAccessTokenResponseClient(refreshTokenResponseClient);
+ }
+ });
+ }
+
+ public PerRegistrationReactiveOAuth2AuthorizedClientProvider(
+ InMemoryReactiveClientRegistrationRepository clientRegistrationRepo,
+ SpringAddonsOidcProperties addonsProperties) {
+ this(clientRegistrationRepo, addonsProperties, Map.of());
+ }
+
+ @Override
+ public Mono authorize(OAuth2AuthorizationContext context) throws UnsupportedGrantTypeException {
+ if (context == null) {
+ return null;
+ }
+
+ final var provider = getDelegate(context.getClientRegistration().getRegistrationId());
+
+ return provider.authorize(context);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getDelegate(String registrationId) throws UnsupportedGrantTypeException {
+ final var provider = providers.get(registrationId);
+ return (T) provider;
+ }
+
+ public PerRegistrationReactiveOAuth2AuthorizedClientProvider setDelegate(String registrationId, ReactiveOAuth2AuthorizedClientProvider delegate) {
+ Assert.notNull(registrationId, "registrationId cannot be null");
+ Assert.notNull(delegate, "delegate cannot be null");
+ providers.put(registrationId, delegate);
+ return this;
+ }
+
+ static class UnsupportedGrantTypeException extends RuntimeException {
+ private static final long serialVersionUID = 5600617070203595919L;
+
+ public UnsupportedGrantTypeException(AuthorizationGrantType grantType) {
+ super(
+ "No OAuth2AuthorizedClientProvider registered for GrantType: %s. Consider adding one to SpringAddonsDelegatingOAuth2AuthorizedClientProvider in your conf."
+ .formatted(grantType));
+ }
+ }
+}
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsAop.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsAop.java
index 01fd22f24..ceeb1ed51 100644
--- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsAop.java
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsAop.java
@@ -21,12 +21,12 @@
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientMultiTenancyEnabled;
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsNotServlet;
-import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsOidcClientCondition;
+import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientWithLoginCondition;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Mono;
-@Conditional({ IsOidcClientCondition.class, IsNotServlet.class, IsClientMultiTenancyEnabled.class })
+@Conditional({ IsClientWithLoginCondition.class, IsNotServlet.class, IsClientMultiTenancyEnabled.class })
@AutoConfiguration
@PropertySource(value = "classpath:/c4-spring-addons.properties", ignoreResourceNotFound = true)
public class ReactiveSpringAddonsAop {
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsOidcClientBeans.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsOidcClientBeans.java
index f5737108a..10c84c490 100644
--- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsOidcClientBeans.java
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsOidcClientBeans.java
@@ -1,297 +1,41 @@
package com.c4_soft.springaddons.security.oidc.starter.reactive.client;
-import java.util.Optional;
-
import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Conditional;
-import org.springframework.core.Ordered;
-import org.springframework.core.annotation.Order;
-import org.springframework.http.HttpStatus;
-import org.springframework.security.config.Customizer;
-import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
-import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;
+import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
-import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver;
-import org.springframework.security.web.server.SecurityWebFilterChain;
-import org.springframework.security.web.server.ServerRedirectStrategy;
-import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
-import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
-import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
-import org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler;
-import org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler;
-import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;
-import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
-import org.springframework.security.web.server.authentication.logout.WebSessionServerLogoutHandler;
-import org.springframework.security.web.server.csrf.CsrfToken;
-import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
-import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
-import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
-import org.springframework.web.server.WebFilter;
-import org.springframework.web.util.UriComponentsBuilder;
+import org.springframework.security.oauth2.client.web.DefaultReactiveOAuth2AuthorizedClientManager;
+import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
-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;
-import com.c4_soft.springaddons.security.oidc.starter.SpringAddonsOAuth2LogoutRequestUriBuilder;
import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties;
-import com.c4_soft.springaddons.security.oidc.starter.properties.condition.bean.CookieCsrfCondition;
-import com.c4_soft.springaddons.security.oidc.starter.properties.condition.bean.DefaultAuthenticationSuccessHandlerCondition;
-import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsNotServlet;
-import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsOidcClientCondition;
-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
- *
- * - 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.
- * - logoutRequestUriBuilder: builder for RP-Initiated Logout 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)
- * - logoutSuccessHandler: a {@link ServerLogoutSuccessHandler}. Default instance is a {@link SpringAddonsServerLogoutSuccessHandler} which logs a user out
- * from the last authorization server he logged on
- * - authoritiesConverter: an {@link ClaimSetAuthoritiesConverter}. Default instance is a {@link ConfigurableClaimSetAuthoritiesConverter} which reads
- * spring-addons {@link SpringAddonsOidcProperties}
- * - csrfCookieWebFilter: a {@link WebFilter} to set the CSRF cookie if "com.c4-soft.springaddons.oidc.client.csrf" is set to cookie
- * - 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.
- * - clientHttpPostProcessor: a {@link ClientHttpSecurityPostProcessor} to override anything from above auto-configuration. It is called just before the
- * security filter-chain is returned. Default is a no-op.
- * - authorizationRequestResolver: a {@link ServerOAuth2AuthorizationRequestResolver} to add custom parameters (from application properties) to authorization
- * code request
- *
- *
- * @author Jerome Wacongne ch4mp@c4-soft.com
- */
-@Conditional({ IsOidcClientCondition.class, IsNotServlet.class })
-@EnableWebFluxSecurity
+@ConditionalOnClass(ReactiveOAuth2AuthorizedClientManager.class)
@AutoConfiguration
-@ImportAutoConfiguration(ReactiveSpringAddonsOidcBeans.class)
-@Slf4j
public class ReactiveSpringAddonsOidcClientBeans {
- /**
- *
- * 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.
- *
- * It defines:
- *
- * - If the path to login page was provided in conf, a @Controller must be provided to handle it. Otherwise Spring Boot default generated one is
- * used
- * - logout (using {@link SpringAddonsServerLogoutSuccessHandler} by default)
- * - forces SSL usage if it is enabled
properties
- * - CSRF protection as defined in spring-addons client properties (enabled by default in this filter-chain).
- * - allow access to unauthorized requests to path matchers listed in spring-security client "permit-all" property
- * - as usual, apply {@link ClientAuthorizeExchangeSpecPostProcessor} for access control configuration from Java conf and
- * {@link ClientHttpSecurityPostProcessor} to override anything from the auto-configuration listed above
- *
- *
- * @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 oidcLogoutCustomizer a configurer for Spring Security Back-Channel Logout implementation
- * @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,
- Optional authenticationSuccessHandler,
- Optional authenticationFailureHandler,
- ServerLogoutSuccessHandler logoutSuccessHandler,
- ClientAuthorizeExchangeSpecPostProcessor authorizePostProcessor,
- ClientHttpSecurityPostProcessor httpPostProcessor,
- ServerLogoutHandler logoutHandler,
- Customizer oidcLogoutCustomizer)
- 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));
-
- // @formatter:off
- addonsProperties.getClient().getLoginPath().ifPresent(loginPath -> {
- http.exceptionHandling(exceptionHandling -> exceptionHandling
- .authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint(UriComponentsBuilder.fromUri(addonsProperties.getClient().getClientUri()).path(loginPath).build().toString())));
- });
-
- http.oauth2Login(oauth2 -> {
- oauth2.authorizationRequestResolver(authorizationRequestResolver);
- oauth2.authorizationRedirectStrategy(preAuthorizationCodeRedirectStrategy);
- authenticationSuccessHandler.ifPresent(oauth2::authenticationSuccessHandler);
- authenticationFailureHandler.ifPresent(oauth2::authenticationFailureHandler);
- });
-
- http.logout((logout) -> {
- logout.logoutHandler(logoutHandler);
- logout.logoutSuccessHandler(logoutSuccessHandler);
- });
-
- if(addonsProperties.getClient().getBackChannelLogout().isEnabled()) {
- http.oidcLogout(oidcLogoutCustomizer);
- }
-
- ReactiveConfigurationSupport.configureClient(http, serverProperties, addonsProperties.getClient(), authorizePostProcessor, httpPostProcessor);
-
- return http.build();
- }
-
- /**
- * Build logout request for RP-Initiated
- * Logout. It works with most OIDC provider: those complying with the spec
- * (Keycloak for instance), off course, but also those which are close enough to
- * it (Auth0, Cognito, ...)
- *
- * @param addonsProperties {@link SpringAddonsOAuth2ClientProperties} to pick logout
- * configuration for divergence to the standard (logout URI
- * not provided in .well-known/openid-configuration and
- * non-conform parameter names)
- * @return {@link SpringAddonsOAuth2LogoutRequestUriBuilder]
- */
- @ConditionalOnMissingBean
- @Bean
- LogoutRequestUriBuilder logoutRequestUriBuilder(SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsOAuth2LogoutRequestUriBuilder(addonsProperties.getClient());
- }
-
- /**
- * Single tenant logout handler for OIDC provider complying to RP-Initiated
- * Logout (or approximately complying to it like Auth0 or Cognito)
- *
- * @param logoutRequestUriBuilder delegate doing the smart job
- * @param clientRegistrationRepository
- * @return {@link SpringAddonsServerLogoutSuccessHandler}
- */
- @ConditionalOnMissingBean
- @Bean
- ServerLogoutSuccessHandler logoutSuccessHandler(LogoutRequestUriBuilder logoutUriBuilder,
- ReactiveClientRegistrationRepository clientRegistrationRepo, SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsServerLogoutSuccessHandler(logoutUriBuilder, clientRegistrationRepo, addonsProperties);
- }
-
- /**
- * Hook to override security rules for all path that are not listed in
- * "permit-all". Default is isAuthenticated().
- *
- * @return a hook to override security rules for all path that are not listed in
- * "permit-all". Default is isAuthenticated().
- */
- @ConditionalOnMissingBean
- @Bean
- ClientAuthorizeExchangeSpecPostProcessor clientAuthorizePostProcessor() {
- return (ServerHttpSecurity.AuthorizeExchangeSpec spec) -> spec.anyExchange().authenticated();
- }
-
- /**
- * Hook to override all or part of HttpSecurity auto-configuration.
- * Called after spring-addons configuration was applied so that you can
- * modify anything
- *
- * @return a hook to override all or part of HttpSecurity auto-configuration.
- * Called after spring-addons configuration was applied so that you can
- * modify anything
- */
@ConditionalOnMissingBean
@Bean
- ClientHttpSecurityPostProcessor clientHttpPostProcessor() {
- return serverHttpSecurity -> serverHttpSecurity;
- }
-
- /**
- * https://docs.spring.io/spring-security/reference/5.8/migration/reactive.html#_i_am_using_angularjs_or_another_javascript_framework
- */
- @Conditional(CookieCsrfCondition.class)
- @ConditionalOnMissingBean(name = "csrfCookieWebFilter")
- @Bean
- WebFilter csrfCookieWebFilter() {
- return (exchange, chain) -> {
- exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty()).subscribe();
- return chain.filter(exchange);
- };
- }
+ ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
+ ReactiveClientRegistrationRepository clientRegistrationRepository,
+ ServerOAuth2AuthorizedClientRepository authorizedClientRepository,
+ ReactiveOAuth2AuthorizedClientProvider oauth2AuthorizedClientProvider) {
- @ConditionalOnMissingBean
- @Bean
- ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver(ReactiveClientRegistrationRepository clientRegistrationRepository, SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsServerOAuth2AuthorizationRequestResolver(clientRegistrationRepository, addonsProperties.getClient());
- }
+ final var authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
+ authorizedClientManager.setAuthorizedClientProvider(oauth2AuthorizedClientProvider);
- @ConditionalOnMissingBean
- @Bean
- ServerLogoutHandler logoutHandler() {
- return new DelegatingServerLogoutHandler(
- new WebSessionServerLogoutHandler(),
- new SecurityContextServerLogoutHandler());
+ return authorizedClientManager;
}
@ConditionalOnMissingBean
@Bean
- PreAuthorizationCodeServerRedirectStrategy preAuthorizationCodeRedirectStrategy(SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsPreAuthorizationCodeServerRedirectStrategy(
- addonsProperties.getClient().getOauth2Redirections().getPreAuthorizationCode());
- }
-
- @Conditional(DefaultAuthenticationSuccessHandlerCondition.class)
- @Bean
- ServerAuthenticationSuccessHandler authenticationSuccessHandler(SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsOauth2ServerAuthenticationSuccessHandler(addonsProperties);
- }
-
- @Conditional(DefaultAuthenticationSuccessHandlerCondition.class)
- @Bean
- ServerAuthenticationFailureHandler authenticationFailureHandler(SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsOauth2ServerAuthenticationFailureHandler(addonsProperties);
- }
-
- static interface PreAuthorizationCodeServerRedirectStrategy extends ServerRedirectStrategy {}
-
- public static class SpringAddonsPreAuthorizationCodeServerRedirectStrategy extends SpringAddonsOauth2ServerRedirectStrategy implements PreAuthorizationCodeServerRedirectStrategy {
- public SpringAddonsPreAuthorizationCodeServerRedirectStrategy(HttpStatus defaultStatus) {
- super(defaultStatus);
- }
-
+ ReactiveOAuth2AuthorizedClientProvider oauth2AuthorizedClientProvider(
+ SpringAddonsOidcProperties addonsProperties,
+ InMemoryReactiveClientRegistrationRepository clientRegistrationRepository) {
+ return new PerRegistrationReactiveOAuth2AuthorizedClientProvider(clientRegistrationRepository, addonsProperties);
}
- @ConditionalOnMissingBean
- @Bean
- Customizer oidcLogoutSpec() {
- return Customizer.withDefaults();
- }
-}
\ No newline at end of file
+}
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsOidcClientWithLoginBeans.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsOidcClientWithLoginBeans.java
new file mode 100644
index 000000000..d7eb520ed
--- /dev/null
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsOidcClientWithLoginBeans.java
@@ -0,0 +1,297 @@
+package com.c4_soft.springaddons.security.oidc.starter.reactive.client;
+
+import java.util.Optional;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
+import org.springframework.security.config.web.server.ServerHttpSecurity;
+import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
+import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizationRequestResolver;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import org.springframework.security.web.server.ServerRedirectStrategy;
+import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
+import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
+import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
+import org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler;
+import org.springframework.security.web.server.authentication.logout.SecurityContextServerLogoutHandler;
+import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;
+import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
+import org.springframework.security.web.server.authentication.logout.WebSessionServerLogoutHandler;
+import org.springframework.security.web.server.csrf.CsrfToken;
+import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
+import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
+import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.util.UriComponentsBuilder;
+
+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;
+import com.c4_soft.springaddons.security.oidc.starter.SpringAddonsOAuth2LogoutRequestUriBuilder;
+import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties;
+import com.c4_soft.springaddons.security.oidc.starter.properties.condition.bean.CookieCsrfCondition;
+import com.c4_soft.springaddons.security.oidc.starter.properties.condition.bean.DefaultAuthenticationSuccessHandlerCondition;
+import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsNotServlet;
+import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientWithLoginCondition;
+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
+ *
+ * - 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.
+ * - logoutRequestUriBuilder: builder for RP-Initiated Logout 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)
+ * - logoutSuccessHandler: a {@link ServerLogoutSuccessHandler}. Default instance is a {@link SpringAddonsServerLogoutSuccessHandler} which logs a user out
+ * from the last authorization server he logged on
+ * - authoritiesConverter: an {@link ClaimSetAuthoritiesConverter}. Default instance is a {@link ConfigurableClaimSetAuthoritiesConverter} which reads
+ * spring-addons {@link SpringAddonsOidcProperties}
+ * - csrfCookieWebFilter: a {@link WebFilter} to set the CSRF cookie if "com.c4-soft.springaddons.oidc.client.csrf" is set to cookie
+ * - 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.
+ * - clientHttpPostProcessor: a {@link ClientHttpSecurityPostProcessor} to override anything from above auto-configuration. It is called just before the
+ * security filter-chain is returned. Default is a no-op.
+ * - authorizationRequestResolver: a {@link ServerOAuth2AuthorizationRequestResolver} to add custom parameters (from application properties) to authorization
+ * code request
+ *
+ *
+ * @author Jerome Wacongne ch4mp@c4-soft.com
+ */
+@Conditional({ IsClientWithLoginCondition.class, IsNotServlet.class })
+@EnableWebFluxSecurity
+@AutoConfiguration
+@ImportAutoConfiguration(ReactiveSpringAddonsOidcBeans.class)
+@Slf4j
+public class ReactiveSpringAddonsOidcClientWithLoginBeans {
+
+ /**
+ *
+ * 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.
+ *
+ * It defines:
+ *
+ * - If the path to login page was provided in conf, a @Controller must be provided to handle it. Otherwise Spring Boot default generated one is
+ * used
+ * - logout (using {@link SpringAddonsServerLogoutSuccessHandler} by default)
+ * - forces SSL usage if it is enabled
properties
+ * - CSRF protection as defined in spring-addons client properties (enabled by default in this filter-chain).
+ * - allow access to unauthorized requests to path matchers listed in spring-security client "permit-all" property
+ * - as usual, apply {@link ClientAuthorizeExchangeSpecPostProcessor} for access control configuration from Java conf and
+ * {@link ClientHttpSecurityPostProcessor} to override anything from the auto-configuration listed above
+ *
+ *
+ * @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 oidcLogoutCustomizer a configurer for Spring Security Back-Channel Logout implementation
+ * @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,
+ Optional authenticationSuccessHandler,
+ Optional authenticationFailureHandler,
+ ServerLogoutSuccessHandler logoutSuccessHandler,
+ ClientAuthorizeExchangeSpecPostProcessor authorizePostProcessor,
+ ClientHttpSecurityPostProcessor httpPostProcessor,
+ ServerLogoutHandler logoutHandler,
+ Customizer oidcLogoutCustomizer)
+ 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));
+
+ // @formatter:off
+ addonsProperties.getClient().getLoginPath().ifPresent(loginPath -> {
+ http.exceptionHandling(exceptionHandling -> exceptionHandling
+ .authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint(UriComponentsBuilder.fromUri(addonsProperties.getClient().getClientUri()).path(loginPath).build().toString())));
+ });
+
+ http.oauth2Login(oauth2 -> {
+ oauth2.authorizationRequestResolver(authorizationRequestResolver);
+ oauth2.authorizationRedirectStrategy(preAuthorizationCodeRedirectStrategy);
+ authenticationSuccessHandler.ifPresent(oauth2::authenticationSuccessHandler);
+ authenticationFailureHandler.ifPresent(oauth2::authenticationFailureHandler);
+ });
+
+ http.logout((logout) -> {
+ logout.logoutHandler(logoutHandler);
+ logout.logoutSuccessHandler(logoutSuccessHandler);
+ });
+
+ if(addonsProperties.getClient().getBackChannelLogout().isEnabled()) {
+ http.oidcLogout(oidcLogoutCustomizer);
+ }
+
+ ReactiveConfigurationSupport.configureClient(http, serverProperties, addonsProperties.getClient(), authorizePostProcessor, httpPostProcessor);
+
+ return http.build();
+ }
+
+ /**
+ * Build logout request for RP-Initiated
+ * Logout. It works with most OIDC provider: those complying with the spec
+ * (Keycloak for instance), off course, but also those which are close enough to
+ * it (Auth0, Cognito, ...)
+ *
+ * @param addonsProperties {@link SpringAddonsOAuth2ClientProperties} to pick logout
+ * configuration for divergence to the standard (logout URI
+ * not provided in .well-known/openid-configuration and
+ * non-conform parameter names)
+ * @return {@link SpringAddonsOAuth2LogoutRequestUriBuilder]
+ */
+ @ConditionalOnMissingBean
+ @Bean
+ LogoutRequestUriBuilder logoutRequestUriBuilder(SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsOAuth2LogoutRequestUriBuilder(addonsProperties.getClient());
+ }
+
+ /**
+ * Single tenant logout handler for OIDC provider complying to RP-Initiated
+ * Logout (or approximately complying to it like Auth0 or Cognito)
+ *
+ * @param logoutRequestUriBuilder delegate doing the smart job
+ * @param clientRegistrationRepository
+ * @return {@link SpringAddonsServerLogoutSuccessHandler}
+ */
+ @ConditionalOnMissingBean
+ @Bean
+ ServerLogoutSuccessHandler logoutSuccessHandler(LogoutRequestUriBuilder logoutUriBuilder,
+ ReactiveClientRegistrationRepository clientRegistrationRepo, SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsServerLogoutSuccessHandler(logoutUriBuilder, clientRegistrationRepo, addonsProperties);
+ }
+
+ /**
+ * Hook to override security rules for all path that are not listed in
+ * "permit-all". Default is isAuthenticated().
+ *
+ * @return a hook to override security rules for all path that are not listed in
+ * "permit-all". Default is isAuthenticated().
+ */
+ @ConditionalOnMissingBean
+ @Bean
+ ClientAuthorizeExchangeSpecPostProcessor clientAuthorizePostProcessor() {
+ return (ServerHttpSecurity.AuthorizeExchangeSpec spec) -> spec.anyExchange().authenticated();
+ }
+
+ /**
+ * Hook to override all or part of HttpSecurity auto-configuration.
+ * Called after spring-addons configuration was applied so that you can
+ * modify anything
+ *
+ * @return a hook to override all or part of HttpSecurity auto-configuration.
+ * Called after spring-addons configuration was applied so that you can
+ * modify anything
+ */
+ @ConditionalOnMissingBean
+ @Bean
+ ClientHttpSecurityPostProcessor clientHttpPostProcessor() {
+ return serverHttpSecurity -> serverHttpSecurity;
+ }
+
+ /**
+ * https://docs.spring.io/spring-security/reference/5.8/migration/reactive.html#_i_am_using_angularjs_or_another_javascript_framework
+ */
+ @Conditional(CookieCsrfCondition.class)
+ @ConditionalOnMissingBean(name = "csrfCookieWebFilter")
+ @Bean
+ WebFilter csrfCookieWebFilter() {
+ return (exchange, chain) -> {
+ exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty()).subscribe();
+ return chain.filter(exchange);
+ };
+ }
+
+ @ConditionalOnMissingBean
+ @Bean
+ ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver(ReactiveClientRegistrationRepository clientRegistrationRepository, SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsServerOAuth2AuthorizationRequestResolver(clientRegistrationRepository, addonsProperties.getClient());
+ }
+
+ @ConditionalOnMissingBean
+ @Bean
+ ServerLogoutHandler logoutHandler() {
+ return new DelegatingServerLogoutHandler(
+ new WebSessionServerLogoutHandler(),
+ new SecurityContextServerLogoutHandler());
+ }
+
+ @ConditionalOnMissingBean
+ @Bean
+ PreAuthorizationCodeServerRedirectStrategy preAuthorizationCodeRedirectStrategy(SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsPreAuthorizationCodeServerRedirectStrategy(
+ addonsProperties.getClient().getOauth2Redirections().getPreAuthorizationCode());
+ }
+
+ @Conditional(DefaultAuthenticationSuccessHandlerCondition.class)
+ @Bean
+ ServerAuthenticationSuccessHandler authenticationSuccessHandler(SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsOauth2ServerAuthenticationSuccessHandler(addonsProperties);
+ }
+
+ @Conditional(DefaultAuthenticationSuccessHandlerCondition.class)
+ @Bean
+ ServerAuthenticationFailureHandler authenticationFailureHandler(SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsOauth2ServerAuthenticationFailureHandler(addonsProperties);
+ }
+
+ static interface PreAuthorizationCodeServerRedirectStrategy extends ServerRedirectStrategy {}
+
+ public static class SpringAddonsPreAuthorizationCodeServerRedirectStrategy extends SpringAddonsOauth2ServerRedirectStrategy implements PreAuthorizationCodeServerRedirectStrategy {
+ public SpringAddonsPreAuthorizationCodeServerRedirectStrategy(HttpStatus defaultStatus) {
+ super(defaultStatus);
+ }
+
+ }
+
+ @ConditionalOnMissingBean
+ @Bean
+ Customizer oidcLogoutSpec() {
+ return Customizer.withDefaults();
+ }
+}
\ No newline at end of file
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/SpringAddonsServerLogoutSuccessHandler.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/SpringAddonsServerLogoutSuccessHandler.java
index ec5cb3877..fce843968 100644
--- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/SpringAddonsServerLogoutSuccessHandler.java
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/SpringAddonsServerLogoutSuccessHandler.java
@@ -30,7 +30,7 @@
* one which emitted the access-token with which the logout request is made).
*
*
- * This bean is auto-configured by {@link ReactiveSpringAddonsOidcClientBeans} as {@link ConditionalOnMissingBean @ConditionalOnMissingBean} of type
+ * This bean is auto-configured by {@link ReactiveSpringAddonsOidcClientWithLoginBeans} as {@link ConditionalOnMissingBean @ConditionalOnMissingBean} of type
* {@link ServerLogoutSuccessHandler}. Usage:
*
*
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/PerRegistrationOAuth2AuthorizedClientProvider.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/PerRegistrationOAuth2AuthorizedClientProvider.java
new file mode 100644
index 000000000..8511cc1e5
--- /dev/null
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/PerRegistrationOAuth2AuthorizedClientProvider.java
@@ -0,0 +1,129 @@
+package com.c4_soft.springaddons.security.oidc.starter.synchronised.client;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.StreamSupport;
+
+import org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.DelegatingOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.OAuth2AuthorizationContext;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.endpoint.DefaultClientCredentialsTokenResponseClient;
+import org.springframework.security.oauth2.client.endpoint.DefaultRefreshTokenTokenResponseClient;
+import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequestEntityConverter;
+import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequestEntityConverter;
+import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.util.Assert;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties;
+
+/**
+ *
+ * An alternative {@link OAuth2AuthorizedClientProvider} to {@link DelegatingOAuth2AuthorizedClientProvider} keeping a different provider for each client
+ * registration. This allows to define for each a set of extra parameters to add to token requests.
+ *
+ *
+ * @author Jerome Wacongne ch4mp@c4-soft.com
+ */
+public final class PerRegistrationOAuth2AuthorizedClientProvider implements OAuth2AuthorizedClientProvider {
+
+ private final Map providers;
+
+ public PerRegistrationOAuth2AuthorizedClientProvider(SpringAddonsOidcProperties addonsProperties, Map providers) {
+ this.providers = new HashMap<>(providers);
+ }
+
+ public PerRegistrationOAuth2AuthorizedClientProvider(
+ InMemoryClientRegistrationRepository clientRegistrationRepo,
+ SpringAddonsOidcProperties addonsProperties,
+ Map customProviders) {
+ this.providers = new HashMap<>(customProviders);
+ StreamSupport.stream(clientRegistrationRepo.spliterator(), false).forEach(reg -> {
+ if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(reg.getAuthorizationGrantType())) {
+ this.providers.putIfAbsent(reg.getRegistrationId(), new AuthorizationCodeOAuth2AuthorizedClientProvider());
+ } else if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(reg.getAuthorizationGrantType())) {
+ this.providers.putIfAbsent(reg.getRegistrationId(), new ClientCredentialsOAuth2AuthorizedClientProvider());
+ } else if (AuthorizationGrantType.REFRESH_TOKEN.equals(reg.getAuthorizationGrantType())) {
+ this.providers.putIfAbsent(reg.getRegistrationId(), new RefreshTokenOAuth2AuthorizedClientProvider());
+ } else {
+ throw new UnsupportedGrantTypeException(reg.getAuthorizationGrantType());
+ }
+
+ final var tokenParams = addonsProperties.getClient().getTokenRequestParams().getOrDefault(reg.getRegistrationId(), List.of());
+ if (tokenParams.isEmpty()) {
+ return;
+ }
+ final MultiValueMap extraParameters = new LinkedMultiValueMap<>(tokenParams.size());
+ for (final var param : tokenParams) {
+ extraParameters.add(param.getName(), param.getValue());
+ }
+
+ final var delegate = this.providers.get(reg.getRegistrationId());
+ if (delegate instanceof ClientCredentialsOAuth2AuthorizedClientProvider clientCredentialsProvider) {
+ final var requestEntityConverter = new OAuth2ClientCredentialsGrantRequestEntityConverter();
+ requestEntityConverter.addParametersConverter(source -> extraParameters);
+
+ final var clientCredentialsResponseClient = new DefaultClientCredentialsTokenResponseClient();
+ clientCredentialsResponseClient.setRequestEntityConverter(requestEntityConverter);
+
+ clientCredentialsProvider.setAccessTokenResponseClient(clientCredentialsResponseClient);
+
+ } else if (delegate instanceof RefreshTokenOAuth2AuthorizedClientProvider refreshTokenProvider) {
+ final var requestEntityConverter = new OAuth2RefreshTokenGrantRequestEntityConverter();
+ requestEntityConverter.addParametersConverter(source -> extraParameters);
+
+ final var refreshTokenResponseClient = new DefaultRefreshTokenTokenResponseClient();
+ refreshTokenResponseClient.setRequestEntityConverter(requestEntityConverter);
+
+ refreshTokenProvider.setAccessTokenResponseClient(refreshTokenResponseClient);
+ }
+ });
+ }
+
+ public PerRegistrationOAuth2AuthorizedClientProvider(
+ InMemoryClientRegistrationRepository clientRegistrationRepo,
+ SpringAddonsOidcProperties addonsProperties) {
+ this(clientRegistrationRepo, addonsProperties, Map.of());
+ }
+
+ @Override
+ public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) throws UnsupportedGrantTypeException {
+ if (context == null) {
+ return null;
+ }
+
+ final var provider = getDelegate(context.getClientRegistration().getRegistrationId());
+
+ return provider.authorize(context);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getDelegate(String registrationId) throws UnsupportedGrantTypeException {
+ final var provider = providers.get(registrationId);
+ return (T) provider;
+ }
+
+ public PerRegistrationOAuth2AuthorizedClientProvider setDelegate(String registrationId, OAuth2AuthorizedClientProvider delegate) {
+ Assert.notNull(registrationId, "registrationId cannot be null");
+ Assert.notNull(delegate, "delegate cannot be null");
+ providers.put(registrationId, delegate);
+ return this;
+ }
+
+ static class UnsupportedGrantTypeException extends RuntimeException {
+ private static final long serialVersionUID = 5600617070203595919L;
+
+ public UnsupportedGrantTypeException(AuthorizationGrantType grantType) {
+ super(
+ "No OAuth2AuthorizedClientProvider registered for GrantType: %s. Consider adding one to SpringAddonsDelegatingOAuth2AuthorizedClientProvider in your conf."
+ .formatted(grantType));
+ }
+ }
+}
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsAop.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsAop.java
index 776989a96..a4dcc74b4 100644
--- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsAop.java
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsAop.java
@@ -20,12 +20,12 @@
import org.springframework.stereotype.Component;
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientMultiTenancyEnabled;
-import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsOidcClientCondition;
+import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientWithLoginCondition;
import lombok.RequiredArgsConstructor;
@ConditionalOnWebApplication(type = Type.SERVLET)
-@Conditional({ IsOidcClientCondition.class, IsClientMultiTenancyEnabled.class })
+@Conditional({ IsClientWithLoginCondition.class, IsClientMultiTenancyEnabled.class })
@AutoConfiguration
@PropertySource(value = "classpath:/c4-spring-addons.properties", ignoreResourceNotFound = true)
public class SpringAddonsAop {
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsLogoutSuccessHandler.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsLogoutSuccessHandler.java
index 98881c310..7b21d32d7 100644
--- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsLogoutSuccessHandler.java
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsLogoutSuccessHandler.java
@@ -32,7 +32,7 @@
* one which emitted the access-token with which the logout request is made).
*
*
- * This bean is auto-configured by {@link SpringAddonsOidcClientBeans} as {@link ConditionalOnMissingBean @ConditionalOnMissingBean} of type
+ * This bean is auto-configured by {@link SpringAddonsOidcClientWithLoginBeans} as {@link ConditionalOnMissingBean @ConditionalOnMissingBean} of type
* {@link LogoutSuccessHandler}. Usage:
*
*
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsOidcClientBeans.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsOidcClientBeans.java
index 3c89fa770..52122edcc 100644
--- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsOidcClientBeans.java
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsOidcClientBeans.java
@@ -1,251 +1,40 @@
package com.c4_soft.springaddons.security.oidc.starter.synchronised.client;
-import java.util.Optional;
-
import org.springframework.boot.autoconfigure.AutoConfiguration;
-import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
-import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Conditional;
-import org.springframework.core.Ordered;
-import org.springframework.core.annotation.Order;
-import org.springframework.http.HttpStatus;
-import org.springframework.security.config.Customizer;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
-import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
-import org.springframework.security.web.SecurityFilterChain;
-import org.springframework.security.web.authentication.AuthenticationFailureHandler;
-import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
-import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
+import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
+import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
-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;
-import com.c4_soft.springaddons.security.oidc.starter.SpringAddonsOAuth2LogoutRequestUriBuilder;
-import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcClientProperties;
import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties;
-import com.c4_soft.springaddons.security.oidc.starter.properties.condition.bean.DefaultAuthenticationFailureHandlerCondition;
-import com.c4_soft.springaddons.security.oidc.starter.properties.condition.bean.DefaultAuthenticationSuccessHandlerCondition;
-import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsOidcClientCondition;
-import com.c4_soft.springaddons.security.oidc.starter.synchronised.ServletConfigurationSupport;
-import com.c4_soft.springaddons.security.oidc.starter.synchronised.SpringAddonsOidcBeans;
-
-import lombok.extern.slf4j.Slf4j;
-/**
- * The following {@link ConditionalOnMissingBean @ConditionalOnMissingBeans} are auto-configured
- *
- * - springAddonsClientFilterChain: a {@link SecurityFilterChain}. Instantiated only if "com.c4-soft.springaddons.oidc.client.security-matchers" property has
- * at least one entry. If defined, it is with highest precedence, to ensure that all routes defined in this security matcher property are intercepted by this
- * filter-chain.
- * - oAuth2AuthorizationRequestResolver: a {@link OAuth2AuthorizationRequestResolver}. Default instance is a
- * {@link SpringAddonsOAuth2AuthorizationRequestResolver} which sets the client hostname in the redirect URI with
- * {@link SpringAddonsOidcClientProperties#clientUri SpringAddonsOidcClientProperties#client-uri}
- * - logoutRequestUriBuilder: builder for RP-Initiated Logout 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)
- * - logoutSuccessHandler: a {@link LogoutSuccessHandler}. Default instance is a {@link SpringAddonsLogoutSuccessHandler} which logs a user out from the last
- * authorization server he logged on.
- * - authoritiesConverter: an {@link ClaimSetAuthoritiesConverter}. Default instance is a {@link ConfigurableClaimSetAuthoritiesConverter} which reads
- * spring-addons {@link SpringAddonsOidcProperties}
- * - clientAuthorizePostProcessor: a {@link ClientExpressionInterceptUrlRegistryPostProcessor} 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.
- * - clientHttpPostProcessor: a {@link ClientHttpSecurityPostProcessor} to override anything from above auto-configuration. It is called just before the
- * security filter-chain is returned. Default is a no-op.
- *
- *
- * @author Jerome Wacongne ch4mp@c4-soft.com
- */
-@ConditionalOnWebApplication(type = Type.SERVLET)
-@Conditional(IsOidcClientCondition.class)
-@EnableWebSecurity
+@ConditionalOnClass(OAuth2AuthorizedClientManager.class)
@AutoConfiguration
-@ImportAutoConfiguration(SpringAddonsOidcBeans.class)
-@Slf4j
public class SpringAddonsOidcClientBeans {
- /**
- *
- * 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.
- *
- * It defines:
- *
- * - If the path to login page was provided in conf, a @Controller must be provided to handle it. Otherwise Spring Boot default generated one is used
- * (be aware that it does not work when bound to 80 or 8080 with SSL enabled, so, in that case, use another port or define a login path and a controller to
- * handle it)
- * - logout (using {@link SpringAddonsLogoutSuccessHandler} by default)
- * - forces SSL usage if it is enabled
properties
- * - CSRF protection as defined in spring-addons client properties (enabled by default in this filter-chain).
- * - allow access to unauthorized requests to path matchers listed in spring-security client "permit-all" property
- * - as usual, apply {@link ClientExpressionInterceptUrlRegistryPostProcessor} for access control configuration from Java conf and
- * {@link ClientHttpSecurityPostProcessor} to override anything from the auto-configuration listed above
- *
- *
- * @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 SpringAddonsOAuth2AuthorizationRequestResolver} (adds
- * authorization request parameters defined in properties and builds absolutes callback URI)
- * @param preAuthorizationCodeRedirectStrategy the redirection strategy to use for authorization-code request
- * @param authenticationSuccessHandler the authentication success handler to use. Default is a {@link SpringAddonsOauth2AuthenticationSuccessHandler}
- * @param authenticationFailureHandler the authentication failure handler to use. Default is a {@link SpringAddonsOauth2AuthenticationFailureHandler}
- * @param logoutSuccessHandler Defaulted to {@link SpringAddonsLogoutSuccessHandler} which can handle "almost" RP Initiated Logout conformant OPs (like
- * Auth0 and Cognito). Default is a {@link SpringAddonsLogoutSuccessHandler}
- * @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 oidcLogoutCustomizer a configurer for Spring Security Back-Channel Logout implementation
- * @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
- SecurityFilterChain springAddonsClientFilterChain(
- HttpSecurity http,
- ServerProperties serverProperties,
- PreAuthorizationCodeRedirectStrategy preAuthorizationCodeRedirectStrategy,
- OAuth2AuthorizationRequestResolver authorizationRequestResolver,
- Optional authenticationSuccessHandler,
- Optional authenticationFailureHandler,
- LogoutSuccessHandler logoutSuccessHandler,
- SpringAddonsOidcProperties addonsProperties,
- ClientExpressionInterceptUrlRegistryPostProcessor authorizePostProcessor,
- ClientHttpSecurityPostProcessor httpPostProcessor,
- Customizer> oidcLogoutCustomizer)
- throws Exception {
- // @formatter:off
- log.info("Applying client OAuth2 configuration for: {}", addonsProperties.getClient().getSecurityMatchers());
- http.securityMatcher(addonsProperties.getClient().getSecurityMatchers().toArray(new String[] {}));
-
- http.oauth2Login(login -> {
- login.authorizationEndpoint(authorizationEndpoint -> {
- authorizationEndpoint.authorizationRedirectStrategy(preAuthorizationCodeRedirectStrategy);
- authorizationEndpoint.authorizationRequestResolver(authorizationRequestResolver);
- });
- addonsProperties.getClient().getLoginPath().ifPresent(login::loginPage);
- authenticationSuccessHandler.ifPresent(login::successHandler);
- authenticationFailureHandler.ifPresent(login::failureHandler);
- });
-
- http.logout(logout -> {
- logout.logoutSuccessHandler(logoutSuccessHandler);
- });
- // @formatter:on
-
- if (addonsProperties.getClient().getBackChannelLogout().isEnabled()) {
- http.oidcLogout(oidcLogoutCustomizer);
- }
-
- ServletConfigurationSupport.configureClient(http, serverProperties, addonsProperties.getClient(), authorizePostProcessor, httpPostProcessor);
-
- return http.build();
- }
-
- /**
- * Use a {@link SpringAddonsOAuth2AuthorizationRequestResolver} which:
- *
- * - takes hostname and port from configuration properties (and works even if SSL is enabled on port 8080)
- * - spport defining additionl authorization request parameters from properties
- *
- *
- * @param clientRegistrationRepository
- * @param addonsProperties
- * @return {@link SpringAddonsOAuth2AuthorizationRequestResolver}
- */
@ConditionalOnMissingBean
@Bean
- OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver(
+ OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
- SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsOAuth2AuthorizationRequestResolver(clientRegistrationRepository, addonsProperties.getClient());
- }
+ OAuth2AuthorizedClientRepository authorizedClientRepository,
+ OAuth2AuthorizedClientProvider oauth2AuthorizedClientProvider) {
- /**
- * Build logout request for RP-Initiated Logout. It works with most OIDC
- * provider: those complying with the spec (Keycloak for instance), off course, but also those which are close enough to it (Auth0, Cognito, ...)
- *
- * @param addonsProperties {@link SpringAddonsOAuth2ClientProperties} to pick logout configuration for divergence to the standard (logout URI not provided
- * in .well-known/openid-configuration and non-conform parameter names)
- * @return {@link SpringAddonsOAuth2LogoutRequestUriBuilder]
- */
- @ConditionalOnMissingBean
- @Bean
- LogoutRequestUriBuilder logoutRequestUriBuilder(SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsOAuth2LogoutRequestUriBuilder(addonsProperties.getClient());
- }
+ final var authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
+ authorizedClientManager.setAuthorizedClientProvider(oauth2AuthorizedClientProvider);
- /**
- * Single tenant logout handler for OIDC provider complying to RP-Initiated
- * Logout (or approximately complying to it like Auth0 or Cognito)
- *
- * @param logoutRequestUriBuilder delegate doing the smart job
- * @param clientRegistrationRepository
- * @param addonsProperties
- * @return {@link SpringAddonsLogoutSuccessHandler}
- */
- @ConditionalOnMissingBean
- @Bean
- LogoutSuccessHandler logoutSuccessHandler(
- LogoutRequestUriBuilder logoutRequestUriBuilder,
- ClientRegistrationRepository clientRegistrationRepository,
- SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsLogoutSuccessHandler(logoutRequestUriBuilder, clientRegistrationRepository, addonsProperties);
+ return authorizedClientManager;
}
- /**
- * @return a Post processor for access control in Java configuration which requires users to be authenticated. It is called after "permit-all" configuration
- * property was applied.
- */
@ConditionalOnMissingBean
@Bean
- ClientExpressionInterceptUrlRegistryPostProcessor clientAuthorizePostProcessor() {
- return registry -> registry.anyRequest().authenticated();
- }
-
- /**
- * @return a no-op post processor
- */
- @ConditionalOnMissingBean
- @Bean
- ClientHttpSecurityPostProcessor clientHttpPostProcessor() {
- return http -> http;
- }
-
- @ConditionalOnMissingBean
- @Bean
- PreAuthorizationCodeRedirectStrategy authorizationCodeRedirectStrategy(SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsPreAuthorizationCodeRedirectStrategy(addonsProperties.getClient().getOauth2Redirections().getPreAuthorizationCode());
- }
-
- static class SpringAddonsPreAuthorizationCodeRedirectStrategy extends SpringAddonsOauth2RedirectStrategy implements PreAuthorizationCodeRedirectStrategy {
- public SpringAddonsPreAuthorizationCodeRedirectStrategy(HttpStatus defaultStatus) {
- super(defaultStatus);
- }
- }
-
- @Conditional(DefaultAuthenticationSuccessHandlerCondition.class)
- @Bean
- AuthenticationSuccessHandler authenticationSuccessHandler(SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsOauth2AuthenticationSuccessHandler(addonsProperties);
- }
-
- @Conditional(DefaultAuthenticationFailureHandlerCondition.class)
- @Bean
- AuthenticationFailureHandler authenticationFailureHandler(SpringAddonsOidcProperties addonsProperties) {
- return new SpringAddonsOauth2AuthenticationFailureHandler(addonsProperties);
- }
-
- @ConditionalOnMissingBean
- @Bean
- Customizer> oidcLogoutCustomizer() {
- return Customizer.withDefaults();
+ OAuth2AuthorizedClientProvider oauth2AuthorizedClientProvider(
+ SpringAddonsOidcProperties addonsProperties,
+ InMemoryClientRegistrationRepository clientRegistrationRepository) {
+ return new PerRegistrationOAuth2AuthorizedClientProvider(clientRegistrationRepository, addonsProperties);
}
}
diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsOidcClientWithLoginBeans.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsOidcClientWithLoginBeans.java
new file mode 100644
index 000000000..c2907a5a7
--- /dev/null
+++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsOidcClientWithLoginBeans.java
@@ -0,0 +1,251 @@
+package com.c4_soft.springaddons.security.oidc.starter.synchronised.client;
+
+import java.util.Optional;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Conditional;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer;
+import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+
+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;
+import com.c4_soft.springaddons.security.oidc.starter.SpringAddonsOAuth2LogoutRequestUriBuilder;
+import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcClientProperties;
+import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties;
+import com.c4_soft.springaddons.security.oidc.starter.properties.condition.bean.DefaultAuthenticationFailureHandlerCondition;
+import com.c4_soft.springaddons.security.oidc.starter.properties.condition.bean.DefaultAuthenticationSuccessHandlerCondition;
+import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientWithLoginCondition;
+import com.c4_soft.springaddons.security.oidc.starter.synchronised.ServletConfigurationSupport;
+import com.c4_soft.springaddons.security.oidc.starter.synchronised.SpringAddonsOidcBeans;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * The following {@link ConditionalOnMissingBean @ConditionalOnMissingBeans} are auto-configured
+ *
+ * - springAddonsClientFilterChain: a {@link SecurityFilterChain}. Instantiated only if "com.c4-soft.springaddons.oidc.client.security-matchers" property has
+ * at least one entry. If defined, it is with highest precedence, to ensure that all routes defined in this security matcher property are intercepted by this
+ * filter-chain.
+ * - oAuth2AuthorizationRequestResolver: a {@link OAuth2AuthorizationRequestResolver}. Default instance is a
+ * {@link SpringAddonsOAuth2AuthorizationRequestResolver} which sets the client hostname in the redirect URI with
+ * {@link SpringAddonsOidcClientProperties#clientUri SpringAddonsOidcClientProperties#client-uri}
+ * - logoutRequestUriBuilder: builder for RP-Initiated Logout 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)
+ * - logoutSuccessHandler: a {@link LogoutSuccessHandler}. Default instance is a {@link SpringAddonsLogoutSuccessHandler} which logs a user out from the last
+ * authorization server he logged on.
+ * - authoritiesConverter: an {@link ClaimSetAuthoritiesConverter}. Default instance is a {@link ConfigurableClaimSetAuthoritiesConverter} which reads
+ * spring-addons {@link SpringAddonsOidcProperties}
+ * - clientAuthorizePostProcessor: a {@link ClientExpressionInterceptUrlRegistryPostProcessor} 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.
+ * - clientHttpPostProcessor: a {@link ClientHttpSecurityPostProcessor} to override anything from above auto-configuration. It is called just before the
+ * security filter-chain is returned. Default is a no-op.
+ *
+ *
+ * @author Jerome Wacongne ch4mp@c4-soft.com
+ */
+@ConditionalOnWebApplication(type = Type.SERVLET)
+@Conditional(IsClientWithLoginCondition.class)
+@EnableWebSecurity
+@AutoConfiguration
+@ImportAutoConfiguration(SpringAddonsOidcBeans.class)
+@Slf4j
+public class SpringAddonsOidcClientWithLoginBeans {
+
+ /**
+ *
+ * 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.
+ *
+ * It defines:
+ *
+ * - If the path to login page was provided in conf, a @Controller must be provided to handle it. Otherwise Spring Boot default generated one is used
+ * (be aware that it does not work when bound to 80 or 8080 with SSL enabled, so, in that case, use another port or define a login path and a controller to
+ * handle it)
+ * - logout (using {@link SpringAddonsLogoutSuccessHandler} by default)
+ * - forces SSL usage if it is enabled
properties
+ * - CSRF protection as defined in spring-addons client properties (enabled by default in this filter-chain).
+ * - allow access to unauthorized requests to path matchers listed in spring-security client "permit-all" property
+ * - as usual, apply {@link ClientExpressionInterceptUrlRegistryPostProcessor} for access control configuration from Java conf and
+ * {@link ClientHttpSecurityPostProcessor} to override anything from the auto-configuration listed above
+ *
+ *
+ * @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 SpringAddonsOAuth2AuthorizationRequestResolver} (adds
+ * authorization request parameters defined in properties and builds absolutes callback URI)
+ * @param preAuthorizationCodeRedirectStrategy the redirection strategy to use for authorization-code request
+ * @param authenticationSuccessHandler the authentication success handler to use. Default is a {@link SpringAddonsOauth2AuthenticationSuccessHandler}
+ * @param authenticationFailureHandler the authentication failure handler to use. Default is a {@link SpringAddonsOauth2AuthenticationFailureHandler}
+ * @param logoutSuccessHandler Defaulted to {@link SpringAddonsLogoutSuccessHandler} which can handle "almost" RP Initiated Logout conformant OPs (like
+ * Auth0 and Cognito). Default is a {@link SpringAddonsLogoutSuccessHandler}
+ * @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 oidcLogoutCustomizer a configurer for Spring Security Back-Channel Logout implementation
+ * @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
+ SecurityFilterChain springAddonsClientFilterChain(
+ HttpSecurity http,
+ ServerProperties serverProperties,
+ PreAuthorizationCodeRedirectStrategy preAuthorizationCodeRedirectStrategy,
+ OAuth2AuthorizationRequestResolver authorizationRequestResolver,
+ Optional authenticationSuccessHandler,
+ Optional authenticationFailureHandler,
+ LogoutSuccessHandler logoutSuccessHandler,
+ SpringAddonsOidcProperties addonsProperties,
+ ClientExpressionInterceptUrlRegistryPostProcessor authorizePostProcessor,
+ ClientHttpSecurityPostProcessor httpPostProcessor,
+ Customizer> oidcLogoutCustomizer)
+ throws Exception {
+ // @formatter:off
+ log.info("Applying client OAuth2 configuration for: {}", addonsProperties.getClient().getSecurityMatchers());
+ http.securityMatcher(addonsProperties.getClient().getSecurityMatchers().toArray(new String[] {}));
+
+ http.oauth2Login(login -> {
+ login.authorizationEndpoint(authorizationEndpoint -> {
+ authorizationEndpoint.authorizationRedirectStrategy(preAuthorizationCodeRedirectStrategy);
+ authorizationEndpoint.authorizationRequestResolver(authorizationRequestResolver);
+ });
+ addonsProperties.getClient().getLoginPath().ifPresent(login::loginPage);
+ authenticationSuccessHandler.ifPresent(login::successHandler);
+ authenticationFailureHandler.ifPresent(login::failureHandler);
+ });
+
+ http.logout(logout -> {
+ logout.logoutSuccessHandler(logoutSuccessHandler);
+ });
+ // @formatter:on
+
+ if (addonsProperties.getClient().getBackChannelLogout().isEnabled()) {
+ http.oidcLogout(oidcLogoutCustomizer);
+ }
+
+ ServletConfigurationSupport.configureClient(http, serverProperties, addonsProperties.getClient(), authorizePostProcessor, httpPostProcessor);
+
+ return http.build();
+ }
+
+ /**
+ * Use a {@link SpringAddonsOAuth2AuthorizationRequestResolver} which:
+ *
+ * - takes hostname and port from configuration properties (and works even if SSL is enabled on port 8080)
+ * - spport defining additionl authorization request parameters from properties
+ *
+ *
+ * @param clientRegistrationRepository
+ * @param addonsProperties
+ * @return {@link SpringAddonsOAuth2AuthorizationRequestResolver}
+ */
+ @ConditionalOnMissingBean
+ @Bean
+ OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver(
+ ClientRegistrationRepository clientRegistrationRepository,
+ SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsOAuth2AuthorizationRequestResolver(clientRegistrationRepository, addonsProperties.getClient());
+ }
+
+ /**
+ * Build logout request for RP-Initiated Logout. It works with most OIDC
+ * provider: those complying with the spec (Keycloak for instance), off course, but also those which are close enough to it (Auth0, Cognito, ...)
+ *
+ * @param addonsProperties {@link SpringAddonsOAuth2ClientProperties} to pick logout configuration for divergence to the standard (logout URI not provided
+ * in .well-known/openid-configuration and non-conform parameter names)
+ * @return {@link SpringAddonsOAuth2LogoutRequestUriBuilder]
+ */
+ @ConditionalOnMissingBean
+ @Bean
+ LogoutRequestUriBuilder logoutRequestUriBuilder(SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsOAuth2LogoutRequestUriBuilder(addonsProperties.getClient());
+ }
+
+ /**
+ * Single tenant logout handler for OIDC provider complying to RP-Initiated
+ * Logout (or approximately complying to it like Auth0 or Cognito)
+ *
+ * @param logoutRequestUriBuilder delegate doing the smart job
+ * @param clientRegistrationRepository
+ * @param addonsProperties
+ * @return {@link SpringAddonsLogoutSuccessHandler}
+ */
+ @ConditionalOnMissingBean
+ @Bean
+ LogoutSuccessHandler logoutSuccessHandler(
+ LogoutRequestUriBuilder logoutRequestUriBuilder,
+ ClientRegistrationRepository clientRegistrationRepository,
+ SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsLogoutSuccessHandler(logoutRequestUriBuilder, clientRegistrationRepository, addonsProperties);
+ }
+
+ /**
+ * @return a Post processor for access control in Java configuration which requires users to be authenticated. It is called after "permit-all" configuration
+ * property was applied.
+ */
+ @ConditionalOnMissingBean
+ @Bean
+ ClientExpressionInterceptUrlRegistryPostProcessor clientAuthorizePostProcessor() {
+ return registry -> registry.anyRequest().authenticated();
+ }
+
+ /**
+ * @return a no-op post processor
+ */
+ @ConditionalOnMissingBean
+ @Bean
+ ClientHttpSecurityPostProcessor clientHttpPostProcessor() {
+ return http -> http;
+ }
+
+ @ConditionalOnMissingBean
+ @Bean
+ PreAuthorizationCodeRedirectStrategy authorizationCodeRedirectStrategy(SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsPreAuthorizationCodeRedirectStrategy(addonsProperties.getClient().getOauth2Redirections().getPreAuthorizationCode());
+ }
+
+ static class SpringAddonsPreAuthorizationCodeRedirectStrategy extends SpringAddonsOauth2RedirectStrategy implements PreAuthorizationCodeRedirectStrategy {
+ public SpringAddonsPreAuthorizationCodeRedirectStrategy(HttpStatus defaultStatus) {
+ super(defaultStatus);
+ }
+ }
+
+ @Conditional(DefaultAuthenticationSuccessHandlerCondition.class)
+ @Bean
+ AuthenticationSuccessHandler authenticationSuccessHandler(SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsOauth2AuthenticationSuccessHandler(addonsProperties);
+ }
+
+ @Conditional(DefaultAuthenticationFailureHandlerCondition.class)
+ @Bean
+ AuthenticationFailureHandler authenticationFailureHandler(SpringAddonsOidcProperties addonsProperties) {
+ return new SpringAddonsOauth2AuthenticationFailureHandler(addonsProperties);
+ }
+
+ @ConditionalOnMissingBean
+ @Bean
+ Customizer> oidcLogoutCustomizer() {
+ return Customizer.withDefaults();
+ }
+}
diff --git a/spring-addons-starter-oidc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-addons-starter-oidc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index e6c060e23..c8f428a70 100644
--- a/spring-addons-starter-oidc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/spring-addons-starter-oidc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,7 +1,9 @@
com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientBeans
+com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsOidcClientWithLoginBeans
com.c4_soft.springaddons.security.oidc.starter.reactive.client.ReactiveSpringAddonsAop
com.c4_soft.springaddons.security.oidc.starter.reactive.resourceserver.ReactiveSpringAddonsOidcResourceServerBeans
com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsOidcClientBeans
+com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsOidcClientWithLoginBeans
com.c4_soft.springaddons.security.oidc.starter.synchronised.client.SpringAddonsAop
com.c4_soft.springaddons.security.oidc.starter.synchronised.resourceserver.SpringAddonsOidcResourceServerBeans