# Authentication Frontend route middleware is UX/navigation protection only. Backend authorization is authoritative. The global AGENTS.md (`C:\Users\Admin\.config\opencode\AGENTS.md`) defines the security authority for auth (authentication, authorization, sessions/tokens, rate limiting, error handling). Its rules apply here: never log or expose tokens, use safe generic errors, and treat all frontend authorization as UX, never as a security boundary. ## Confirmed contract (Phase 00 discovery, 2026-08-12) Source of truth: `docs/keluargamart-openapi.yaml` (the backend's OpenAPI 3.0.3 spec, archived 2026-08-12) — the three auth endpoints below are defined there and match the live probes. - **Model**: bearer token. - `POST /api/v1/auth/login` with `{ email, password }` → `200 { "data": { "token": "", "user": { id, name, email, role, … } }, "meta": { … } }`. - `GET /api/v1/auth/me` with `Authorization: Bearer ` → `200 { "data": { "user": { … }, "employee": …|null, "permissions": { … } }, "meta": { … } }`. - `POST /api/v1/auth/logout` with bearer → `204` (no body). ### Frontend behavior - The bearer token lives in the Nuxt session state (`useAuth`) and is mirrored into the shared Axios client (`setAuthToken` in `app/services/http.ts`), so every request carries `Authorization: Bearer …`. - Session persistence (2026-08-12): the session is mirrored to localStorage (`keluargamart.session` = `{ token, user, expiresAt }`) so a hard refresh / full page load does not log the user out. `useAuth()` hydrates synchronously from storage (client-side), and `app/plugins/session.client.ts` revalidates the token in the background via `GET /auth/me` — a 401/error drops the stored session and the middleware routes to `/login`. - Trade-off: the bearer token is readable by page scripts (standard SPA trade-off — the backend controls token scope and can revoke it; logout is server-side). No refresh-token flow exists — the backend does not return a token lifetime or refresh mechanism, so nothing is invented here (AGENTS.md). `expiresAt` is a client-side 24 h fallback that only prunes stale storage. - SSR and middleware (2026-08-12): the session is client-only, so all auth-gated routes (`/admin/**`, `/account/**`, `/checkout/**`, `/orders/**`, `/payments/**`, `/quick-order`, `/order-import`, `/returns`, `/support/**`) are `ssr: false` in `nuxt.config.ts`, and every auth/admin/guest middleware redirect is client-gated (`import.meta.client`). Without this, a full reload of an authed URL would redirect server-side to `/login` (no session exists during SSR) and the hydrated client would then bounce to `/account`. - The backend's system `role` (`'admin'` = merchant operator) is mapped to the storefront's `customerRole` (`'admin'` / `'customer'`) in `app/services/http/repos.ts` (`mapSessionUser`). - The customer/partner binding for the storefront (`customerId`) is not part of the login/me response yet — see the `TODO(Phase 00)` in `mapSessionUser`. - Registration, forgot/reset password: the backend exposes no such endpoints today (404). The storefront's self-registration flow therefore stays mock-only until the backend defines these contracts. `PUT /api/v1/auth/me` (update own name/email/password) IS in the spec but is not wired — the storefront profile is customer-scoped, and the spec's `UpdateProfileRequest` does not cover company/billing fields. ### Not confirmed yet - Token expiry/rotation, refresh semantics, and whether `/auth/me` restores sessions across restarts — pending the backend's token model.