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

@@ -471,6 +471,38 @@ const load = useCallback(() => {
}, []);
```
### Session Validation on App Mount
The `AuthProvider` uses the `useSessionValidation` hook to validate the cached session with the backend on app mount. This pattern ensures that the UI state always reflects reality — expired or revoked sessions are detected immediately, not after the first API call.
**How it works:**
1. `useSessionValidation` is called during `AuthProvider` initialization.
2. It calls `GET /api/auth/session`, which requires a valid session cookie/header.
3. While the check is in flight, a loading spinner (`SessionValidationLoading`) is displayed.
4. **On 200 (valid session):** The app proceeds with the cached session state.
5. **On 401 (invalid session):** The user is logged out and redirected to `/login`.
6. **On network error:** The error is logged but the user is not logged out — the backend may be temporarily unreachable. The next API call will trigger a 401 if needed.
**Example hook signature:**
```ts
interface UseSessionValidationResult {
isLoading: boolean;
error: Error | null;
}
function useSessionValidation(
onSessionValid: () => void,
onSessionExpired: () => void,
onNetworkError?: (error: Error) => void,
): UseSessionValidationResult {
// Calls validateSession() and handles the three outcomes.
}
```
This pattern prevents **stale session flicker** — the brief moment when a user sees the authenticated UI before the first API call reveals a 401. It also handles scenarios where the session cookie has expired server-side (server restart, session duration elapsed, manual DB deletion) before the frontend detects it.
---
## 8. Naming Conventions