feat(auth): add server-backed demo sessions

The browser treated sessionStorage as the source of truth for the logged-in
user and never verified or invalidated the server-side session cookie: no
GET /api/v1/demo/session or POST /api/v1/demo/logout endpoint existed, and a
central 401 handler was defined but never wired up.

Add both endpoints; the session-check response is marked Cache-Control:
no-store to avoid the browser serving a stale "authenticated" response right
after logout. AuthProvider now verifies against the server on every mount
(sessionStorage only caches presentation state to avoid a login-screen
flash), subscribes to a central 401 listener on the API client, and
RequireAuth shows a loading state during verification instead of flashing
protected content or the wrong role.
This commit is contained in:
NuklearRabbit
2026-08-02 04:51:54 +02:00
parent 56a65b2364
commit ffc88e33b4
7 changed files with 149 additions and 22 deletions
+11
View File
@@ -1,5 +1,13 @@
const API_BASE = import.meta.env.VITE_API_BASE_URL ?? "";
type UnauthorizedListener = () => void;
const unauthorizedListeners = new Set<UnauthorizedListener>();
export function onUnauthorized(listener: UnauthorizedListener): () => void {
unauthorizedListeners.add(listener);
return () => unauthorizedListeners.delete(listener);
}
export class ApiError extends Error {
status: number;
code: string;
@@ -31,6 +39,9 @@ async function request<T>(path: string, init?: RequestInit): Promise<T> {
body = undefined;
}
const error = body?.error;
if (response.status === 401) {
unauthorizedListeners.forEach((listener) => listener());
}
throw new ApiError(
response.status,
error?.code ?? String(response.status),