From 784182716931600fd32e36181ee2ef73d89b262e Mon Sep 17 00:00:00 2001 From: Alonso Araya Calvo Date: Fri, 24 Jun 2022 14:40:14 -0600 Subject: [PATCH] Adds the ability to set the CSRF Token cookie max age value Closes gh-11432 --- .../csrf/CookieServerCsrfTokenRepository.java | 33 +++++++++++++++++-- .../CookieServerCsrfTokenRepositoryTests.java | 15 ++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java b/web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java index bc3a20e711..68fac2fdad 100644 --- a/web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java +++ b/web/src/main/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepository.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import org.springframework.web.server.ServerWebExchange; * * @author Eric Deandrea * @author Thomas Vitale + * @author Alonso Araya * @since 5.1 */ public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRepository { @@ -57,6 +58,8 @@ public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRep private Boolean secure; + private int cookieMaxAge = -1; + /** * Factory method to conveniently create an instance that has * {@link #setCookieHttpOnly(boolean)} set to false. @@ -83,7 +86,7 @@ public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRep .from(this.cookieName, tokenValue) .domain(this.cookieDomain) .httpOnly(this.cookieHttpOnly) - .maxAge(!tokenValue.isEmpty() ? -1 : 0) + .maxAge(!tokenValue.isEmpty() ? this.cookieMaxAge : 0) .path((this.cookiePath != null) ? this.cookiePath : getRequestContext(exchange.getRequest())) .secure((this.secure != null) ? this.secure : (exchange.getRequest().getSslInfo() != null)) .build(); @@ -164,6 +167,32 @@ public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRep this.secure = secure; } + /** + * Sets maximum age in seconds for the cookie that the expected CSRF token is saved to + * and read from. By default maximum age value is -1. + * + *

+ * A positive value indicates that the cookie will expire after that many seconds have + * passed. Note that the value is the maximum age when the cookie will expire, + * not the cookie's current age. + * + *

+ * A negative value means that the cookie is not stored persistently and will be + * deleted when the Web browser exits. + * + *

+ * A zero value causes the cookie to be deleted immediately therefore it is not a + * valid value and in that case an {@link IllegalArgumentException} will be thrown. + * @param cookieMaxAge an integer specifying the maximum age of the cookie in seconds; + * if negative, means the cookie is not stored; if zero, the method throws an + * {@link IllegalArgumentException} + * @since 5.8 + */ + public void setCookieMaxAge(int cookieMaxAge) { + Assert.isTrue(cookieMaxAge != 0, "cookieMaxAge cannot be zero"); + this.cookieMaxAge = cookieMaxAge; + } + private CsrfToken createCsrfToken() { return createCsrfToken(createNewToken()); } diff --git a/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java b/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java index a1711dc201..b190642158 100644 --- a/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java +++ b/web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Eric Deandrea * @author Thomas Vitale + * @author Alonso Araya * @since 5.1 */ public class CookieServerCsrfTokenRepositoryTests { @@ -113,6 +114,12 @@ public class CookieServerCsrfTokenRepositoryTests { saveAndAssertExpectedValues(createToken()); } + @Test + public void saveTokenWhenCookieMaxAgeThenCookieMaxAge() { + setExpectedCookieMaxAge(3600); + saveAndAssertExpectedValues(createToken()); + } + @Test public void saveTokenWhenCustomPropertiesThenCustomProperties() { setExpectedDomain("spring.io"); @@ -120,6 +127,7 @@ public class CookieServerCsrfTokenRepositoryTests { setExpectedPath("/some/path"); setExpectedHeaderName("headerName"); setExpectedParameterName("paramName"); + setExpectedCookieMaxAge(3600); saveAndAssertExpectedValues(createToken()); } @@ -235,6 +243,11 @@ public class CookieServerCsrfTokenRepositoryTests { this.csrfTokenRepository.setCookieName(expectedCookieName); } + private void setExpectedCookieMaxAge(int expectedCookieMaxAge) { + this.csrfTokenRepository.setCookieMaxAge(expectedCookieMaxAge); + this.expectedMaxAge = Duration.ofSeconds(expectedCookieMaxAge); + } + private void setExpectedCookieValue(String expectedCookieValue) { this.expectedCookieValue = expectedCookieValue; }