feat(ui/auth): redirect to /login when the session times out
Previously a user idling past the 30-minute cookie expiry stayed parked on a stale page until they tried to navigate. The auth cookie's UTC expiry is now also stamped onto an expires_at claim at sign-in, and a SessionExpiry component mounted in MainLayout schedules a delay until expiry + 2s grace, then force-loads /login — at which point the standard cookie middleware confirms the session is gone and serves the login page.
This commit is contained in:
@@ -44,12 +44,15 @@ public static class AuthEndpoints
|
|||||||
// Map LDAP groups to roles
|
// Map LDAP groups to roles
|
||||||
var roleMappingResult = await roleMapper.MapGroupsToRolesAsync(authResult.Groups ?? []);
|
var roleMappingResult = await roleMapper.MapGroupsToRolesAsync(authResult.Groups ?? []);
|
||||||
|
|
||||||
|
var expiresAt = DateTimeOffset.UtcNow.AddMinutes(30);
|
||||||
|
|
||||||
// Build claims from LDAP auth + role mapping
|
// Build claims from LDAP auth + role mapping
|
||||||
var claims = new List<Claim>
|
var claims = new List<Claim>
|
||||||
{
|
{
|
||||||
new(ClaimTypes.Name, authResult.Username ?? username),
|
new(ClaimTypes.Name, authResult.Username ?? username),
|
||||||
new(JwtTokenService.DisplayNameClaimType, authResult.DisplayName ?? username),
|
new(JwtTokenService.DisplayNameClaimType, authResult.DisplayName ?? username),
|
||||||
new(JwtTokenService.UsernameClaimType, authResult.Username ?? username),
|
new(JwtTokenService.UsernameClaimType, authResult.Username ?? username),
|
||||||
|
new("expires_at", expiresAt.ToUnixTimeSeconds().ToString()),
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var role in roleMappingResult.Roles)
|
foreach (var role in roleMappingResult.Roles)
|
||||||
@@ -74,7 +77,7 @@ public static class AuthEndpoints
|
|||||||
new AuthenticationProperties
|
new AuthenticationProperties
|
||||||
{
|
{
|
||||||
IsPersistent = true,
|
IsPersistent = true,
|
||||||
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(30)
|
ExpiresUtc = expiresAt
|
||||||
});
|
});
|
||||||
|
|
||||||
context.Response.Redirect("/");
|
context.Response.Redirect("/");
|
||||||
|
|||||||
@@ -25,3 +25,5 @@
|
|||||||
@* Global host for IDialogService. One instance per layout renders all confirm/prompt
|
@* Global host for IDialogService. One instance per layout renders all confirm/prompt
|
||||||
dialogs raised via IDialogService.ConfirmAsync / PromptAsync. *@
|
dialogs raised via IDialogService.ConfirmAsync / PromptAsync. *@
|
||||||
<DialogHost />
|
<DialogHost />
|
||||||
|
|
||||||
|
<SessionExpiry />
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
@implements IDisposable
|
||||||
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
|
@inject NavigationManager Navigation
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private CancellationTokenSource? _cts;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
var auth = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
|
if (auth.User.Identity?.IsAuthenticated != true) return;
|
||||||
|
|
||||||
|
var claim = auth.User.FindFirst("expires_at")?.Value;
|
||||||
|
if (!long.TryParse(claim, out var unix)) return;
|
||||||
|
|
||||||
|
var remaining = DateTimeOffset.FromUnixTimeSeconds(unix) - DateTimeOffset.UtcNow;
|
||||||
|
if (remaining <= TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
Navigation.NavigateTo("/login", forceLoad: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
_ = ScheduleRedirectAsync(remaining + TimeSpan.FromSeconds(2), _cts.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ScheduleRedirectAsync(TimeSpan delay, CancellationToken token)
|
||||||
|
{
|
||||||
|
try { await Task.Delay(delay, token); }
|
||||||
|
catch (TaskCanceledException) { return; }
|
||||||
|
await InvokeAsync(() => Navigation.NavigateTo("/login", forceLoad: true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_cts?.Cancel();
|
||||||
|
_cts?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user