diff --git a/shared/Microsoft.AspNetCore.ChunkingCookieManager.Sources/ChunkingCookieManager.cs b/shared/Microsoft.AspNetCore.ChunkingCookieManager.Sources/ChunkingCookieManager.cs
index 16426507c..9b602383c 100644
--- a/shared/Microsoft.AspNetCore.ChunkingCookieManager.Sources/ChunkingCookieManager.cs
+++ b/shared/Microsoft.AspNetCore.ChunkingCookieManager.Sources/ChunkingCookieManager.cs
@@ -33,7 +33,7 @@ internal class ChunkingCookieManager
///
/// The default maximum size of characters in a cookie to send back to the client.
///
- public const int DefaultChunkSize = 4070;
+ public const int DefaultChunkSize = 4050;
private const string ChunkKeySuffix = "C";
private const string ChunkCountPrefix = "chunks-";
@@ -42,7 +42,7 @@ public ChunkingCookieManager()
{
// Lowest common denominator. Safari has the lowest known limit (4093), and we leave little extra just in case.
// See http://browsercookielimits.x64.me/.
- // Leave at least 20 in case CookiePolicy tries to add 'secure' and/or 'httponly'.
+ // Leave at least 40 in case CookiePolicy tries to add 'secure', 'samesite=strict' and/or 'httponly'.
ChunkSize = DefaultChunkSize;
ThrowForPartialCookies = true;
}
@@ -166,6 +166,7 @@ public void AppendResponseCookie(HttpContext context, string key, string value,
{
Domain = options.Domain,
Expires = options.Expires,
+ SameSite = (Net.Http.Headers.SameSiteMode)options.SameSite,
HttpOnly = options.HttpOnly,
Path = options.Path,
Secure = options.Secure,
@@ -284,6 +285,7 @@ public void DeleteCookie(HttpContext context, string key, CookieOptions options)
{
Path = options.Path,
Domain = options.Domain,
+ SameSite = options.SameSite,
Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
});
@@ -297,6 +299,7 @@ public void DeleteCookie(HttpContext context, string key, CookieOptions options)
{
Path = options.Path,
Domain = options.Domain,
+ SameSite = options.SameSite,
Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc),
});
}
diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs
index 5a4ffc547..13a20e55b 100644
--- a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs
@@ -179,6 +179,7 @@ private CookieOptions BuildCookieOptions()
var cookieOptions = new CookieOptions
{
Domain = Options.CookieDomain,
+ SameSite = Options.CookieSameSite,
HttpOnly = Options.CookieHttpOnly,
Path = Options.CookiePath ?? (OriginalPathBase.HasValue ? OriginalPathBase.ToString() : "/"),
};
diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs
index 9b6b51d8e..02d0361b7 100644
--- a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs
@@ -4,7 +4,6 @@
using System;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Authentication.Cookies
{
@@ -23,6 +22,7 @@ public CookieAuthenticationOptions()
ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
ExpireTimeSpan = TimeSpan.FromDays(14);
SlidingExpiration = true;
+ CookieSameSite = SameSiteMode.Strict;
CookieHttpOnly = true;
CookieSecure = CookieSecurePolicy.SameAsRequest;
Events = new CookieAuthenticationEvents();
@@ -57,6 +57,12 @@ public string CookieName
///
public string CookiePath { get; set; }
+ ///
+ /// Determines if the browser should allow the cookie to be attached to same-site or cross-site requests. The
+ /// default is Strict, which means the cookie is only allowed to be attached to same-site requests.
+ ///
+ public SameSiteMode CookieSameSite { get; set; }
+
///
/// Determines if the browser should allow the cookie to be accessed by client-side javascript. The
/// default is true, which means the cookie will only be passed to http requests and is not made available
diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs
index d51c324dc..04359142a 100644
--- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs
+++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs
@@ -899,6 +899,7 @@ private void WriteNonceCookie(string nonce)
new CookieOptions
{
HttpOnly = true,
+ SameSite = Http.SameSiteMode.Lax,
Secure = Request.IsHttps,
Expires = Clock.UtcNow.Add(Options.ProtocolValidator.NonceLifetime)
});
@@ -930,6 +931,7 @@ private string ReadNonceCookie(string nonce)
var cookieOptions = new CookieOptions
{
HttpOnly = true,
+ SameSite = Http.SameSiteMode.Lax,
Secure = Request.IsHttps
};
diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs
index 7ae9f973c..ff054d3c6 100644
--- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs
@@ -83,6 +83,7 @@ protected override async Task HandleRemoteAuthenticateAsync(
var cookieOptions = new CookieOptions
{
HttpOnly = true,
+ SameSite = SameSiteMode.Lax,
Secure = Request.IsHttps
};
@@ -160,6 +161,7 @@ protected override async Task HandleUnauthorizedAsync(ChallengeContext context)
var cookieOptions = new CookieOptions
{
HttpOnly = true,
+ SameSite = SameSiteMode.Lax,
Secure = Request.IsHttps,
Expires = Clock.UtcNow.Add(Options.RemoteAuthenticationTimeout),
};
diff --git a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs
index bf875a7a0..a7de95dfe 100644
--- a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs
+++ b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs
@@ -203,6 +203,7 @@ protected virtual void GenerateCorrelationId(AuthenticationProperties properties
var cookieOptions = new CookieOptions
{
HttpOnly = true,
+ SameSite = SameSiteMode.Lax,
Secure = Request.IsHttps,
Expires = Clock.UtcNow.Add(Options.RemoteAuthenticationTimeout),
};
@@ -242,6 +243,7 @@ protected virtual bool ValidateCorrelationId(AuthenticationProperties properties
var cookieOptions = new CookieOptions
{
HttpOnly = true,
+ SameSite = SameSiteMode.Lax,
Secure = Request.IsHttps
};
Response.Cookies.Delete(cookieName, cookieOptions);
diff --git a/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs b/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs
index 46daaad81..92adac967 100644
--- a/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs
+++ b/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs
@@ -74,7 +74,7 @@ public IResponseCookies Cookies
private bool PolicyRequiresCookieOptions()
{
- return Policy.HttpOnly != HttpOnlyPolicy.None || Policy.Secure != CookieSecurePolicy.None;
+ return Policy.MinimumSameSitePolicy != SameSiteMode.None || Policy.HttpOnly != HttpOnlyPolicy.None || Policy.Secure != CookieSecurePolicy.None;
}
public void Append(string key, string value)
@@ -151,6 +151,22 @@ private void ApplyPolicy(CookieOptions options)
default:
throw new InvalidOperationException();
}
+ switch (Policy.MinimumSameSitePolicy)
+ {
+ case SameSiteMode.None:
+ break;
+ case SameSiteMode.Lax:
+ if (options.SameSite == SameSiteMode.None)
+ {
+ options.SameSite = SameSiteMode.Lax;
+ }
+ break;
+ case SameSiteMode.Strict:
+ options.SameSite = SameSiteMode.Strict;
+ break;
+ default:
+ throw new InvalidOperationException($"Unrecognized {nameof(SameSiteMode)} value {Policy.MinimumSameSitePolicy.ToString()}");
+ }
switch (Policy.HttpOnly)
{
case HttpOnlyPolicy.Always:
@@ -159,7 +175,7 @@ private void ApplyPolicy(CookieOptions options)
case HttpOnlyPolicy.None:
break;
default:
- throw new InvalidOperationException();
+ throw new InvalidOperationException($"Unrecognized {nameof(HttpOnlyPolicy)} value {Policy.HttpOnly.ToString()}");
}
}
}
diff --git a/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyOptions.cs b/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyOptions.cs
index 6aed18bfb..7203e73e6 100644
--- a/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyOptions.cs
+++ b/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyOptions.cs
@@ -12,6 +12,11 @@ namespace Microsoft.AspNetCore.Builder
///
public class CookiePolicyOptions
{
+ ///
+ /// Affects the cookie's same site attribute.
+ ///
+ public SameSiteMode MinimumSameSitePolicy { get; set; } = SameSiteMode.Strict;
+
///
/// Affects whether cookies must be HttpOnly.
///
diff --git a/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs
index 419d82493..e38dc1587 100644
--- a/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs
+++ b/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs
@@ -136,6 +136,7 @@ public async Task SignInCausesDefaultCookieToBeCreated()
Assert.StartsWith("TestCookie=", setCookie);
Assert.Contains("; path=/", setCookie);
Assert.Contains("; httponly", setCookie);
+ Assert.Contains("; samesite=", setCookie);
Assert.DoesNotContain("; expires=", setCookie);
Assert.DoesNotContain("; domain=", setCookie);
Assert.DoesNotContain("; secure", setCookie);
@@ -206,6 +207,7 @@ public async Task CookieOptionsAlterSetCookieHeader()
o.CookiePath = "/foo";
o.CookieDomain = "another.com";
o.CookieSecure = CookieSecurePolicy.Always;
+ o.CookieSameSite = SameSiteMode.None;
o.CookieHttpOnly = true;
}, SignInAsAlice, baseAddress: new Uri("http://example.com/base"));
@@ -217,12 +219,14 @@ public async Task CookieOptionsAlterSetCookieHeader()
Assert.Contains(" path=/foo", setCookie1);
Assert.Contains(" domain=another.com", setCookie1);
Assert.Contains(" secure", setCookie1);
+ Assert.DoesNotContain(" samesite", setCookie1);
Assert.Contains(" httponly", setCookie1);
var server2 = CreateServer(o =>
{
o.CookieName = "SecondCookie";
o.CookieSecure = CookieSecurePolicy.None;
+ o.CookieSameSite = SameSiteMode.Strict;
o.CookieHttpOnly = false;
}, SignInAsAlice, baseAddress: new Uri("http://example.com/base"));
@@ -232,6 +236,7 @@ public async Task CookieOptionsAlterSetCookieHeader()
Assert.Contains("SecondCookie=", setCookie2);
Assert.Contains(" path=/base", setCookie2);
+ Assert.Contains(" samesite=strict", setCookie2);
Assert.DoesNotContain(" domain=", setCookie2);
Assert.DoesNotContain(" secure", setCookie2);
Assert.DoesNotContain(" httponly", setCookie2);
diff --git a/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/CookieChunkingTests.cs b/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/CookieChunkingTests.cs
index c978d169e..143e1d254 100644
--- a/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/CookieChunkingTests.cs
+++ b/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/CookieChunkingTests.cs
@@ -18,7 +18,7 @@ public void AppendLargeCookie_Appended()
new ChunkingCookieManager() { ChunkSize = null }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
var values = context.Response.Headers["Set-Cookie"];
Assert.Equal(1, values.Count);
- Assert.Equal("TestCookie=" + testString + "; path=/", values[0]);
+ Assert.Equal("TestCookie=" + testString + "; path=/; samesite=lax", values[0]);
}
[Fact]
@@ -27,20 +27,20 @@ public void AppendLargeCookieWithLimit_Chunked()
HttpContext context = new DefaultHttpContext();
string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- new ChunkingCookieManager() { ChunkSize = 30 }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
+ new ChunkingCookieManager() { ChunkSize = 44 }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions());
var values = context.Response.Headers["Set-Cookie"];
Assert.Equal(9, values.Count);
Assert.Equal(new[]
{
- "TestCookie=chunks-8; path=/",
- "TestCookieC1=abcdefgh; path=/",
- "TestCookieC2=ijklmnop; path=/",
- "TestCookieC3=qrstuvwx; path=/",
- "TestCookieC4=yz012345; path=/",
- "TestCookieC5=6789ABCD; path=/",
- "TestCookieC6=EFGHIJKL; path=/",
- "TestCookieC7=MNOPQRST; path=/",
- "TestCookieC8=UVWXYZ; path=/",
+ "TestCookie=chunks-8; path=/; samesite=lax",
+ "TestCookieC1=abcdefgh; path=/; samesite=lax",
+ "TestCookieC2=ijklmnop; path=/; samesite=lax",
+ "TestCookieC3=qrstuvwx; path=/; samesite=lax",
+ "TestCookieC4=yz012345; path=/; samesite=lax",
+ "TestCookieC5=6789ABCD; path=/; samesite=lax",
+ "TestCookieC6=EFGHIJKL; path=/; samesite=lax",
+ "TestCookieC7=MNOPQRST; path=/; samesite=lax",
+ "TestCookieC8=UVWXYZ; path=/; samesite=lax",
}, values);
}
@@ -116,14 +116,14 @@ public void DeleteChunkedCookieWithOptions_AllDeleted()
Assert.Equal(8, cookies.Count);
Assert.Equal(new[]
{
- "TestCookie=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
- "TestCookieC1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
- "TestCookieC2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
- "TestCookieC3=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
- "TestCookieC4=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
- "TestCookieC5=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
- "TestCookieC6=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
- "TestCookieC7=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/",
+ "TestCookie=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
+ "TestCookieC1=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
+ "TestCookieC2=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
+ "TestCookieC3=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
+ "TestCookieC4=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
+ "TestCookieC5=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
+ "TestCookieC6=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
+ "TestCookieC7=; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=foo.com; path=/; samesite=lax",
}, cookies);
}
}
diff --git a/test/Microsoft.AspNetCore.CookiePolicy.Test/CookiePolicyTests.cs b/test/Microsoft.AspNetCore.CookiePolicy.Test/CookiePolicyTests.cs
index e45c7f690..737c12dc3 100644
--- a/test/Microsoft.AspNetCore.CookiePolicy.Test/CookiePolicyTests.cs
+++ b/test/Microsoft.AspNetCore.CookiePolicy.Test/CookiePolicyTests.cs
@@ -36,6 +36,15 @@ public class CookiePolicyTests
context.Response.Cookies.Append("D", "D", new CookieOptions { HttpOnly = true });
return Task.FromResult(0);
};
+ private RequestDelegate SameSiteCookieAppends = context =>
+ {
+ context.Response.Cookies.Append("A", "A");
+ context.Response.Cookies.Append("B", "B", new CookieOptions { SameSite = Http.SameSiteMode.None });
+ context.Response.Cookies.Append("C", "C", new CookieOptions());
+ context.Response.Cookies.Append("D", "D", new CookieOptions { SameSite = Http.SameSiteMode.Lax });
+ context.Response.Cookies.Append("E", "E", new CookieOptions { SameSite = Http.SameSiteMode.Strict });
+ return Task.FromResult(0);
+ };
[Fact]
public async Task SecureAlwaysSetsSecure()
@@ -50,10 +59,10 @@ await RunTest("/secureAlways",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
- Assert.Equal("A=A; path=/; secure", transaction.SetCookie[0]);
- Assert.Equal("B=B; path=/; secure", transaction.SetCookie[1]);
- Assert.Equal("C=C; path=/; secure", transaction.SetCookie[2]);
- Assert.Equal("D=D; path=/; secure", transaction.SetCookie[3]);
+ Assert.Equal("A=A; path=/; secure; samesite=strict", transaction.SetCookie[0]);
+ Assert.Equal("B=B; path=/; secure; samesite=strict", transaction.SetCookie[1]);
+ Assert.Equal("C=C; path=/; secure; samesite=strict", transaction.SetCookie[2]);
+ Assert.Equal("D=D; path=/; secure; samesite=strict", transaction.SetCookie[3]);
}));
}
@@ -70,10 +79,10 @@ await RunTest("/secureNone",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
- Assert.Equal("A=A; path=/", transaction.SetCookie[0]);
- Assert.Equal("B=B; path=/", transaction.SetCookie[1]);
- Assert.Equal("C=C; path=/", transaction.SetCookie[2]);
- Assert.Equal("D=D; path=/; secure", transaction.SetCookie[3]);
+ Assert.Equal("A=A; path=/; samesite=strict", transaction.SetCookie[0]);
+ Assert.Equal("B=B; path=/; samesite=strict", transaction.SetCookie[1]);
+ Assert.Equal("C=C; path=/; samesite=strict", transaction.SetCookie[2]);
+ Assert.Equal("D=D; path=/; secure; samesite=strict", transaction.SetCookie[3]);
}));
}
@@ -90,19 +99,19 @@ await RunTest("/secureSame",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
- Assert.Equal("A=A; path=/", transaction.SetCookie[0]);
- Assert.Equal("B=B; path=/", transaction.SetCookie[1]);
- Assert.Equal("C=C; path=/", transaction.SetCookie[2]);
- Assert.Equal("D=D; path=/", transaction.SetCookie[3]);
+ Assert.Equal("A=A; path=/; samesite=strict", transaction.SetCookie[0]);
+ Assert.Equal("B=B; path=/; samesite=strict", transaction.SetCookie[1]);
+ Assert.Equal("C=C; path=/; samesite=strict", transaction.SetCookie[2]);
+ Assert.Equal("D=D; path=/; samesite=strict", transaction.SetCookie[3]);
}),
new RequestTest("https://example.com/secureSame",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
- Assert.Equal("A=A; path=/; secure", transaction.SetCookie[0]);
- Assert.Equal("B=B; path=/; secure", transaction.SetCookie[1]);
- Assert.Equal("C=C; path=/; secure", transaction.SetCookie[2]);
- Assert.Equal("D=D; path=/; secure", transaction.SetCookie[3]);
+ Assert.Equal("A=A; path=/; secure; samesite=strict", transaction.SetCookie[0]);
+ Assert.Equal("B=B; path=/; secure; samesite=strict", transaction.SetCookie[1]);
+ Assert.Equal("C=C; path=/; secure; samesite=strict", transaction.SetCookie[2]);
+ Assert.Equal("D=D; path=/; secure; samesite=strict", transaction.SetCookie[3]);
}));
}
@@ -119,10 +128,10 @@ await RunTest("/httpOnlyAlways",
transaction =>
{
Assert.NotNull(transaction.SetCookie);
- Assert.Equal("A=A; path=/; httponly", transaction.SetCookie[0]);
- Assert.Equal("B=B; path=/; httponly", transaction.SetCookie[1]);
- Assert.Equal("C=C; path=/; httponly", transaction.SetCookie[2]);
- Assert.Equal("D=D; path=/; httponly", transaction.SetCookie[3]);
+ Assert.Equal("A=A; path=/; samesite=strict; httponly", transaction.SetCookie[0]);
+ Assert.Equal("B=B; path=/; samesite=strict; httponly", transaction.SetCookie[1]);
+ Assert.Equal("C=C; path=/; samesite=strict; httponly", transaction.SetCookie[2]);
+ Assert.Equal("D=D; path=/; samesite=strict; httponly", transaction.SetCookie[3]);
}));
}
@@ -137,12 +146,75 @@ await RunTest("/httpOnlyNone",
HttpCookieAppends,
new RequestTest("http://example.com/httpOnlyNone",
transaction =>
+ {
+ Assert.NotNull(transaction.SetCookie);
+ Assert.Equal("A=A; path=/; samesite=strict", transaction.SetCookie[0]);
+ Assert.Equal("B=B; path=/; samesite=strict", transaction.SetCookie[1]);
+ Assert.Equal("C=C; path=/; samesite=strict", transaction.SetCookie[2]);
+ Assert.Equal("D=D; path=/; samesite=strict; httponly", transaction.SetCookie[3]);
+ }));
+ }
+
+ [Fact]
+ public async Task SameSiteStrictSetsItAlways()
+ {
+ await RunTest("/sameSiteStrict",
+ new CookiePolicyOptions
+ {
+ MinimumSameSitePolicy = Http.SameSiteMode.Strict
+ },
+ SameSiteCookieAppends,
+ new RequestTest("http://example.com/sameSiteStrict",
+ transaction =>
+ {
+ Assert.NotNull(transaction.SetCookie);
+ Assert.Equal("A=A; path=/; samesite=strict", transaction.SetCookie[0]);
+ Assert.Equal("B=B; path=/; samesite=strict", transaction.SetCookie[1]);
+ Assert.Equal("C=C; path=/; samesite=strict", transaction.SetCookie[2]);
+ Assert.Equal("D=D; path=/; samesite=strict", transaction.SetCookie[3]);
+ Assert.Equal("E=E; path=/; samesite=strict", transaction.SetCookie[4]);
+ }));
+ }
+
+ [Fact]
+ public async Task SameSiteLaxSetsItAlways()
+ {
+ await RunTest("/sameSiteLax",
+ new CookiePolicyOptions
+ {
+ MinimumSameSitePolicy = Http.SameSiteMode.Lax
+ },
+ SameSiteCookieAppends,
+ new RequestTest("http://example.com/sameSiteLax",
+ transaction =>
+ {
+ Assert.NotNull(transaction.SetCookie);
+ Assert.Equal("A=A; path=/; samesite=lax", transaction.SetCookie[0]);
+ Assert.Equal("B=B; path=/; samesite=lax", transaction.SetCookie[1]);
+ Assert.Equal("C=C; path=/; samesite=lax", transaction.SetCookie[2]);
+ Assert.Equal("D=D; path=/; samesite=lax", transaction.SetCookie[3]);
+ Assert.Equal("E=E; path=/; samesite=strict", transaction.SetCookie[4]);
+ }));
+ }
+
+ [Fact]
+ public async Task SameSiteNoneLeavesItAlone()
+ {
+ await RunTest("/sameSiteNone",
+ new CookiePolicyOptions
+ {
+ MinimumSameSitePolicy = Http.SameSiteMode.None
+ },
+ SameSiteCookieAppends,
+ new RequestTest("http://example.com/sameSiteNone",
+ transaction =>
{
Assert.NotNull(transaction.SetCookie);
Assert.Equal("A=A; path=/", transaction.SetCookie[0]);
Assert.Equal("B=B; path=/", transaction.SetCookie[1]);
- Assert.Equal("C=C; path=/", transaction.SetCookie[2]);
- Assert.Equal("D=D; path=/; httponly", transaction.SetCookie[3]);
+ Assert.Equal("C=C; path=/; samesite=lax", transaction.SetCookie[2]);
+ Assert.Equal("D=D; path=/; samesite=lax", transaction.SetCookie[3]);
+ Assert.Equal("E=E; path=/; samesite=strict", transaction.SetCookie[4]);
}));
}
@@ -170,10 +242,10 @@ public async Task CookiePolicyCanHijackAppend()
var transaction = await server.SendAsync("http://example.com/login");
Assert.NotNull(transaction.SetCookie);
- Assert.Equal("Hao=Hao; path=/", transaction.SetCookie[0]);
- Assert.Equal("Hao=Hao; path=/", transaction.SetCookie[1]);
- Assert.Equal("Hao=Hao; path=/", transaction.SetCookie[2]);
- Assert.Equal("Hao=Hao; path=/; secure", transaction.SetCookie[3]);
+ Assert.Equal("Hao=Hao; path=/; samesite=strict", transaction.SetCookie[0]);
+ Assert.Equal("Hao=Hao; path=/; samesite=strict", transaction.SetCookie[1]);
+ Assert.Equal("Hao=Hao; path=/; samesite=strict", transaction.SetCookie[2]);
+ Assert.Equal("Hao=Hao; path=/; secure; samesite=strict", transaction.SetCookie[3]);
}
[Fact]
@@ -201,7 +273,7 @@ public async Task CookiePolicyCanHijackDelete()
Assert.NotNull(transaction.SetCookie);
Assert.Equal(1, transaction.SetCookie.Count);
- Assert.Equal("A=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/", transaction.SetCookie[0]);
+ Assert.Equal("A=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=lax", transaction.SetCookie[0]);
}
[Fact]