using System.Security.Claims; using Microsoft.AspNetCore.Http; using ScadaLink.CentralUI.Auth; namespace ScadaLink.CentralUI.Tests.Auth; /// /// Regression tests for CentralUI-004. The provider used to read /// on every call; once the Blazor /// circuit is established that context is gone, so later re-evaluations saw an /// unauthenticated principal. The provider must snapshot the principal once at /// construction (during the initial HTTP request) and serve it for the circuit. /// public class CookieAuthenticationStateProviderTests { private static ClaimsPrincipal AuthenticatedUser(string name) { var identity = new ClaimsIdentity( new[] { new Claim(ClaimTypes.Name, name) }, authenticationType: "TestCookie"); return new ClaimsPrincipal(identity); } [Fact] public async Task GetAuthenticationStateAsync_ReturnsAuthenticatedUser_WhenHttpContextPresent() { var accessor = new HttpContextAccessor { HttpContext = new DefaultHttpContext { User = AuthenticatedUser("alice") } }; var provider = new CookieAuthenticationStateProvider(accessor); var state = await provider.GetAuthenticationStateAsync(); Assert.True(state.User.Identity?.IsAuthenticated); Assert.Equal("alice", state.User.Identity?.Name); } [Fact] public async Task GetAuthenticationStateAsync_StillReturnsUser_AfterHttpContextIsGone() { // The circuit is built during the HTTP request: HttpContext is valid then. var accessor = new HttpContextAccessor { HttpContext = new DefaultHttpContext { User = AuthenticatedUser("bob") } }; var provider = new CookieAuthenticationStateProvider(accessor); // After the request completes, IHttpContextAccessor.HttpContext is null for // the life of the long-lived SignalR circuit. accessor.HttpContext = null; var state = await provider.GetAuthenticationStateAsync(); // The pre-fix implementation returned an anonymous principal here. Assert.True(state.User.Identity?.IsAuthenticated); Assert.Equal("bob", state.User.Identity?.Name); } [Fact] public async Task GetAuthenticationStateAsync_IsStableAcrossCalls_IgnoringStaleForeignContext() { var accessor = new HttpContextAccessor { HttpContext = new DefaultHttpContext { User = AuthenticatedUser("carol") } }; var provider = new CookieAuthenticationStateProvider(accessor); // A stale/foreign context leaking through the AsyncLocal accessor must NOT // change what this circuit's provider reports. accessor.HttpContext = new DefaultHttpContext { User = AuthenticatedUser("intruder") }; var first = await provider.GetAuthenticationStateAsync(); var second = await provider.GetAuthenticationStateAsync(); Assert.Equal("carol", first.User.Identity?.Name); Assert.Equal("carol", second.User.Identity?.Name); } }