TASK-004: Bootstrap frontend auth state from backend session check

Validates session on app mount by calling GET /api/auth/session instead of relying
solely on cached sessionStorage. This ensures the UI state always reflects server
reality — expired or revoked sessions are detected immediately.

Changes:
- Backend: Add GET /api/auth/session endpoint (requires valid session, returns 200/401)
- Frontend: Add useSessionValidation hook for mount-time validation
- Frontend: Add SessionValidationLoading component for validation spinner
- Frontend: Update AuthProvider to call validation on mount with loading state
- Frontend: Add validateSession API function
- Docs: Update Features.md with session validation behavior
- Docs: Update Web-Development.md with session validation pattern

Handles three outcomes:
1. Valid session (200): Proceed with cached state
2. Invalid session (401): Clear sessionStorage and redirect to login
3. Network error: Don't logout (backend may be temporarily unreachable)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-26 12:00:21 +02:00
parent d982fe3efc
commit 29daaa9906
11 changed files with 1314 additions and 15 deletions

View File

@@ -1,7 +1,7 @@
/**
* Authentication API functions.
*
* Wraps calls to POST /api/auth/login and POST /api/auth/logout
* Wraps calls to POST /api/auth/login, POST /api/auth/logout, and GET /api/auth/session
* using the central typed fetch client.
*/
@@ -27,3 +27,17 @@ export async function login(password: string): Promise<LoginResponse> {
export async function logout(): Promise<LogoutResponse> {
return api.post<LogoutResponse>(ENDPOINTS.authLogout, {});
}
/**
* Validate the current session with the backend.
*
* This is called on app mount to confirm the session is still valid on the
* server side, rather than relying solely on the cached sessionStorage flag.
*
* @param signal - Optional abort signal for request cancellation.
* @returns An object confirming the session is valid.
* @throws {ApiError} When the session is invalid or expired (401).
*/
export async function validateSession(signal?: AbortSignal): Promise<{ valid: boolean }> {
return api.get<{ valid: boolean }>(ENDPOINTS.authSession, signal);
}

View File

@@ -23,6 +23,7 @@ export const ENDPOINTS = {
// -------------------------------------------------------------------------
authLogin: "/auth/login",
authLogout: "/auth/logout",
authSession: "/auth/session",
// -------------------------------------------------------------------------
// Dashboard