@@ -69,6 +69,14 @@ public class Cookie {
6969 */
7070 private long maxAge = -1 ;
7171
72+ /**
73+ * Value for the 'SameSite' cookie attribute.
74+ *
75+ * @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite">
76+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite</a>
77+ */
78+ private SameSite sameSite ;
79+
7280 /**
7381 * Creates a response cookie.
7482 *
@@ -97,6 +105,7 @@ private Cookie(@Nonnull Cookie cookie) {
97105 this .path = cookie .path ;
98106 this .secure = cookie .secure ;
99107 this .httpOnly = cookie .httpOnly ;
108+ this .sameSite = cookie .sameSite ;
100109 }
101110
102111 /**
@@ -238,12 +247,19 @@ public boolean isSecure() {
238247 }
239248
240249 /**
241- * Set cookie secure flag..
250+ * Set cookie secure flag.
242251 *
243252 * @param secure Cookie's secure.
244253 * @return This cookie.
254+ * @throws IllegalArgumentException if {@code false} is specified and the 'SameSite'
255+ * attribute value requires a secure cookie.
245256 */
246257 public @ Nonnull Cookie setSecure (boolean secure ) {
258+ if (sameSite != null && sameSite .requiresSecure () && !secure ) {
259+ throw new IllegalArgumentException ("Cookies with SameSite=" + sameSite .getValue ()
260+ + " must be flagged as Secure. Call Cookie.setSameSite(...) with an argument"
261+ + " allowing non-secure cookies before calling Cookie.setSecure(false)." );
262+ }
247263 this .secure = secure ;
248264 return this ;
249265 }
@@ -297,6 +313,60 @@ public long getMaxAge() {
297313 return this ;
298314 }
299315
316+ /**
317+ * Returns the value for the 'SameSite' parameter.
318+ * <ul>
319+ * <li>{@link SameSite#LAX} - Cookies are allowed to be sent with top-level navigations and
320+ * will be sent along with GET request initiated by third party website. This is the default
321+ * value in modern browsers.</li>
322+ * <li>{@link SameSite#STRICT} - Cookies will only be sent in a first-party context and not be
323+ * sent along with requests initiated by third party websites.</li>
324+ * <li>{@link SameSite#NONE} - Cookies will be sent in all contexts, i.e sending cross-origin
325+ * is allowed. Requires the {@code Secure} attribute in latest browser versions.</li>
326+ * <li>{@code null} - Not specified.</li>
327+ * </ul>
328+ *
329+ * @return the value for 'SameSite' parameter.
330+ * @see #setSecure(boolean)
331+ * @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite">
332+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite</a>
333+ */
334+ @ Nullable
335+ public SameSite getSameSite () {
336+ return sameSite ;
337+ }
338+
339+ /**
340+ * Sets the value for the 'SameSite' parameter.
341+ * <ul>
342+ * <li>{@link SameSite#LAX} - Cookies are allowed to be sent with top-level navigations and
343+ * will be sent along with GET request initiated by third party website. This is the default
344+ * value in modern browsers.</li>
345+ * <li>{@link SameSite#STRICT} - Cookies will only be sent in a first-party context and not be
346+ * sent along with requests initiated by third party websites.</li>
347+ * <li>{@link SameSite#NONE} - Cookies will be sent in all contexts, i.e sending cross-origin
348+ * is allowed. Requires the {@code Secure} attribute in latest browser versions.</li>
349+ * <li>{@code null} - Not specified.</li>
350+ * </ul>
351+ *
352+ * @param sameSite the value for the 'SameSite' parameter.
353+ * @return this instance.
354+ * @throws IllegalArgumentException if a value requiring a secure cookie is specified and this
355+ * cookie is not flagged as secure.
356+ * @see #setSecure(boolean)
357+ * @see <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite">
358+ * https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite</a>
359+ */
360+ public Cookie setSameSite (@ Nullable SameSite sameSite ) {
361+ if (sameSite != null && sameSite .requiresSecure () && !isSecure ()) {
362+ throw new IllegalArgumentException ("Cookies with SameSite=" + sameSite .getValue ()
363+ + " must be flagged as Secure. Call Cookie.setSecure(true)"
364+ + " before calling Cookie.setSameSite(...)." );
365+ }
366+ this .sameSite = sameSite ;
367+ return this ;
368+ }
369+
300370 @ Override public String toString () {
301371 StringBuilder buff = new StringBuilder ();
302372 buff .append (name ).append ("=" );
@@ -334,6 +404,12 @@ public long getMaxAge() {
334404 append (sb , domain );
335405 }
336406
407+ // SameSite
408+ if (sameSite != null ) {
409+ sb .append (";SameSite=" );
410+ append (sb , sameSite .getValue ());
411+ }
412+
337413 // Secure
338414 if (secure ) {
339415 sb .append (";Secure" );
@@ -491,6 +567,8 @@ public long getMaxAge() {
491567 value (conf , namespace + ".httpOnly" , Config ::getBoolean , cookie ::setHttpOnly );
492568 value (conf , namespace + ".maxAge" , (c , path ) -> c .getDuration (path , TimeUnit .SECONDS ),
493569 cookie ::setMaxAge );
570+ value (conf , namespace + ".sameSite" , (c , path ) -> SameSite .of (c .getString (path )),
571+ cookie ::setSameSite );
494572 return Optional .of (cookie );
495573 }
496574 return Optional .empty ();
0 commit comments