ASP.NET Core 2.1 Razor Pages SameSite cookie sample

This sample targets .NET Framework Targeted

ASP.NET Core 2.1 has built-in support for the SameSite attribute, but it was written to the original standard. The patched behavior changed the meaning of SameSite.None to emit the sameSite attribute with a value of None, rather than not emit the value at all. If you want to not emit the value you can set the SameSite property on a cookie to -1.

ASP.NET Core Identity is largely unaffected by SameSite cookies except for advanced scenarios like IFrames or OpenIdConnect integration.

When using Identity, do not add any cookie providers or call services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme), Identity takes care of that.

Writing the SameSite attribute

The following code is an example of how to write a SameSite attribute on a cookie:

var cookieOptions = new CookieOptions
{
    // Set the secure flag, which Chrome's changes will require for SameSite none.
    // Note this will also require you to be running on HTTPS
    Secure = true,

    // Set the cookie to HTTP only which is good practice unless you really do need
    // to access it client side in scripts.
    HttpOnly = true,

    // Add the SameSite attribute, this will emit the attribute with a value of none.
    // To not emit the attribute at all set the SameSite property to (SameSiteMode)(-1).
    SameSite = SameSiteMode.None
};

// Add the cookie to the response cookie collection
Response.Cookies.Append(CookieName, "cookieValue", cookieOptions);

Cookie authentication, session state and various other components set their sameSite options via Cookie options, for example

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.Cookie.SameSite = SameSiteMode.None;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.IsEssential = true;
    });

services.AddSession(options =>
{
    options.Cookie.SameSite = SameSiteMode.None;
    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
    options.Cookie.IsEssential = true;
});

In the preceding code, both cookie authentication and session state set their sameSite attribute to None, emitting the attribute with a None value, and also set the Secure attribute to true.

Run the sample

If you run the sample project, load your browser debugger on the initial page and use it to view the cookie collection for the site. To do so in Edge and Chrome press F12 then select the Application tab and click the site URL under the Cookies option in the Storage section.

Browser Debugger Cookie List

You can see from the image above that the cookie created by the sample when you click the "Create SameSite Cookie" button has a SameSite attribute value of Lax, matching the value set in the sample code.

Intercepting cookies

In order to intercept cookies, to adjust the none value according to its support in the user's browser agent you must use the CookiePolicy middleware. This must be placed into the http request pipeline before any components that write cookies and configured within ConfigureServices().

To insert it into the pipeline use app.UseCookiePolicy() in the Configure(IApplicationBuilder, IHostingEnvironment) method in your Startup.cs. For example

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
       app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseAuthentication();
    app.UseSession();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Then in the ConfigureServices(IServiceCollection services) configure the cookie policy to call out to a helper class when cookies are appended or deleted, like so;

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
        options.OnAppendCookie = cookieContext =>
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
        options.OnDeleteCookie = cookieContext =>
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    });
}

private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        if (SameSite.BrowserDetection.DisallowsSameSiteNone(userAgent))
        {
            options.SameSite = (SameSiteMode)(-1);
        }
    }
}

The helper function CheckSameSite(HttpContext, CookieOptions):

  • Is called when cookies are appended to the request or deleted from the request.
  • Checks to see if the SameSite property is set to None.
  • If SameSite is set to None and the current user agent is known to not support the none attribute value. The check is done using the SameSiteSupport class:
    • Sets SameSite to not emit the value by setting the property to (SameSiteMode)(-1)

Targeting .NET Framework

ASP.NET Core and System.Web (ASP.NET 4.x) have independent implementations of SameSite. The SameSite KB patches for .NET Framework are not required if using ASP.NET Core nor does the System.Web SameSite minimum framework version requirement (.NET Framework 4.7.2) apply to ASP.NET Core.

ASP.NET Core on .NET requires updating NuGet package dependencies to get the appropriate fixes.

To get the ASP.NET Core changes for .NET Framework ensure that you have a direct reference to the patched packages and versions (2.1.14 or later 2.1 versions).

<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.1.14" />
<PackageReference Include="Microsoft.AspNetCore.CookiePolicy" Version="2.1.14" />

More Information

Chrome Updates ASP.NET Core SameSite Documentation ASP.NET Core 2.1 SameSite Change Announcement