fix(central-ui): resolve CentralUI-020..025 — auth-ping idle logout, DebugView race, push-handler disposal guard, JS-interop catch narrowing, claim-constant helper, SessionExpiry tests
This commit is contained in:
@@ -134,9 +134,35 @@ public static class AuthEndpoints
|
||||
context.Response.Redirect("/login");
|
||||
});
|
||||
|
||||
// CentralUI-020: liveness probe for the client-side idle-logout check.
|
||||
// The Blazor circuit's CookieAuthenticationStateProvider serves a frozen
|
||||
// constructor-time principal (CentralUI-004), so a circuit can never
|
||||
// observe a server-side cookie expiry by polling the auth state.
|
||||
// SessionExpiry instead polls this endpoint via fetch(): being a normal
|
||||
// HTTP request, the cookie middleware re-validates (and slides) the
|
||||
// cookie on every hit. It deliberately does NOT use RequireAuthorization
|
||||
// — that would make the middleware answer a lapsed request with a 302 to
|
||||
// /login, which fetch() follows transparently and reads as a 200 login
|
||||
// page. Allowing anonymous access and returning 200/401 ourselves gives
|
||||
// the client an unambiguous expiry signal.
|
||||
endpoints.MapGet("/auth/ping", HandlePing);
|
||||
|
||||
return endpoints;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for <c>GET /auth/ping</c>. Returns <c>200</c> while the caller's
|
||||
/// cookie session is still valid and <c>401</c> once it has lapsed
|
||||
/// server-side. See CentralUI-020.
|
||||
/// </summary>
|
||||
public static Task HandlePing(HttpContext context)
|
||||
{
|
||||
context.Response.StatusCode = context.User.Identity?.IsAuthenticated == true
|
||||
? StatusCodes.Status200OK
|
||||
: StatusCodes.Status401Unauthorized;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the <see cref="AuthenticationProperties"/> for the login sign-in.
|
||||
/// CentralUI-005: deliberately does <b>not</b> set <see cref="AuthenticationProperties.ExpiresUtc"/>.
|
||||
|
||||
43
src/ScadaLink.CentralUI/Auth/ClaimsPrincipalExtensions.cs
Normal file
43
src/ScadaLink.CentralUI/Auth/ClaimsPrincipalExtensions.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using ScadaLink.Security;
|
||||
|
||||
namespace ScadaLink.CentralUI.Auth;
|
||||
|
||||
/// <summary>
|
||||
/// Claim-lookup helpers for the Central UI. CentralUI-024: claim types are owned
|
||||
/// by <see cref="JwtTokenService"/> (the single source of truth). These helpers
|
||||
/// resolve them through the <c>JwtTokenService</c> constants so a rename there
|
||||
/// propagates here instead of silently breaking ten copy-pasted call sites.
|
||||
/// </summary>
|
||||
public static class ClaimsPrincipalExtensions
|
||||
{
|
||||
/// <summary>Fallback returned when no username claim is present.</summary>
|
||||
public const string UnknownUser = "unknown";
|
||||
|
||||
/// <summary>
|
||||
/// The audit username for <paramref name="principal"/>, or
|
||||
/// <see cref="UnknownUser"/> when the claim is absent.
|
||||
/// </summary>
|
||||
public static string GetUsername(this ClaimsPrincipal principal)
|
||||
=> principal.FindFirst(JwtTokenService.UsernameClaimType)?.Value ?? UnknownUser;
|
||||
|
||||
/// <summary>
|
||||
/// The display name for <paramref name="principal"/>, or <c>null</c> when
|
||||
/// the claim is absent.
|
||||
/// </summary>
|
||||
public static string? GetDisplayName(this ClaimsPrincipal principal)
|
||||
=> principal.FindFirst(JwtTokenService.DisplayNameClaimType)?.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the current user's audit username from the auth state provider.
|
||||
/// Replaces the <c>GetCurrentUserAsync</c> helper that was copy-pasted into
|
||||
/// ten components (CentralUI-024).
|
||||
/// </summary>
|
||||
public static async Task<string> GetCurrentUsernameAsync(
|
||||
this AuthenticationStateProvider authStateProvider)
|
||||
{
|
||||
var authState = await authStateProvider.GetAuthenticationStateAsync();
|
||||
return authState.User.GetUsername();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user