Read this when changing browser-to-server API requests, sessions, CSRF, or JSON response behavior. # Browser JSON API ## Implementing Files | File | Key symbols | |---|---| | `ui/app/src/api/client.js` | `apiRequest`, `getSession`, `login`, `logout`, navigation/notification functions | | `src/Core/Api/Api.php` | Versioned route registry | | `src/Core/Api/Controller.php` | JSON, request decoding, current user, CSRF, authorization helpers | | `src/Core/Api/AuthController.php` | Session inspection, login, logout | | `src/Core/Api/*Controller.php` | Navigation, configuration, user, and notification handlers | ## Flow 1. React boot calls `getSession()` through `apiRequest()`. 2. The client resolves the URL against the Vite asset base, sends same-origin cookies, and accepts JSON. 3. The API reads the PHP session, produces a safe session DTO, and supplies a CSRF token when absent. 4. Later non-GET calls attach the in-memory token as `X-CSRF-Token`. 5. Controllers validate JSON/authentication/roles/CSRF as applicable, then return explicit JSON DTOs. ## Trust Boundaries | Boundary | Mechanism | Location | |---|---|---| | Client-supplied URL path | API root is derived from same-origin Vite base; client uses fixed resource paths | `ui/app/src/api/client.js` | | Credential input | Requires JSON object plus non-empty string username/password; verifies bcrypt hash server-side | `AuthController::login()` | | Session fixation | Regenerates session ID after successful login | `AuthController::login()` | | Cross-site state change | Session token compared with `hash_equals` before protected mutations | `Controller::validateCsrfToken()` and mutation controllers | | Authorization | Authentication and role checks run in PHP before data access | `Controller::requireAuthentication()` / `requireRole()` | | Sensitive output | Controllers return explicit DTOs instead of mapper fields | `Controller`, `UserController`, `NotificationController` | | Browser-only route control | React redirects unauthorized users, but does not grant access | `ui/app/src/routes.jsx` | ## Config - Base path: Vite `base` is `/phengine/assets/app/`; the API client removes `/assets/app` to reach application root. - Credentials: `fetch(..., { credentials: 'include' })`. - Cache policy: API responses send `Cache-Control: no-store`. - Notification polling: 30,000 ms in `NotificationMenu`. See [API routes](../api-routes.md) and [identity spec](../identity/spec.md).