Skip to content

Commit 180c9d6

Browse files
Extended csrf cookie customization (#286)
CSRF token repository customization via post-processor(s)
1 parent 4ce35ff commit 180c9d6

File tree

8 files changed

+78
-31
lines changed

8 files changed

+78
-31
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.c4_soft.springaddons.security.oidc.starter.reactive;
2+
3+
import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository;
4+
5+
/**
6+
* Customize the reactive csrf token repository configured by spring-addons
7+
*/
8+
@FunctionalInterface
9+
public interface CookieServerCsrfTokenRepositoryPostProcessor {
10+
CookieServerCsrfTokenRepository process(CookieServerCsrfTokenRepository repository);
11+
}

spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/ReactiveConfigurationSupport.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.nio.charset.Charset;
66
import java.util.ArrayList;
77
import java.util.List;
8+
import java.util.Optional;
89
import java.util.stream.Collectors;
910
import org.springframework.boot.autoconfigure.web.ServerProperties;
1011
import org.springframework.core.io.buffer.DataBufferUtils;
@@ -37,7 +38,8 @@ public class ReactiveConfigurationSupport {
3738
public static ServerHttpSecurity configureResourceServer(ServerHttpSecurity http,
3839
ServerProperties serverProperties, SpringAddonsOidcProperties addonsProperties,
3940
ResourceServerAuthorizeExchangeSpecPostProcessor authorizePostProcessor,
40-
ResourceServerReactiveHttpSecurityPostProcessor httpPostProcessor) {
41+
ResourceServerReactiveHttpSecurityPostProcessor httpPostProcessor,
42+
Optional<CookieServerCsrfTokenRepositoryPostProcessor> csrfPostProcessor) {
4143

4244
http.exceptionHandling(exceptions -> {
4345
final var issuers = addonsProperties.getOps().stream().map(OpenidProviderProperties::getIss)
@@ -59,7 +61,8 @@ public static ServerHttpSecurity configureResourceServer(ServerHttpSecurity http
5961
ReactiveConfigurationSupport.configureState(http,
6062
addonsProperties.getResourceserver().isStatlessSessions(),
6163
addonsProperties.getResourceserver().getCsrf(), addonsProperties.getResourceserver().getCsrfCookieName(),
62-
addonsProperties.getResourceserver().getCsrfCookiePath());
64+
addonsProperties.getResourceserver().getCsrfCookiePath(),
65+
csrfPostProcessor);
6366

6467
// FIXME: use only the new CORS properties at next major release
6568
final var corsProps = new ArrayList<>(addonsProperties.getCors());
@@ -83,11 +86,13 @@ public static ServerHttpSecurity configureResourceServer(ServerHttpSecurity http
8386
public static ServerHttpSecurity configureClient(ServerHttpSecurity http,
8487
ServerProperties serverProperties, SpringAddonsOidcProperties addonsProperties,
8588
ClientAuthorizeExchangeSpecPostProcessor authorizePostProcessor,
86-
ClientReactiveHttpSecurityPostProcessor httpPostProcessor) {
89+
ClientReactiveHttpSecurityPostProcessor httpPostProcessor,
90+
Optional<CookieServerCsrfTokenRepositoryPostProcessor> csrfPostProcessor) {
8791

8892
ReactiveConfigurationSupport.configureState(http, false,
8993
addonsProperties.getClient().getCsrf(), addonsProperties.getClient().getCsrfCookieName(),
90-
addonsProperties.getClient().getCsrfCookiePath());
94+
addonsProperties.getClient().getCsrfCookiePath(),
95+
csrfPostProcessor);
9196

9297
// FIXME: use only the new CORS properties at next major release
9398
final var corsProps = new ArrayList<>(addonsProperties.getCors());
@@ -149,7 +154,8 @@ public static CorsWebFilter getCorsFilterBean(List<CorsProperties> corsPropertie
149154
}
150155

151156
public static ServerHttpSecurity configureState(ServerHttpSecurity http, boolean isStatless,
152-
Csrf csrfEnum, String csrfCookieName, String csrfCookiePath) {
157+
Csrf csrfEnum, String csrfCookieName, String csrfCookiePath,
158+
Optional<CookieServerCsrfTokenRepositoryPostProcessor> csrfPostProcessor) {
153159

154160
if (isStatless) {
155161
http.securityContextRepository(NoOpServerSecurityContextRepository.getInstance());
@@ -176,7 +182,7 @@ public static ServerHttpSecurity configureState(ServerHttpSecurity http, boolean
176182
final var repo = CookieServerCsrfTokenRepository.withHttpOnlyFalse();
177183
repo.setCookiePath(csrfCookiePath);
178184
repo.setCookieName(csrfCookieName);
179-
csrf.csrfTokenRepository(repo)
185+
csrf.csrfTokenRepository(csrfPostProcessor.map(pp -> pp.process(repo)).orElse(repo))
180186
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler());
181187
break;
182188
}

spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/ReactiveSpringAddonsOidcClientWithLoginBeans.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsNotServlet;
5252
import com.c4_soft.springaddons.security.oidc.starter.reactive.ReactiveConfigurationSupport;
5353
import com.c4_soft.springaddons.security.oidc.starter.reactive.ReactiveSpringAddonsOidcBeans;
54+
import com.c4_soft.springaddons.security.oidc.starter.reactive.CookieServerCsrfTokenRepositoryPostProcessor;
5455
import lombok.extern.slf4j.Slf4j;
5556
import reactor.core.publisher.Mono;
5657

@@ -133,6 +134,7 @@ public class ReactiveSpringAddonsOidcClientWithLoginBeans {
133134
* applied (default is "isAuthenticated()" to everything that was not matched)
134135
* @param httpPostProcessor post process the "http" builder just before it is returned (enables to
135136
* override anything from the auto-configuration) spring-addons client properties}
137+
* @param csrfPostProcessor Optional hook to customize the csrf token repository
136138
* @param logoutHandler if present, this custom {@link ServerLogoutHandler} is applied.
137139
* @param oidcBackChannelLogoutHandler if present, Back-Channel Logout is enabled. A default
138140
* {@link OidcBackChannelServerLogoutHandler} is provided if
@@ -153,6 +155,7 @@ SecurityWebFilterChain clientFilterChain(ServerHttpSecurity http,
153155
ServerLogoutSuccessHandler logoutSuccessHandler,
154156
ClientAuthorizeExchangeSpecPostProcessor authorizePostProcessor,
155157
ClientReactiveHttpSecurityPostProcessor httpPostProcessor,
158+
Optional<CookieServerCsrfTokenRepositoryPostProcessor> csrfPostProcessor,
156159
Optional<ServerLogoutHandler> logoutHandler, BeanFactory beanFactory) throws Exception {
157160

158161
final var clientRoutes = addonsProperties.getClient().getSecurityMatchers().stream()
@@ -192,7 +195,8 @@ SecurityWebFilterChain clientFilterChain(ServerHttpSecurity http,
192195
http.oidcLogout(ol -> ol.backChannel(bc -> bc.logoutHandler(handler)));
193196
}
194197

195-
ReactiveConfigurationSupport.configureClient(http, serverProperties, addonsProperties, authorizePostProcessor, httpPostProcessor);
198+
ReactiveConfigurationSupport.configureClient(http, serverProperties, addonsProperties,
199+
authorizePostProcessor, httpPostProcessor, csrfPostProcessor);
196200

197201
return http.build();
198202
}

spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/resourceserver/ReactiveSpringAddonsOidcResourceServerBeans.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
package com.c4_soft.springaddons.security.oidc.starter.reactive.resourceserver;
22

33
import java.time.Instant;
4-
import java.util.ArrayList;
5-
import java.util.Collection;
6-
import java.util.Date;
7-
import java.util.Map;
8-
4+
import java.util.*;
95
import org.springframework.boot.autoconfigure.AutoConfiguration;
106
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
117
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -52,6 +48,7 @@
5248
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsOidcResourceServerCondition;
5349
import com.c4_soft.springaddons.security.oidc.starter.reactive.ReactiveConfigurationSupport;
5450
import com.c4_soft.springaddons.security.oidc.starter.reactive.ReactiveSpringAddonsOidcBeans;
51+
import com.c4_soft.springaddons.security.oidc.starter.reactive.CookieServerCsrfTokenRepositoryPostProcessor;
5552

5653
import reactor.core.publisher.Mono;
5754

@@ -103,6 +100,7 @@ public class ReactiveSpringAddonsOidcResourceServerBeans {
103100
* @param authorizePostProcessor Hook to override access-control rules for all path that are not listed in "permit-all"
104101
* @param httpPostProcessor Hook to override all or part of HttpSecurity auto-configuration
105102
* @param authenticationManagerResolver Converts successful JWT decoding result into an {@link Authentication}
103+
* @param csrfPostProcessor Optional hook to customize the csrf token repository
106104
* @return A default {@link SecurityWebFilterChain} for reactive resource-servers with JWT decoder(matches all
107105
* unmatched routes with lowest precedence)
108106
*/
@@ -115,12 +113,14 @@ SecurityWebFilterChain springAddonsJwtResourceServerSecurityFilterChain(
115113
SpringAddonsOidcProperties addonsProperties,
116114
ResourceServerAuthorizeExchangeSpecPostProcessor authorizePostProcessor,
117115
ResourceServerReactiveHttpSecurityPostProcessor httpPostProcessor,
118-
ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver) {
116+
ReactiveAuthenticationManagerResolver<ServerWebExchange> authenticationManagerResolver,
117+
Optional<CookieServerCsrfTokenRepositoryPostProcessor> csrfPostProcessor) {
119118
http.oauth2ResourceServer(server -> {
120119
server.authenticationManagerResolver(authenticationManagerResolver);
121120
});
122121

123-
ReactiveConfigurationSupport.configureResourceServer(http, serverProperties, addonsProperties, authorizePostProcessor, httpPostProcessor);
122+
ReactiveConfigurationSupport.configureResourceServer(http, serverProperties, addonsProperties,
123+
authorizePostProcessor, httpPostProcessor, csrfPostProcessor);
124124

125125
return http.build();
126126
}
@@ -142,6 +142,7 @@ SecurityWebFilterChain springAddonsJwtResourceServerSecurityFilterChain(
142142
* @param authorizePostProcessor Hook to override access-control rules for all path that are not listed in "permit-all"
143143
* @param httpPostProcessor Hook to override all or part of HttpSecurity auto-configuration
144144
* @param introspectionAuthenticationConverter Converts successful introspection result into an {@link Authentication}
145+
* @param csrfPostProcessor Optional hook to customize the csrf token repository
145146
* @return A default {@link SecurityWebFilterChain} for reactive resource-servers with access-token
146147
* introspection (matches all unmatched routes with lowest precedence)
147148
*/
@@ -155,13 +156,15 @@ SecurityWebFilterChain springAddonsIntrospectingResourceServerSecurityFilterChai
155156
ResourceServerAuthorizeExchangeSpecPostProcessor authorizePostProcessor,
156157
ResourceServerReactiveHttpSecurityPostProcessor httpPostProcessor,
157158
ReactiveOpaqueTokenAuthenticationConverter introspectionAuthenticationConverter,
158-
ReactiveOpaqueTokenIntrospector opaqueTokenIntrospector) {
159+
ReactiveOpaqueTokenIntrospector opaqueTokenIntrospector,
160+
Optional<CookieServerCsrfTokenRepositoryPostProcessor> csrfPostProcessor) {
159161
http.oauth2ResourceServer(server -> server.opaqueToken(ot -> {
160162
ot.introspector(opaqueTokenIntrospector);
161163
ot.authenticationConverter(introspectionAuthenticationConverter);
162164
}));
163165

164-
ReactiveConfigurationSupport.configureResourceServer(http, serverProperties, addonsProperties, authorizePostProcessor, httpPostProcessor);
166+
ReactiveConfigurationSupport.configureResourceServer(http, serverProperties, addonsProperties,
167+
authorizePostProcessor, httpPostProcessor, csrfPostProcessor);
165168

166169
return http.build();
167170
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.c4_soft.springaddons.security.oidc.starter.synchronised;
2+
3+
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
4+
5+
/**
6+
* Customize the servlet csrf token repository configured by spring-addons
7+
*/
8+
@FunctionalInterface
9+
public interface CookieCsrfTokenRepositoryPostProcessor {
10+
CookieCsrfTokenRepository process(CookieCsrfTokenRepository repository);
11+
}

spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/ServletConfigurationSupport.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.net.URI;
55
import java.util.ArrayList;
66
import java.util.List;
7+
import java.util.Optional;
78
import java.util.function.Supplier;
89
import java.util.stream.Collectors;
910
import org.springframework.boot.autoconfigure.web.ServerProperties;
@@ -37,7 +38,8 @@ public class ServletConfigurationSupport {
3738
public static HttpSecurity configureResourceServer(HttpSecurity http,
3839
ServerProperties serverProperties, SpringAddonsOidcProperties addonsProperties,
3940
ResourceServerExpressionInterceptUrlRegistryPostProcessor authorizePostProcessor,
40-
ResourceServerSynchronizedHttpSecurityPostProcessor httpPostProcessor) throws Exception {
41+
ResourceServerSynchronizedHttpSecurityPostProcessor httpPostProcessor,
42+
Optional<CookieCsrfTokenRepositoryPostProcessor> csrfPostProcessor) throws Exception {
4143

4244
http.exceptionHandling(exceptions -> {
4345
final var issuers = addonsProperties.getOps().stream().map(OpenidProviderProperties::getIss)
@@ -54,7 +56,8 @@ public static HttpSecurity configureResourceServer(HttpSecurity http,
5456
addonsProperties.getResourceserver().isStatlessSessions(),
5557
addonsProperties.getResourceserver().getCsrf(),
5658
addonsProperties.getResourceserver().getCsrfCookieName(),
57-
addonsProperties.getResourceserver().getCsrfCookiePath());
59+
addonsProperties.getResourceserver().getCsrfCookiePath(),
60+
csrfPostProcessor);
5861

5962
// FIXME: use only the new CORS properties at next major release
6063
final var corsProps = new ArrayList<>(addonsProperties.getCors());
@@ -75,11 +78,13 @@ public static HttpSecurity configureResourceServer(HttpSecurity http,
7578
public static HttpSecurity configureClient(HttpSecurity http, ServerProperties serverProperties,
7679
SpringAddonsOidcProperties addonsProperties,
7780
ClientExpressionInterceptUrlRegistryPostProcessor authorizePostProcessor,
78-
ClientSynchronizedHttpSecurityPostProcessor httpPostProcessor) throws Exception {
81+
ClientSynchronizedHttpSecurityPostProcessor httpPostProcessor,
82+
Optional<CookieCsrfTokenRepositoryPostProcessor> csrfPostProcessor) throws Exception {
7983

8084
ServletConfigurationSupport.configureState(http, false, addonsProperties.getClient().getCsrf(),
8185
addonsProperties.getClient().getCsrfCookieName(),
82-
addonsProperties.getClient().getCsrfCookiePath());
86+
addonsProperties.getClient().getCsrfCookiePath(),
87+
csrfPostProcessor);
8388

8489
// FIXME: use only the new CORS properties at next major release
8590
final var corsProps = new ArrayList<>(addonsProperties.getCors());
@@ -141,7 +146,7 @@ public static CorsFilter getCorsFilterBean(List<CorsProperties> corsProperties)
141146
}
142147

143148
public static HttpSecurity configureState(HttpSecurity http, boolean isStatless, Csrf csrfEnum, String csrfCookieName,
144-
String csrfCookiePath)
149+
String csrfCookiePath, Optional<CookieCsrfTokenRepositoryPostProcessor> csrfPostProcessor)
145150
throws Exception {
146151

147152
if (isStatless) {
@@ -165,7 +170,7 @@ public static HttpSecurity configureState(HttpSecurity http, boolean isStatless,
165170
final var repo = CookieCsrfTokenRepository.withHttpOnlyFalse();
166171
repo.setCookiePath(csrfCookiePath);
167172
repo.setCookieName(csrfCookieName);
168-
configurer.csrfTokenRepository(repo)
173+
configurer.csrfTokenRepository(csrfPostProcessor.map(pp -> pp.process(repo)).orElse(repo))
169174
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler());
170175
break;
171176
}

spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/synchronised/client/SpringAddonsOidcClientWithLoginBeans.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.c4_soft.springaddons.security.oidc.starter.properties.condition.configuration.IsClientWithLoginCondition;
5151
import com.c4_soft.springaddons.security.oidc.starter.synchronised.ServletConfigurationSupport;
5252
import com.c4_soft.springaddons.security.oidc.starter.synchronised.SpringAddonsOidcBeans;
53+
import com.c4_soft.springaddons.security.oidc.starter.synchronised.CookieCsrfTokenRepositoryPostProcessor;
5354
import jakarta.servlet.http.HttpServletRequest;
5455
import jakarta.servlet.http.HttpServletResponse;
5556
import lombok.extern.slf4j.Slf4j;
@@ -140,6 +141,7 @@ public class SpringAddonsOidcClientWithLoginBeans {
140141
* applied (default is "isAuthenticated()" to everything that was not matched)
141142
* @param httpPostProcessor post process the "http" builder just before it is returned (enables to
142143
* override anything from the auto-configuration) spring-addons client properties}
144+
* @param csrfPostProcessor Optional hook to customize the csrf token repository
143145
* @return a security filter-chain scoped to specified security-matchers and adapted to OAuth2
144146
* clients
145147
* @throws Exception in case of miss-configuration
@@ -156,6 +158,7 @@ SecurityFilterChain springAddonsClientFilterChain(HttpSecurity http,
156158
InvalidSessionStrategy invalidSessionStrategy, LogoutSuccessHandler logoutSuccessHandler,
157159
SpringAddonsOidcProperties addonsProperties,
158160
ClientExpressionInterceptUrlRegistryPostProcessor authorizePostProcessor,
161+
Optional<CookieCsrfTokenRepositoryPostProcessor> csrfPostProcessor,
159162
ClientSynchronizedHttpSecurityPostProcessor httpPostProcessor, BeanFactory beanFactory)
160163
throws Exception {
161164
// @formatter:off
@@ -194,7 +197,7 @@ SecurityFilterChain springAddonsClientFilterChain(HttpSecurity http,
194197
}
195198

196199
ServletConfigurationSupport.configureClient(http, serverProperties, addonsProperties,
197-
authorizePostProcessor, httpPostProcessor);
200+
authorizePostProcessor, httpPostProcessor, csrfPostProcessor);
198201

199202
return http.build();
200203
}

0 commit comments

Comments
 (0)