forked from jappel/leistungsbilanz-ts
No self-signup: admins create every account in Config -> Benutzer with a typed start password; a forced password change gates first use. Session- cookie auth protects all /api/* routes; /api/users additionally requires the admin role. See docs/authentication.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
|
|
interface Session {
|
|
id: string;
|
|
userId: string;
|
|
expiresAt: number;
|
|
}
|
|
|
|
/**
|
|
* In-memory session store. The API runs as a single container/process, so a
|
|
* restart clearing every session is an acceptable trade-off for the
|
|
* simplicity of not needing a persisted session table.
|
|
*/
|
|
export class SessionStore {
|
|
private readonly sessions = new Map<string, Session>();
|
|
|
|
constructor(private readonly ttlMs: number) {}
|
|
|
|
create(userId: string): Session {
|
|
const session: Session = {
|
|
id: randomUUID(),
|
|
userId,
|
|
expiresAt: Date.now() + this.ttlMs,
|
|
};
|
|
this.sessions.set(session.id, session);
|
|
return session;
|
|
}
|
|
|
|
/** Returns the session and slides its expiry forward, or undefined if gone/expired. */
|
|
get(sessionId: string | undefined, slideExpiry = true): Session | undefined {
|
|
if (!sessionId) return undefined;
|
|
const session = this.sessions.get(sessionId);
|
|
if (!session) return undefined;
|
|
if (session.expiresAt < Date.now()) {
|
|
this.sessions.delete(sessionId);
|
|
return undefined;
|
|
}
|
|
if (slideExpiry) session.expiresAt = Date.now() + this.ttlMs;
|
|
return session;
|
|
}
|
|
|
|
destroy(sessionId: string | undefined): void {
|
|
if (!sessionId) return;
|
|
this.sessions.delete(sessionId);
|
|
}
|
|
|
|
/** Ends every other session for a user, e.g. after a password change. */
|
|
destroyAllForUser(userId: string, exceptSessionId?: string): void {
|
|
for (const [id, session] of this.sessions) {
|
|
if (session.userId === userId && id !== exceptSessionId) {
|
|
this.sessions.delete(id);
|
|
}
|
|
}
|
|
}
|
|
}
|