diff --git a/docs/README.md b/docs/README.md index 8f75bbf1..1e54c70f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,6 +17,7 @@ This folder is the canonical documentation set for Mission Control. 5. [Architecture](05-architecture.md) 6. [Configuration](06-configuration.md) 7. [API reference](07-api-reference.md) + - [Frontend API + auth modules](frontend-api-auth.md) 8. [Agents & skills](08-agents-and-skills.md) 9. [Ops / runbooks](09-ops-runbooks.md) 10. [Troubleshooting](10-troubleshooting.md) diff --git a/docs/frontend-api-auth.md b/docs/frontend-api-auth.md new file mode 100644 index 00000000..c7e19899 --- /dev/null +++ b/docs/frontend-api-auth.md @@ -0,0 +1,109 @@ +# Frontend API client and auth integration + +This page documents the frontend integration points you’ll touch when changing how the UI talks to the backend or how auth is applied. + +## Related docs + +- [Architecture](05-architecture.md) +- [Configuration](06-configuration.md) +- [API reference](07-api-reference.md) + +## API base URL + +The frontend uses `NEXT_PUBLIC_API_URL` as the single source of truth for where to send API requests. + +- Code: `frontend/src/lib/api-base.ts` +- Behavior: + - reads `process.env.NEXT_PUBLIC_API_URL` + - normalizes by trimming trailing slashes + - throws early if missing/invalid + +In Docker Compose, `compose.yml` sets `NEXT_PUBLIC_API_URL` both: +- as a **build arg** (for `next build`), and +- as a **runtime env var**. + +## API client layout + +### Generated client + +- Location: `frontend/src/api/generated/*` +- Generator: **Orval** + - Config: `frontend/orval.config.ts` + - Script: `cd frontend && npm run api:gen` + - Convenience target: `make api-gen` + +By default, Orval reads the backend OpenAPI schema from: +- `ORVAL_INPUT` (if set), otherwise +- `http://127.0.0.1:8000/openapi.json` + +Output details (from `orval.config.ts`): +- Mode: `tags-split` +- Target index: `frontend/src/api/generated/index.ts` +- Schemas: `frontend/src/api/generated/model` +- Client: `react-query` +- All requests go through the custom mutator below. + +### Custom fetch / mutator + +All generated requests go through: + +- Code: `frontend/src/api/mutator.ts` +- What it does: + - resolves `NEXT_PUBLIC_API_URL` and builds the full request URL + - sets `Content-Type: application/json` when there’s a body + - injects `Authorization: Bearer ` when a Clerk session token is available + - converts non-2xx responses into a typed `ApiError` (status + parsed response) + +## Auth enablement and token injection + +### Clerk enablement (publishable key gating) + +Clerk is enabled in the frontend only when `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` looks valid. + +- Gating helper (dependency-free): `frontend/src/auth/clerkKey.ts` +- UI-safe wrappers/hooks: `frontend/src/auth/clerk.tsx` + - provides `SignedIn`, `SignedOut`, `SignInButton`, `SignOutButton`, `useUser`, and `useAuth` + - returns safe fallbacks when Clerk is disabled (to allow secretless builds/prerender) + +### Token injection + +When the UI makes an API request, the mutator attempts to read a token from the Clerk session: + +- Code: `frontend/src/api/mutator.ts` (`resolveClerkToken()`) +- If a token is available, the request includes: + - `Authorization: Bearer ` + +### Route protection (middleware) + +Request-time route protection is implemented via Next.js middleware: + +- Code: `frontend/src/proxy.ts` +- Behavior: + - when Clerk is enabled: uses `clerkMiddleware()` to enforce auth on non-public routes + - when Clerk is disabled: passes all requests through + +## Common workflows + +### Update the backend API and regenerate the client + +1. Run the backend so OpenAPI is available: + +```bash +# from repo root +cp backend/.env.example backend/.env +make backend-migrate +cd backend && uv run uvicorn app.main:app --reload --port 8000 +``` + +2. Regenerate the client: + +```bash +# from repo root +make api-gen + +# or from frontend/ +ORVAL_INPUT=http://127.0.0.1:8000/openapi.json npm run api:gen +``` + +3. Review diffs under `frontend/src/api/generated/*`. +