using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using ZB.MOM.WW.MxGateway.Server; using ZB.MOM.WW.MxGateway.Server.Dashboard; namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard; public sealed class DashboardCookieOptionsTests { /// /// Verifies that the application configures secure dashboard authentication cookies. /// With RequireHttpsCookie defaulting to and no explicit /// CookieName override, the cookie is named with the __Host- prefix so /// browsers enforce the Secure/no-Domain/Path=/ guarantees the prefix promises. /// /// A task that represents the asynchronous operation. [Fact] public async Task Build_ConfiguresSecureDashboardCookie() { await using WebApplication app = GatewayApplication.Build([]); IOptionsMonitor optionsMonitor = app.Services .GetRequiredService>(); CookieAuthenticationOptions options = optionsMonitor.Get( DashboardAuthenticationDefaults.AuthenticationScheme); Assert.Equal(DashboardAuthenticationDefaults.SecureCookieName, options.Cookie.Name); Assert.True(options.Cookie.HttpOnly); Assert.Equal(CookieSecurePolicy.Always, options.Cookie.SecurePolicy); Assert.Equal(SameSiteMode.Strict, options.Cookie.SameSite); Assert.Equal("/", options.Cookie.Path); Assert.Equal("/login", options.LoginPath); Assert.Equal("/logout", options.LogoutPath); Assert.Equal("/denied", options.AccessDeniedPath); } /// /// Verifies that setting MxGateway:Dashboard:RequireHttpsCookie=false /// relaxes the cookie to so /// the dashboard can be reached over plain HTTP in dev, and that the plain /// is used rather than the /// __Host- name — a __Host- cookie without a guaranteed Secure flag is /// silently dropped by browsers. /// /// A task that represents the asynchronous operation. [Fact] public async Task Build_WithRequireHttpsCookieFalse_UsesSameAsRequestAndPlainName() { await using WebApplication app = GatewayApplication.Build( ["--MxGateway:Dashboard:RequireHttpsCookie=false"]); IOptionsMonitor optionsMonitor = app.Services .GetRequiredService>(); CookieAuthenticationOptions options = optionsMonitor.Get( DashboardAuthenticationDefaults.AuthenticationScheme); Assert.Equal(CookieSecurePolicy.SameAsRequest, options.Cookie.SecurePolicy); Assert.Equal(DashboardAuthenticationDefaults.CookieName, options.Cookie.Name); } /// /// Verifies that an explicit MxGateway:Dashboard:CookieName override wins over the /// secure __Host- default (this build leaves RequireHttpsCookie at its /// default), so a gateway instance sharing a hostname with another /// can be given a distinct name (browser cookies are scoped by host+path, not port). /// /// A task that represents the asynchronous operation. [Fact] public async Task Build_WithCookieNameOverride_UsesConfiguredName() { await using WebApplication app = GatewayApplication.Build( ["--MxGateway:Dashboard:CookieName=MxGatewayDashboard.env2"]); IOptionsMonitor optionsMonitor = app.Services .GetRequiredService>(); CookieAuthenticationOptions options = optionsMonitor.Get( DashboardAuthenticationDefaults.AuthenticationScheme); Assert.Equal("MxGatewayDashboard.env2", options.Cookie.Name); } }