diff --git a/Docs/Tasks.md b/Docs/Tasks.md index a9c8029..5a6267a 100644 --- a/Docs/Tasks.md +++ b/Docs/Tasks.md @@ -1,23 +1,3 @@ -### TASK-STATE-03 — `DashboardFilterBar` Has Dual State Source - -**Where found** -`frontend/src/components/DashboardFilterBar.tsx`. The component reads from both `DashboardFilterProvider` context and from props using `??` as a fallback. `HistoryPage` passes filter values via props without mounting a `DashboardFilterProvider`, so the context values are `undefined` and the `??` fallback silently provides context defaults. `DashboardPage` uses context, so props are `undefined` and context values apply. Both pages render the same component but through different code paths. - -**Goal** -Choose one data source and use it consistently. The recommended approach is to use props everywhere: `DashboardPage` should read from context via `useDashboardFilter()` and pass the values explicitly to `DashboardFilterBar` as props, exactly like `HistoryPage` does. This makes the component's behaviour predictable — it always reads from props, never from context. - -**Possible traps and issues** -- `DashboardFilterProvider` may be used by other components on the dashboard. Audit all consumers of `useDashboardFilter()` before removing the context read from `DashboardFilterBar`. -- The `??` fallback chain must be fully removed; otherwise the dual-source behaviour can creep back. - -**Docs changes needed** -None required. - -**Why this is needed** -Silent dual-source components are a debugging hazard. A developer adding a new consumer of `DashboardFilterBar` has no obvious signal about which data source is active, leading to subtle bugs when one source overrides the other. - ---- - ### TASK-STATE-04 — Token in `sessionStorage` Is Never Sent (Misleading Auth Model) **Where found** diff --git a/Docs/Web-Development.md b/Docs/Web-Development.md index 5abdcf7..2573ca6 100644 --- a/Docs/Web-Development.md +++ b/Docs/Web-Development.md @@ -485,7 +485,42 @@ if (data.length > MAX_VISIBLE_BANS) { ... } --- -## 11. Error Handling +## 10. Authentication + +### Session Model + +The authentication model is **cookie-based**: + +1. **Login:** The frontend sends the master password (SHA256-hashed) to `POST /api/auth/login`. The backend validates it, creates a session, and returns an HTTP response with a `Set-Cookie` header containing `bangui_session`. + +2. **Requests:** All API requests automatically include the session cookie via `credentials: "include"` in the fetch options. The frontend does **not** send an Authorization header or token in the request body. + +3. **Session validity:** The backend is the **sole authority** on whether a session is valid. The frontend is authenticated when the backend accepts the request (returns 2xx) and is not authenticated when the backend rejects it (returns 401 or 403). + +4. **Logout:** The frontend sends `POST /api/auth/logout`, and the backend invalidates the session and clears the cookie. + +### Frontend Auth State + +- The `AuthProvider` context (`providers/AuthProvider.tsx`) manages a simple boolean `isAuthenticated` state. +- On successful login, `isAuthenticated` is set to `true` and persisted to `sessionStorage` for page-reload continuity. +- On logout or when `SESSION_EXPIRED_EVENT` fires (triggered by a 401/403 API response), `isAuthenticated` is set to `false` and cleared from `sessionStorage`. +- The `sessionStorage` entry (`bangui_authenticated`) survives page refreshes within the same tab but is automatically cleared when the tab closes. +- The session cookie persists according to the backend's cookie settings (typically for the duration of the browser session or as configured server-side). + +### Why Not Token-Based? + +The frontend previously stored JWT tokens in `sessionStorage` but never actually used them. The authentication model is entirely cookie-based (handled by the browser automatically), making stored tokens confusing and misleading. If token-based auth is needed in the future, the storage approach would need to change significantly (e.g., to include Authorization headers in all requests). For now, the only persistent state the frontend needs is the boolean `isAuthenticated` flag. + +### Error Handling + +When an API request returns 401 or 403: +1. The `client.ts` module dispatches a `SESSION_EXPIRED_EVENT`. +2. The `AuthProvider` listener handles it by clearing `isAuthenticated` and redirecting to `/login`. +3. Hooks must use `handleFetchError` (from `utils/fetchError.ts`) to avoid displaying auth errors as user-facing error messages. + +--- + +## 12. Error Handling - Wrap API calls in `try-catch` inside hooks — components should never see raw exceptions. - **All hook catch blocks must use `handleFetchError` rather than directly calling `setError`.** This ensures auth errors (401/403) are routed to the global session-expiry flow instead of displaying confusing error text in the UI. Use the pattern: `handleFetchError(err, setError, "User-friendly fallback message")`. @@ -496,7 +531,7 @@ if (data.length > MAX_VISIBLE_BANS) { ... } --- -## 12. Performance +## 13. Performance - Use `React.memo` only when profiling reveals unnecessary re-renders — do not wrap every component by default. - Use `useMemo` and `useCallback` for expensive computations and stable callback references passed to child components — not for trivial values. @@ -506,7 +541,7 @@ if (data.length > MAX_VISIBLE_BANS) { ... } --- -## 13. Accessibility +## 14. Accessibility - Use semantic HTML elements (`