Skip to content

Commit ccbc4f9

Browse files
filiphrwilkinsona
authored andcommitted
Add support for omitting SameSite attribute from session cookie
See gh-44714 Signed-off-by: Filip Hrisafov <filip.hrisafov@gmail.com>
1 parent 9920b5f commit ccbc4f9

File tree

8 files changed

+52
-12
lines changed

8 files changed

+52
-12
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebSessionIdResolverAutoConfiguration.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,7 @@ private void initializeCookie(ResponseCookieBuilder builder) {
7777
map.from(cookie::getSecure).to(builder::secure);
7878
map.from(cookie::getMaxAge).to(builder::maxAge);
7979
map.from(cookie::getPartitioned).to(builder::partitioned);
80-
map.from(getSameSite(cookie)).to(builder::sameSite);
81-
}
82-
83-
private String getSameSite(Cookie properties) {
84-
SameSite sameSite = properties.getSameSite();
85-
return (sameSite != null) ? sameSite.attributeValue() : null;
80+
map.from(cookie::getSameSite).as(SameSite::attributeValue).to(builder::sameSite);
8681
}
8782

8883
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/session/SessionAutoConfigurationTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,16 @@ void sessionCookieConfigurationIsAppliedToAutoConfiguredCookieSerializer() {
170170
});
171171
}
172172

173+
@Test
174+
void sessionCookieSameSiteOmittedIsAppliedToAutoConfiguredCookieSerializer() {
175+
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)
176+
.withPropertyValues("server.servlet.session.cookie.sameSite=omitted")
177+
.run((context) -> {
178+
DefaultCookieSerializer cookieSerializer = context.getBean(DefaultCookieSerializer.class);
179+
assertThat(cookieSerializer).hasFieldOrPropertyWithValue("sameSite", null);
180+
});
181+
}
182+
173183
@Test
174184
void autoConfiguredCookieSerializerIsUsedBySessionRepositoryFilter() {
175185
this.contextRunner.withUserConfiguration(SessionRepositoryConfiguration.class)

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,15 @@ void customSessionCookieConfigurationShouldBeApplied() {
676676
}));
677677
}
678678

679+
@Test
680+
void sessionCookieOmittedConfigurationShouldBeApplied() {
681+
this.contextRunner.withPropertyValues("server.reactive.session.cookie.same-site:omitted")
682+
.run(assertExchangeWithSession((exchange) -> {
683+
List<ResponseCookie> cookies = exchange.getResponse().getCookies().get("SESSION");
684+
assertThat(cookies).extracting(ResponseCookie::getSameSite).containsOnlyNulls();
685+
}));
686+
}
687+
679688
@ParameterizedTest
680689
@ValueSource(classes = { ServerProperties.class, WebFluxProperties.class })
681690
void propertiesAreNotEnabledInNonWebApplication(Class<?> propertiesClass) {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ protected final void configureWebAppContext(WebAppContext context, ServletContex
284284
private void configureSession(WebAppContext context) {
285285
SessionHandler handler = context.getSessionHandler();
286286
SameSite sessionSameSite = getSession().getCookie().getSameSite();
287-
if (sessionSameSite != null) {
287+
if (sessionSameSite != null && sessionSameSite != SameSite.OMITTED) {
288288
handler.setSameSite(HttpCookie.SameSite.valueOf(sessionSameSite.name()));
289289
}
290290
Duration sessionTimeout = getSession().getTimeout();

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -998,11 +998,12 @@ private static class SuppliedSameSiteCookieProcessor extends Rfc6265CookieProces
998998
@Override
999999
public String generateHeader(Cookie cookie, HttpServletRequest request) {
10001000
SameSite sameSite = getSameSite(cookie);
1001-
if (sameSite == null) {
1001+
String sameSiteValue = (sameSite != null) ? sameSite.attributeValue() : null;
1002+
if (sameSiteValue == null) {
10021003
return super.generateHeader(cookie, request);
10031004
}
10041005
Rfc6265CookieProcessor delegate = new Rfc6265CookieProcessor();
1005-
delegate.setSameSiteCookies(sameSite.attributeValue());
1006+
delegate.setSameSiteCookies(sameSiteValue);
10061007
return delegate.generateHeader(cookie, request);
10071008
}
10081009

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/UndertowServletWebServerFactory.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,10 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
635635
private void beforeCommit(HttpServerExchange exchange) {
636636
for (Cookie cookie : exchange.responseCookies()) {
637637
SameSite sameSite = getSameSite(asServletCookie(cookie));
638-
if (sameSite != null) {
638+
if (sameSite == SameSite.OMITTED) {
639+
cookie.setSameSite(false);
640+
}
641+
else if (sameSite != null) {
639642
cookie.setSameSiteMode(sameSite.attributeValue());
640643
}
641644
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/Cookie.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ public void setPartitioned(Boolean partitioned) {
145145
*/
146146
public enum SameSite {
147147

148+
/**
149+
* The SameSite cookie attribute will be omitted when creating the cookie.
150+
*/
151+
OMITTED(null),
152+
148153
/**
149154
* Cookies are sent in both first-party and cross-origin requests.
150155
*/

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ void sessionCookieConfiguration() {
881881
}
882882

883883
@ParameterizedTest
884-
@EnumSource
884+
@EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "OMITTED")
885885
void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookies(SameSite sameSite) throws Exception {
886886
AbstractServletWebServerFactory factory = getFactory();
887887
factory.getSession().getCookie().setSameSite(sameSite);
@@ -896,7 +896,7 @@ void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookies(S
896896
}
897897

898898
@ParameterizedTest
899-
@EnumSource
899+
@EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "OMITTED")
900900
void sessionCookieSameSiteAttributeCanBeConfiguredAndOnlyAffectsSessionCookiesWhenUsingCustomName(SameSite sameSite)
901901
throws Exception {
902902
AbstractServletWebServerFactory factory = getFactory();
@@ -949,6 +949,23 @@ void cookieSameSiteSuppliersShouldNotAffectSessionCookie() throws IOException, U
949949
(header) -> assertThat(header).contains("test=test").contains("SameSite=Strict"));
950950
}
951951

952+
@Test
953+
void cookieSameSiteSuppliersShouldNotAffectOmittedSameSite() throws IOException, URISyntaxException {
954+
AbstractServletWebServerFactory factory = getFactory();
955+
factory.getSession().getCookie().setSameSite(SameSite.OMITTED);
956+
factory.getSession().getCookie().setName("SESSIONCOOKIE");
957+
factory.addCookieSameSiteSuppliers(CookieSameSiteSupplier.ofStrict());
958+
factory.addInitializers(new ServletRegistrationBean<>(new CookieServlet(false), "/"));
959+
this.webServer = factory.getWebServer();
960+
this.webServer.start();
961+
ClientHttpResponse clientResponse = getClientResponse(getLocalUrl("/"));
962+
assertThat(clientResponse.getStatusCode()).isEqualTo(HttpStatus.OK);
963+
List<String> setCookieHeaders = clientResponse.getHeaders().get("Set-Cookie");
964+
assertThat(setCookieHeaders).satisfiesExactlyInAnyOrder(
965+
(header) -> assertThat(header).contains("SESSIONCOOKIE").doesNotContain("SameSite"),
966+
(header) -> assertThat(header).contains("test=test").contains("SameSite=Strict"));
967+
}
968+
952969
@Test
953970
protected void sslSessionTracking() {
954971
AbstractServletWebServerFactory factory = getFactory();

0 commit comments

Comments
 (0)