Add session management: see who's signed in, revoke a session
No visibility existed into who was currently signed in or a way to force a device out. Sessions already live as files via session-file-store, so this reads that store directly rather than adding a new DB table: new Settings-adjacent "Sessions" page (admin-only, alongside Users) lists every live session with the user's name/email/resolved role, IP, a friendly "Browser on OS" summary parsed from the user-agent, last-active time, and expiry, with a Revoke button per row (extra confirmation if you revoke your own current session, since that signs you out immediately). IP and user-agent are now captured into the session at login (auth/router.ts) since express-session doesn't track them itself. session-file-store's own Store type doesn't declare its list() method, so sessionStore.ts adds a narrow local interface for it rather than losing type safety on the rest of the store. Verified against a real session directory seeded through the actual session-file-store APIs (not hand-written JSON): confirmed correct field resolution including a session whose user row was later deleted (role resolves to null instead of crashing), correctly excluded a mid-OIDC-login session with no completed user yet, correctly excluded an already-expired session, and confirmed revoke actually deletes the right session file and only that one. Confirmed the real dev database's mtime was untouched throughout. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1cae35a59e
commit
6d673db9ec
9 files changed
+318
-1
No files matched your search
@@ -18,6 +18,18 @@ export interface UserRecord {
|
||||
lastLoginAt: string | null;
|
||||
}
|
||||
|
||||
export interface SessionSummary {
|
||||
id: string;
|
||||
sub: string;
|
||||
email: string | null;
|
||||
name: string | null;
|
||||
role: UserRole | null;
|
||||
ip: string | null;
|
||||
userAgent: string | null;
|
||||
lastAccess: string;
|
||||
expiresAt: string | null;
|
||||
}
|
||||
|
||||
export interface AuditLogEntry {
|
||||
id: number;
|
||||
actorUserId: number | null;
|
||||
@@ -587,6 +599,10 @@ export const api = {
|
||||
body: JSON.stringify({ role }),
|
||||
}),
|
||||
},
|
||||
sessions: {
|
||||
list: () => request<{ sessions: SessionSummary[]; currentSessionId: string }>("/api/sessions"),
|
||||
revoke: (id: string) => request<void>(`/api/sessions/${encodeURIComponent(id)}`, { method: "DELETE" }),
|
||||
},
|
||||
auditLog: {
|
||||
list: (limit = 200) => request<{ entries: AuditLogEntry[] }>(`/api/audit-log?limit=${limit}`),
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user