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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user