diff --git a/src/main/asciidoc/enums.adoc b/src/main/asciidoc/enums.adoc
index f201c5f35..6794a434c 100644
--- a/src/main/asciidoc/enums.adoc
+++ b/src/main/asciidoc/enums.adoc
@@ -26,6 +26,32 @@ Require client to present authentication, if not presented then negotiations wil
+++
|===
+[[CookieSameSite]]
+== CookieSameSite
+
+++++
+ Represents the Cookie SameSite policy to be used. For more info https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_cookies.
+++++
+'''
+
+[cols=">25%,75%"]
+[frame="topbot"]
+|===
+^|Name | Description
+|[[NONE]]`NONE`|+++
+The browser will send cookies with both cross-site requests and same-site requests.
++++
+|[[STRICT]]`STRICT`|+++
+The browser will only send cookies for same-site requests (requests originating from the site that set the cookie).
+ If the request originated from a different URL than the URL of the current location, none of the cookies tagged
+ with the Strict attribute will be included.
++++
+|[[LAX]]`LAX`|+++
+Same-site cookies are withheld on cross-site subrequests, such as calls to load images or frames, but will be sent
+ when a user navigates to the URL from an external site; for example, by following a link.
++++
+|===
+
[[DnsResponseCode]]
== DnsResponseCode
diff --git a/src/main/asciidoc/http.adoc b/src/main/asciidoc/http.adoc
index 96cc36b3f..ca0204e65 100644
--- a/src/main/asciidoc/http.adoc
+++ b/src/main/asciidoc/http.adoc
@@ -349,6 +349,19 @@ browser can store them.
Cookies are described by instances of {@link io.vertx.core.http.Cookie}. This allows you to retrieve the name,
value, domain, path and other normal cookie properties.
+Same Site Cookies let servers require that a cookie shouldn't be sent with cross-site (where Site is defined by the
+registrable domain) requests, which provides some protection against cross-site request forgery attacks. This kind
+of cookies are enabled using the setter: {@link io.vertx.core.http.Cookie#setSameSite(CookieSameSite)}.
+
+Same site cookies can have one of 3 values:
+
+* None - The browser will send cookies with both cross-site requests and same-site requests.
+* Strict - he browser will only send cookies for same-site requests (requests originating from the site that set the
+ cookie). If the request originated from a different URL than the URL of the current location, none of the cookies
+ tagged with the Strict attribute will be included.
+* Lax - Same-site cookies are withheld on cross-site subrequests, such as calls to load images or frames, but will be
+ sent when a user navigates to the URL from an external site; for example, by following a link.
+
Here's an example of querying and adding cookies:
[source,$lang]
diff --git a/src/main/java/io/vertx/core/http/Cookie.java b/src/main/java/io/vertx/core/http/Cookie.java
index d290c098a..5603fbb3c 100644
--- a/src/main/java/io/vertx/core/http/Cookie.java
+++ b/src/main/java/io/vertx/core/http/Cookie.java
@@ -118,6 +118,15 @@ public interface Cookie {
@Fluent
Cookie setHttpOnly(boolean httpOnly);
+ /**
+ * Sets the same site of this cookie.
+ *
+ * @param policy The policy should be one of {@link CookieSameSite}.
+ * @return a reference to this, so the API can be used fluently
+ */
+ @Fluent
+ Cookie setSameSite(CookieSameSite policy);
+
/**
* Encode the cookie to a string. This is what is used in the Set-Cookie header
*
diff --git a/src/main/java/io/vertx/core/http/CookieSameSite.java b/src/main/java/io/vertx/core/http/CookieSameSite.java
new file mode 100644
index 000000000..8aeb1dd04
--- /dev/null
+++ b/src/main/java/io/vertx/core/http/CookieSameSite.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ * which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ */
+package io.vertx.core.http;
+
+import io.vertx.codegen.annotations.VertxGen;
+
+/**
+ * Represents the Cookie SameSite policy to be used. For more info https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_cookies.
+ *
+ * @author Paulo Lopes
+ */
+@VertxGen
+public enum CookieSameSite {
+
+ /**
+ * The browser will send cookies with both cross-site requests and same-site requests.
+ */
+ NONE("None"),
+
+ /**
+ * The browser will only send cookies for same-site requests (requests originating from the site that set the cookie).
+ * If the request originated from a different URL than the URL of the current location, none of the cookies tagged
+ * with the Strict attribute will be included.
+ */
+ STRICT("Strict"),
+
+ /**
+ * Same-site cookies are withheld on cross-site subrequests, such as calls to load images or frames, but will be sent
+ * when a user navigates to the URL from an external site; for example, by following a link.
+ */
+ LAX("Lax");
+
+ /**
+ * Just use a human friendly label instead of the capitalized name.
+ */
+ private final String label;
+
+ CookieSameSite(String label) {
+ this.label = label;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+}
diff --git a/src/main/java/io/vertx/core/http/impl/CookieImpl.java b/src/main/java/io/vertx/core/http/impl/CookieImpl.java
index 4ba08d4df..649c4cb6d 100644
--- a/src/main/java/io/vertx/core/http/impl/CookieImpl.java
+++ b/src/main/java/io/vertx/core/http/impl/CookieImpl.java
@@ -15,6 +15,7 @@ import io.netty.handler.codec.http.cookie.DefaultCookie;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
import io.vertx.core.http.Cookie;
+import io.vertx.core.http.CookieSameSite;
import java.util.HashMap;
import java.util.Map;
@@ -63,6 +64,8 @@ public class CookieImpl implements ServerCookie {
private final io.netty.handler.codec.http.cookie.Cookie nettyCookie;
private boolean changed;
private boolean fromUserAgent;
+ // extension features
+ private CookieSameSite sameSite;
public CookieImpl(String name, String value) {
this.nettyCookie = new DefaultCookie(name, value);
@@ -136,9 +139,20 @@ public class CookieImpl implements ServerCookie {
return this;
}
+ @Override
+ public Cookie setSameSite(final CookieSameSite sameSite) {
+ this.sameSite = sameSite;
+ this.changed = true;
+ return this;
+ }
+
@Override
public String encode() {
- return ServerCookieEncoder.STRICT.encode(nettyCookie);
+ if (sameSite != null) {
+ return ServerCookieEncoder.STRICT.encode(nettyCookie) + "; SameSite=" + sameSite.toString();
+ } else {
+ return ServerCookieEncoder.STRICT.encode(nettyCookie);
+ }
}
public boolean isChanged() {
diff --git a/src/test/java/io/vertx/core/http/HttpTest.java b/src/test/java/io/vertx/core/http/HttpTest.java
index 83503c789..afdc1da76 100644
--- a/src/test/java/io/vertx/core/http/HttpTest.java
+++ b/src/test/java/io/vertx/core/http/HttpTest.java
@@ -5656,6 +5656,37 @@ public abstract class HttpTest extends HttpTestBase {
assertEquals("foo=bar; Path=/somepath; Domain=foo.com; Secure; HTTPOnly", cookie.encode());
}
+ @Test
+ public void testCookieSameSiteFieldEncoding() throws Exception {
+ Cookie cookie = Cookie.cookie("foo", "bar").setSameSite(CookieSameSite.LAX);
+ assertEquals("foo", cookie.getName());
+ assertEquals("bar", cookie.getValue());
+ assertEquals("foo=bar; SameSite=Lax", cookie.encode());
+
+ cookie.setSecure(true);
+ assertEquals("foo=bar; Secure; SameSite=Lax", cookie.encode());
+ cookie.setHttpOnly(true);
+ assertEquals("foo=bar; Secure; HTTPOnly; SameSite=Lax", cookie.encode());
+ }
+
+ @Test
+ public void testCookieSameSiteFieldValidation() throws Exception {
+ Cookie cookie = Cookie.cookie("foo", "bar");
+
+ try {
+ cookie.setSameSite(CookieSameSite.LAX);
+ // OK
+ cookie.setSameSite(CookieSameSite.STRICT);
+ // OK
+ cookie.setSameSite(CookieSameSite.NONE);
+ // OK
+ cookie.setSameSite(null);
+ // OK
+ } catch (RuntimeException e) {
+ fail();
+ }
+ }
+
@Test
public void testRemoveCookies() throws Exception {
testCookies("foo=bar", req -> {