d38356efdb
Host infrastructure (WP-11–17): - StartupValidator with 19 validation rules - /health/ready endpoint with DB + Akka health checks - Akka.NET bootstrap via AkkaHostedService (HOCON config, cluster, remoting, SBR) - Serilog with SiteId/NodeHostname/NodeRole enrichment - DeadLetterMonitorActor with count tracking - CoordinatedShutdown wiring (no Environment.Exit) - Windows Service support (UseWindowsService) Central UI (WP-18–21): - Blazor Server shell with Bootstrap 5, role-aware NavMenu - Login/logout flow (LDAP auth → JWT → HTTP-only cookie) - CookieAuthenticationStateProvider with idle timeout - LDAP group mapping CRUD page (Admin role) - Route guards with Authorize attributes per role - SignalR reconnection overlay for failover Integration tests (WP-22): - Startup validation, auth flow, audit transactions, readiness gating 186 tests pass (1 skipped: LDAP integration), zero warnings.
57 lines
1.9 KiB
C#
57 lines
1.9 KiB
C#
using System.Security.Claims;
|
|
using Microsoft.AspNetCore.Components.Authorization;
|
|
using Microsoft.AspNetCore.Components.Server;
|
|
using Microsoft.AspNetCore.Http;
|
|
using ScadaLink.Security;
|
|
|
|
namespace ScadaLink.CentralUI.Auth;
|
|
|
|
/// <summary>
|
|
/// Reads the JWT from an HTTP-only cookie and creates a ClaimsPrincipal for Blazor Server.
|
|
/// This bridges cookie-based auth (set by the login endpoint) with Blazor's auth state.
|
|
/// </summary>
|
|
public class CookieAuthenticationStateProvider : ServerAuthenticationStateProvider
|
|
{
|
|
public const string AuthCookieName = "ScadaLink.Auth";
|
|
|
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
private readonly JwtTokenService _jwtTokenService;
|
|
|
|
public CookieAuthenticationStateProvider(
|
|
IHttpContextAccessor httpContextAccessor,
|
|
JwtTokenService jwtTokenService)
|
|
{
|
|
_httpContextAccessor = httpContextAccessor;
|
|
_jwtTokenService = jwtTokenService;
|
|
}
|
|
|
|
public override Task<AuthenticationState> GetAuthenticationStateAsync()
|
|
{
|
|
var httpContext = _httpContextAccessor.HttpContext;
|
|
if (httpContext == null)
|
|
{
|
|
return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));
|
|
}
|
|
|
|
var token = httpContext.Request.Cookies[AuthCookieName];
|
|
if (string.IsNullOrEmpty(token))
|
|
{
|
|
return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));
|
|
}
|
|
|
|
var principal = _jwtTokenService.ValidateToken(token);
|
|
if (principal == null)
|
|
{
|
|
return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));
|
|
}
|
|
|
|
// Check idle timeout
|
|
if (_jwtTokenService.IsIdleTimedOut(principal))
|
|
{
|
|
return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));
|
|
}
|
|
|
|
return Task.FromResult(new AuthenticationState(principal));
|
|
}
|
|
}
|