diff --git a/src/Nancy/Cookies/INancyCookie.cs b/src/Nancy/Cookies/INancyCookie.cs index 31989b93cc..a36b50745f 100644 --- a/src/Nancy/Cookies/INancyCookie.cs +++ b/src/Nancy/Cookies/INancyCookie.cs @@ -52,5 +52,10 @@ public interface INancyCookie /// Whether the cookie is secure (i.e. HTTPS only) /// bool Secure { get; } + + /// + /// Wheather the cookie is same site + /// + SameSite? SameSite { get; } } -} \ No newline at end of file +} diff --git a/src/Nancy/Cookies/NancyCookie.cs b/src/Nancy/Cookies/NancyCookie.cs index 4e4ee00b8b..091b44a8b2 100644 --- a/src/Nancy/Cookies/NancyCookie.cs +++ b/src/Nancy/Cookies/NancyCookie.cs @@ -30,7 +30,7 @@ public NancyCookie(string name, string value) /// The value of the cookie. /// The expiration date of the cookie. Can be if it should expire at the end of the session. public NancyCookie(string name, string value, DateTime expires) - : this(name, value, false, false, expires) + : this(name, value, false, false, expires, null) { } @@ -42,7 +42,7 @@ public NancyCookie(string name, string value, DateTime expires) /// The value of the cookie. /// Whether the cookie is http only. public NancyCookie(string name, string value, bool httpOnly) - : this(name, value, httpOnly, false, null) + : this(name, value, httpOnly, false, null, null) { } @@ -55,7 +55,7 @@ public NancyCookie(string name, string value, bool httpOnly) /// Whether the cookie is http only. /// Whether the cookie is secure (i.e. HTTPS only). public NancyCookie(string name, string value, bool httpOnly, bool secure) - : this(name, value, httpOnly, secure, null) + : this(name, value, httpOnly, secure, null, null) { } @@ -68,13 +68,15 @@ public NancyCookie(string name, string value, bool httpOnly, bool secure) /// Whether the cookie is http only. /// Whether the cookie is secure (i.e. HTTPS only). /// The expiration date of the cookie. Can be if it should expire at the end of the session. - public NancyCookie(string name, string value, bool httpOnly, bool secure, DateTime? expires) + /// The same site attribute of the cookie. Can be . + public NancyCookie(string name, string value, bool httpOnly, bool secure, DateTime? expires, SameSite? sameSite = null) { this.Name = name; this.Value = value; this.HttpOnly = httpOnly; this.Secure = secure; this.Expires = expires; + this.SameSite = sameSite; } /// @@ -135,6 +137,11 @@ public string EncodedValue /// public bool Secure { get; private set; } + /// + /// Wheather the cookie is same site + /// + public SameSite? SameSite { get; set; } + /// /// Returns a that represents this instance. /// @@ -169,7 +176,12 @@ public override string ToString() sb.Append("; HttpOnly"); } + if(SameSite.HasValue) + { + sb.Append("; samesite=").Append(SameSite.ToString()); + } + return sb.ToString(); } } -} \ No newline at end of file +} diff --git a/src/Nancy/Cookies/SameSite.cs b/src/Nancy/Cookies/SameSite.cs new file mode 100644 index 0000000000..9477dc40e5 --- /dev/null +++ b/src/Nancy/Cookies/SameSite.cs @@ -0,0 +1,29 @@ +namespace Nancy.Cookies +{ + /// + /// Represents the SameSite for NancyCookie + /// + public enum SameSite + { + /// + /// If the value is invalid, the cookie will only be sent along with + /// "same-site" requests. If the value is "Lax", the cookie will be + /// sent with "same-site" requests, and with "cross-site" top-level navigations + /// + Lax = 0, + + /// + /// If you set SameSite to Strict, your cookie will only be sent in a + /// first-party context. In user terms, the cookie will only be sent + /// if the site for the cookie matches the site currently shown in + /// the browser's URL bar. + /// + Strict, + + /// + /// Cookies with SameSite=None must also specify Secure, + /// meaning they require a secure context. + /// + None + } +} diff --git a/test/Nancy.Tests/Unit/NancyCookieFixture.cs b/test/Nancy.Tests/Unit/NancyCookieFixture.cs index fb8ac9b93f..a916763f62 100644 --- a/test/Nancy.Tests/Unit/NancyCookieFixture.cs +++ b/test/Nancy.Tests/Unit/NancyCookieFixture.cs @@ -162,6 +162,16 @@ public void Should_encode_value_if_necessary() result.ShouldEqual("Value+with+spaces"); } + [Fact] + public void Should_add_same_site_if_set() + { + // When + var cookie = new NancyCookie("leto", "worm") { SameSite = SameSite.Lax }.ToString(); + + // Then + cookie.ShouldEqual("leto=worm; path=/; samesite=Lax"); + } + public static string GetInvariantAbbreviatedMonthName(DateTime dateTime) { return CultureInfo.InvariantCulture.DateTimeFormat.AbbreviatedMonthNames[dateTime.Month - 1]; @@ -173,4 +183,4 @@ public static string GetInvariantAbbreviatedWeekdayName(DateTime dateTime) } } -} \ No newline at end of file +}