# 26 — Foundation Audit Fixes

A strict CTO/CPO/security/QA audit of the Phase-1 foundation surfaced several gaps. Fixes shipped in this slice; the rest are queued for the next slice.

## Fixed in this slice

### API
- **JWT auth middleware** (`common/middleware/jwt-auth.middleware.ts`) decodes the Bearer token, resolves the user with roles/permissions/memberships, and populates `req.context`.
- **Async-local request context** (`common/context/request-context.store.ts`) lets services anywhere in the call stack read the actor without explicit threading. `RequestContextStore.run(ctx, fn)` wraps every request.
- **Prisma scope helper** (`common/scope/prisma-scope.ts`) — `applyScope(where, 'own'|'assigned'|'all')` and `assertBusinessAccess(businessId)`. Mandatory near every controller boundary that returns or mutates business data.
- **Idempotency middleware** (`common/middleware/idempotency.middleware.ts`) — caches a successful response keyed by `Idempotency-Key`; in-memory for development, ready to be backed by Redis/Postgres `IdempotencyRecord`.
- **HMAC verifier** (`common/security/hmac.ts`) — timing-safe webhook signature check with replay protection.
- **WebhooksModule** scaffold — receives provider events at `POST /v1/webhooks/:provider`, verifies the signature, writes to `AuditLog`. Real event store + retry queue ships in Phase 2.
- **RefundsModule** scaffold — `POST /v1/refunds`, `POST /v1/refunds/:id/approve`, `GET /v1/refunds/mine`; permission-gated and audited.
- **Audit interceptor semantics** corrected — the request body is no longer stored as `before`. `before` is reserved for the resource's prior state (when controllers expose `req._priorState`); the result and request payload land under `after`.
- **Schema additions** — `WebhookEvent` (replay protection), `IdempotencyRecord` (durable idempotency), `Booking.checkInAt` index.
- **Seed** — added `audit.read.assigned` and `membership.read.assigned` to all partner roles.

### Mobile
- **Locale persistence** — `useLocale` now hydrates from SecureStore and applies the value to both `i18next` and `I18nManager` on boot.
- **Onboarding flag** — `useOnboarding` persists `hasOnboarded`; the splash now routes correctly for first-time vs returning users.
- **Tabs route guard** — `(tabs)/_layout.tsx` redirects to `/(auth)/login` if not authenticated.
- **Error boundary** — root `_layout` exports `ErrorBoundary` (Expo Router), renders a graceful retry view.
- **Trip-planner result** — no more hardcoded AED literals; renders through the shared `formatMoney` helper using a clearly-labelled placeholder data shape until the API path is wired.

### Dashboard
- **Auth middleware** (`apps/dashboard/src/middleware.ts`) — server-side gate that redirects unauthenticated requests to `/login`.

### Shared
- **`@navi/ui/format`** — `formatMoney`, `formatDate`, `formatNumber`, `isRtl`. AED-aware, locale-aware, no float math.

## Known gaps still open (for the next slice)

1. **Refresh-token rotation** — the endpoint still throws "not implemented." The next slice persists hashed refresh tokens in `Session` and rotates on use.
2. **Mobile auth wired to API** — login/signup/forgot/OTP screens still bypass `/v1/auth/*`. The next slice replaces stubs with real calls and sets the session via `useAuth.setSession`.
3. **Dashboard login → API** — the login page is a UI stub. The next slice issues a session cookie via a Next.js route handler that calls `/v1/auth/login`.
4. **Cross-partner isolation test fixture** — must exist as a non-skippable CI test. The helper is in place; the test needs writing.
5. **Idempotency persistence** — current implementation is process-memory only. Move to Redis (or `IdempotencyRecord`) before launch.
6. **Webhook event store** — current `WebhooksModule` writes to `AuditLog`; the next slice persists into `WebhookEvent` for replay protection and retry orchestration.
7. **Raw-body capture for HMAC** — the current `verifyHmacSignature` call hashes `JSON.stringify(body)`. Production providers sign the literal request bytes. The next slice registers a Fastify `addContentTypeParser` to retain the raw buffer alongside the parsed body.
8. **Mobile network/offline detection** — `OfflineBanner` not yet built; `StateView('offline')` exists as a primitive.
9. **`Tag` / `Chip` / `Card` / `RatingPill`** primitives still inlined per app. To be promoted into `@navi/ui` once their API stabilizes.
10. **Custom font loading** on mobile (Inter, IBM Plex Sans Arabic) not yet declared in `app.json`.
11. **Website SEO files** — `sitemap.xml`, `robots.txt`, `metadataBase`, `hreflang` alternates pending.
12. **UserRole composite PK + nullable businessId** quirk — Postgres treats NULL ≠ NULL, so a user could in principle hold the same role twice with `businessId = null`. Mitigation: defensive upserts in any code that assigns roles. Long-term: introduce a `__global__` sentinel or a partial unique index.

## Next implementation order (foundation-only)

1. Wire mobile auth screens to `/v1/auth/*`.
2. Implement refresh-token rotation in the API.
3. Implement dashboard login → session cookie.
4. Cross-partner isolation test fixture in CI.
5. Move idempotency to Redis.
6. Persist webhook events with replay protection.
7. Add raw-body parser for HMAC verification.

Only after the seven items above are green do we move into Phase-2 *features*.
