fix(security): make auth cookie name configurable, override per env
The auth cookie name was hardcoded to ZB.MOM.WW.ScadaBridge.Auth. Because browser cookies are scoped by host+path but NOT by port, two ScadaBridge clusters on the same host (the local docker stack on localhost:9000 and docker-env2 on localhost:9100) shared one cookie jar: signing into one overwrote the other's cookie, and since the clusters use different JWT signing keys + separate Data Protection key rings, the overwritten side could no longer validate its cookie and the session died. Add SecurityOptions.CookieName (default = canonical ZB.MOM.WW.ScadaBridge.Auth, blank falls back to the default) applied via the SecurityOptions-bound cookie PostConfigure. Override it to ...Auth.env2 in both docker-env2 Central nodes so the two local clusters no longer collide; the primary cluster keeps the default so its live sessions and production are unaffected. Adds 3 Security.Tests cases.
This commit is contained in:
@@ -515,6 +515,72 @@ public class SecurityReviewRegressionTests
|
||||
Assert.True(cookieOptions.SlidingExpiration);
|
||||
}
|
||||
|
||||
// --- Configurable cookie name: two ScadaBridge environments sharing a hostname
|
||||
// (browser cookies are scoped by host+path, NOT port) must be able to use
|
||||
// distinct cookie names so signing into one does not clobber the other's session. ---
|
||||
|
||||
[Fact]
|
||||
public void AddSecurity_AuthCookie_DefaultsToCanonicalName()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddDataProtection();
|
||||
services.AddSecurity();
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
var cookieOptions = provider
|
||||
.GetRequiredService<IOptionsMonitor<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>>()
|
||||
.Get(Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
// Unconfigured deployments (incl. production and the primary docker cluster) keep the
|
||||
// canonical name, so existing sessions survive and only an explicit override diverges.
|
||||
Assert.Equal(SecurityOptions.DefaultCookieName, cookieOptions.Cookie.Name);
|
||||
Assert.Equal("ZB.MOM.WW.ScadaBridge.Auth", cookieOptions.Cookie.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddSecurity_AuthCookie_CookieNameIsConfigurable()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddDataProtection();
|
||||
services.AddSecurity();
|
||||
// The per-environment override the docker-env2 cluster uses to avoid clobbering the
|
||||
// primary cluster's cookie on localhost.
|
||||
services.Configure<SecurityOptions>(o => o.CookieName = "ZB.MOM.WW.ScadaBridge.Auth.env2");
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
var cookieOptions = provider
|
||||
.GetRequiredService<IOptionsMonitor<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>>()
|
||||
.Get(Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
Assert.Equal("ZB.MOM.WW.ScadaBridge.Auth.env2", cookieOptions.Cookie.Name);
|
||||
// The name override must be purely additive — it must not reset the hardened defaults.
|
||||
Assert.True(cookieOptions.Cookie.HttpOnly);
|
||||
Assert.Equal(Microsoft.AspNetCore.Http.SameSiteMode.Strict, cookieOptions.Cookie.SameSite);
|
||||
Assert.True(cookieOptions.SlidingExpiration);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void AddSecurity_AuthCookie_BlankCookieName_FallsBackToDefault(string blank)
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddLogging();
|
||||
services.AddDataProtection();
|
||||
services.AddSecurity();
|
||||
services.Configure<SecurityOptions>(o => o.CookieName = blank);
|
||||
|
||||
using var provider = services.BuildServiceProvider();
|
||||
var cookieOptions = provider
|
||||
.GetRequiredService<IOptionsMonitor<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>>()
|
||||
.Get(Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
||||
// A blank value must never produce an unnamed cookie.
|
||||
Assert.Equal(SecurityOptions.DefaultCookieName, cookieOptions.Cookie.Name);
|
||||
}
|
||||
|
||||
// --- Security-001: transport security (now owned by the shared LdapOptions) ---
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user