Add login-required user management (admin/user roles, Config->Benutzer)
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>
This commit is contained in:
parent
b50c00c0ac
commit
c56cfb30ad
36 changed files with 4928 additions and 38 deletions
55
src/server/auth/sessions.ts
Normal file
55
src/server/auth/sessions.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue