Files
jobtrackingapp/JobTrackerApi/Services/LocalSessionValidator.cs
T
cesnimda 842e793f69
CI and Deploy / test (pull_request) Successful in 5m18s
CI and Deploy / deploy (pull_request) Has been skipped
feat(account): add deletion lifecycle
2026-08-15 19:03:54 +02:00

36 lines
1.7 KiB
C#

using System.Security.Claims;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using Microsoft.EntityFrameworkCore;
namespace JobTrackerApi.Services;
// The actual revocation check behind Program.cs's "local" JwtBearer OnTokenValidated. Pulled out
// of Program.cs so it's unit-testable without standing up a full TestServer/HTTP pipeline.
public static class LocalSessionValidator
{
public static async Task<bool> IsValidAsync(JobTrackerContext db, ClaimsPrincipal? principal, DateTimeOffset now, bool requireConfirmedEmail = false, CancellationToken cancellationToken = default)
{
var sid = principal?.FindFirst("sid")?.Value;
var userId = principal is null ? null : LocalAuthIdentity.GetRequiredUserId(principal);
// Fail closed: see the comment on the OnTokenValidated wiring in Program.cs for why a
// missing sid is rejected rather than grandfathered in.
if (string.IsNullOrWhiteSpace(sid) || string.IsNullOrWhiteSpace(userId)) return false;
var session = await db.UserSessions.IgnoreQueryFilters()
.FirstOrDefaultAsync(x => x.Id == sid && x.UserId == userId, cancellationToken);
if (session is null || session.RevokedAtUtc is not null || session.ExpiresAtUtc <= now) return false;
var user = await db.Users.IgnoreQueryFilters().AsNoTracking().FirstOrDefaultAsync(x => x.Id == userId, cancellationToken);
if (user is null || user.DeletionStatus != AccountDeletionStatuses.Active) return false;
if (requireConfirmedEmail && !user.EmailConfirmed) return false;
if (now - session.LastSeenAtUtc > TimeSpan.FromMinutes(5))
{
session.LastSeenAtUtc = now;
await db.SaveChangesAsync(cancellationToken);
}
return true;
}
}