73 lines
2.6 KiB
C#
73 lines
2.6 KiB
C#
using System.Net.Http.Json;
|
|
using System.Security.Claims;
|
|
using Microsoft.AspNetCore.Components.Authorization;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Security.Blazor;
|
|
|
|
/// <summary>
|
|
/// Blazor Server <see cref="AuthenticationStateProvider"/> that snapshots the cookie-backed
|
|
/// principal supplied at circuit boot and polls <c>/auth/ping</c> every 60 seconds to detect
|
|
/// expiry. Mirrors ScadaLink's CentralUI implementation.
|
|
/// </summary>
|
|
public sealed class CookieAuthenticationStateProvider : AuthenticationStateProvider, IAsyncDisposable
|
|
{
|
|
private static readonly TimeSpan PingInterval = TimeSpan.FromSeconds(60);
|
|
|
|
private readonly HttpClient _http;
|
|
private readonly ILogger<CookieAuthenticationStateProvider> _logger;
|
|
private readonly CancellationTokenSource _cts = new();
|
|
private ClaimsPrincipal _current;
|
|
private Task? _pingLoop;
|
|
|
|
public CookieAuthenticationStateProvider(
|
|
ClaimsPrincipal initial,
|
|
HttpClient http,
|
|
ILogger<CookieAuthenticationStateProvider> logger)
|
|
{
|
|
_current = initial;
|
|
_http = http;
|
|
_logger = logger;
|
|
}
|
|
|
|
public override Task<AuthenticationState> GetAuthenticationStateAsync()
|
|
{
|
|
_pingLoop ??= Task.Run(() => PingLoopAsync(_cts.Token));
|
|
return Task.FromResult(new AuthenticationState(_current));
|
|
}
|
|
|
|
private async Task PingLoopAsync(CancellationToken ct)
|
|
{
|
|
try
|
|
{
|
|
while (!ct.IsCancellationRequested)
|
|
{
|
|
await Task.Delay(PingInterval, ct).ConfigureAwait(false);
|
|
var resp = await _http.GetAsync("/auth/ping", ct).ConfigureAwait(false);
|
|
if (!resp.IsSuccessStatusCode && _current.Identity?.IsAuthenticated == true)
|
|
{
|
|
_logger.LogInformation("/auth/ping returned {Code}; notifying circuit", (int)resp.StatusCode);
|
|
_current = new ClaimsPrincipal(new ClaimsIdentity());
|
|
NotifyAuthenticationStateChanged(
|
|
Task.FromResult(new AuthenticationState(_current)));
|
|
}
|
|
}
|
|
}
|
|
catch (OperationCanceledException) { /* expected on shutdown */ }
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogWarning(ex, "Auth ping loop terminated unexpectedly");
|
|
}
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
_cts.Cancel();
|
|
if (_pingLoop is not null)
|
|
{
|
|
try { await _pingLoop.ConfigureAwait(false); } catch { /* swallow shutdown errors */ }
|
|
}
|
|
_cts.Dispose();
|
|
}
|
|
}
|