fix(cookies): set Identity cookies to SameSite=Lax in dev (avoid Chromium rejection on http://localhost)

Chromium rejects cookies that have SameSite=None but no
Secure flag. The default Identity cookie policy uses
SameSite=None, which is invalid on http://localhost (no
TLS, no Secure). Result on http://localhost:5000:

  Cookie '.AspNetCore.Identity.Application' rejected
  because it has the 'SameSite=None' attribute but is
  missing the 'secure' attribute.

Fix: in Development environment, configure
ConfigureApplicationCookie and ConfigureExternalCookie
to use SameSite=Lax and SameAsRequest SecurePolicy.
Lax is permissive enough for OAuth callbacks (top-level
GET navigations) and avoids the rejection.

Production (https://) is untouched — the default
SameSite=None is correct when Secure is set.

Note on the sameSiteMode reference: SameSiteMode is
defined in two namespaces
(Microsoft.AspNetCore.Http and Microsoft.Net.Http.Headers).
The file already uses 'using Microsoft.Net.Http.Headers;'
so a bare 'SameSiteMode' is ambiguous. Using the
fully-qualified name 'Microsoft.AspNetCore.Http.SameSiteMode'
to disambiguate, no new using needed.

Tested: dotnet build OK, dotnet test 11/11 green.
This commit is contained in:
Paul Schneider 2026-06-14 16:27:54 +01:00
commit ec5c1b6a95

View file

@ -170,6 +170,24 @@ public static class HostingExtensions
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>(); services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>>();
// Dev-only: Chromium rejects SameSite=None without Secure on http://
// (e.g. http://localhost:5000). The default Identity cookie policy
// sets SameSite=None, which is invalid without Secure. Force Lax in
// dev. In production (https://) the default SameSite=None is fine.
if (builder.Environment.IsDevelopment())
{
services.ConfigureApplicationCookie(options =>
{
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
});
services.ConfigureExternalCookie(options =>
{
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Lax;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
});
}
return identityBuilder; return identityBuilder;
} }