feat(auth): add session history retention and lifecycle updates
- Add daily session history cleanup with a 180-day retention period - Run cleanup on startup and every 24 hours in batches of up to 1,000 sessions - Add tests for session retention and derived session status - Allow viewers to manage their own sessions while preserving global access for super admins - Prevent the current browser session from being revoked through session management - Derive active, expired, and revoked status without writing during list requests - Count only active sessions toward the 20-session per-user limit - Remove unused session event queries and payloads from the session listing endpoint - Update the session management UI for the revised access and revocation policies
This commit is contained in:
1 parent
6ce3dd590a
commit
0bd696d37f
12 files changed
+271
-181
No files matched your search
@@ -14,13 +14,35 @@ export function canRevokeManagedSession(input: {
|
||||
actorUserId: string;
|
||||
actorRole: string;
|
||||
targetUserId: string;
|
||||
currentSessionId: string;
|
||||
targetSessionId: string;
|
||||
}): boolean {
|
||||
if (input.currentSessionId === input.targetSessionId) return false;
|
||||
return (
|
||||
input.actorUserId === input.targetUserId ||
|
||||
input.actorRole === "SUPER_ADMIN"
|
||||
);
|
||||
}
|
||||
|
||||
export type ManagedSessionStatus = "active" | "expired" | "revoked";
|
||||
|
||||
export function getManagedSessionStatus(
|
||||
session: {
|
||||
revokedAt: Date | null;
|
||||
revokeReason: string | null;
|
||||
idleExpiresAt: Date;
|
||||
absoluteExpiresAt: Date;
|
||||
},
|
||||
now = new Date(),
|
||||
): ManagedSessionStatus {
|
||||
if (session.revokedAt) {
|
||||
return session.revokeReason === "EXPIRED" ? "expired" : "revoked";
|
||||
}
|
||||
return session.idleExpiresAt <= now || session.absoluteExpiresAt <= now
|
||||
? "expired"
|
||||
: "active";
|
||||
}
|
||||
|
||||
export function getSessionCookieName(env = process.env): string {
|
||||
return env.NODE_ENV === "production"
|
||||
? PRODUCTION_SESSION_COOKIE
|
||||
@@ -120,7 +142,12 @@ export async function createUserSession(input: {
|
||||
});
|
||||
|
||||
const overflowSessions = await prisma.userSession.findMany({
|
||||
where: { userId: input.userId, revokedAt: null },
|
||||
where: {
|
||||
userId: input.userId,
|
||||
revokedAt: null,
|
||||
idleExpiresAt: { gt: now },
|
||||
absoluteExpiresAt: { gt: now },
|
||||
},
|
||||
orderBy: { createdAt: "desc" },
|
||||
skip: MAX_ACTIVE_SESSIONS_PER_USER,
|
||||
select: { id: true },
|
||||
|
||||
Reference in new issue
Block a user