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);
}
}