Skip to content

Commit 40592d6

Browse files
committed
docs: name the four API/auth hosts and retire the pre-migration pair
The code already resolves the right hosts after the Aug 2026 issuer migration — `accounts.mukoko.com` for the AuthKit issuer, `auth.mukoko.com` for the auth API, both allowed in the CSP. CLAUDE.md and SECURITY.md still named the retired `identity.nyuchi.com` / `api.identity.nyuchi.com` pair, so the documented setup no longer matched the shipped one. Adds a four-host table to CLAUDE.md rather than only correcting the strings. Two of these have now been confused in practice: an issuer default pointed at an auth API host, and a platform client default pointed at `api.mukoko.com` — a separate Mukoko gateway still being built, which is neither WorkOS nor the gateway serving traffic (`api.nyuchi.com`). Four adjacent-looking hostnames, four distinct jobs, and each mix-up fails a long way from its cause: an issuer pointed at an API host yields metadata that validates but dead-ends at discovery, before sign-in is ever attempted. Historical references are kept where they aid recognition — a reader who finds the old host in a dashboard needs to know it was real and is now retired, not wonder whether it is a typo. Docs only; no code paths touched. 887 tests pass, lint clean (0 errors). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HFaeL83iEL5gqQXhHweH6u
1 parent 6d9193f commit 40592d6

2 files changed

Lines changed: 17 additions & 5 deletions

File tree

CLAUDE.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ Mutations and client-invoked reads: `auth`, `events`, `event-updates`, `discover
116116

117117
Same-origin fallback endpoints: `events`, `events/[id]` (both also take bearer-authed `POST`/`PATCH` for the MCP), `categories`, `cities`, `community/stats`, `health` (liveness + Mongo dependency probe — `200 ok` / `503 down`, `X-Health-Status` header, `HEAD` for cheap polling; shaped like kweli's `/api/health` so one monitor config covers both, and necessary because every Mongo read degrades to empty rather than throwing, so a `200` on `/` is NOT evidence the app is healthy), `og` (OG image), `media/upload` (WorkOS-gated cover-image upload to R2), `webhooks/workos` (guaranteed user provisioning — see Authentication Flow), and `auth/dev-login` (local dev bypass only). Auth itself is handled by the hosted-AuthKit entry route `/auth/hosted` (redirects to WorkOS) and `/callback` — see the Authentication Flow section.
118118

119-
**Agent-readiness discovery** — three `.well-known` route handlers (`src/app/.well-known/*/route.ts`, all `force-dynamic`, `Access-Control-Allow-Origin: *`) advertise the **WorkOS AuthKit OAuth 2.1 authorization server** (`identity.nyuchi.com/oauth2/*`) from the Nhimbe origin so MCP agents/clients can run standard discovery: `oauth-authorization-server` (RFC 8414 — authorize/token/JWKS **plus the DCR `registration_endpoint`**), `oauth-protected-resource` (RFC 9728 — Nhimbe as resource server, pointing at `/auth.md`; its `resource` is derived from the request host so the doc self-identifies correctly whether served on `nhimbe.com` or `events.mukoko.com`), and `openid-configuration` (OIDC Discovery 1.0, RS256). All four surfaces (these three + `/auth.md`) derive their endpoints from the single helper `src/lib/auth/workos-metadata.ts` (`WORKOS_AUTHKIT_DOMAIN`, default `identity.nyuchi.com`) — the AuthKit domain that serves its own self-consistent metadata and DCR. This is deliberately **not** the API domain in the RFC 9728 `authorization_servers` pointer: `api.identity.nyuchi.com` serves no authorization-server metadata (a client following a pointer there 404s and the flow dead-ends). WorkOS remains the real authorization server; these endpoints just advertise it. The bearer-token **verifier** (`workos-token.ts`) independently reads JWKS from the **API** domain (`WORKOS_API_HOSTNAME` → `/sso/jwks/{clientId}`); advertising the AuthKit domain while verifying against the API domain is safe because a WorkOS environment signs every access token with one key, published (identical `kid`) at both hosts, and the verifier pins only signature + expiry + subject.
119+
**Agent-readiness discovery** — three `.well-known` route handlers (`src/app/.well-known/*/route.ts`, all `force-dynamic`, `Access-Control-Allow-Origin: *`) advertise the **WorkOS AuthKit OAuth 2.1 authorization server** (`accounts.mukoko.com/oauth2/*`) from the Nhimbe origin so MCP agents/clients can run standard discovery: `oauth-authorization-server` (RFC 8414 — authorize/token/JWKS **plus the DCR `registration_endpoint`**), `oauth-protected-resource` (RFC 9728 — Nhimbe as resource server, pointing at `/auth.md`; its `resource` is derived from the request host so the doc self-identifies correctly whether served on `nhimbe.com` or `events.mukoko.com`), and `openid-configuration` (OIDC Discovery 1.0, RS256). All four surfaces (these three + `/auth.md`) derive their endpoints from the single helper `src/lib/auth/workos-metadata.ts` (`WORKOS_AUTHKIT_DOMAIN` / `WORKOS_ISSUER`, default `accounts.mukoko.com`) — the AuthKit domain that serves its own self-consistent metadata and DCR. This is deliberately **not** the API domain in the RFC 9728 `authorization_servers` pointer: `auth.mukoko.com` serves no authorization-server metadata (a client following a pointer there 404s and the flow dead-ends). WorkOS remains the real authorization server; these endpoints just advertise it. The bearer-token **verifier** (`workos-token.ts`) independently reads JWKS from the **API** domain (`WORKOS_API_HOSTNAME` → `/sso/jwks/{clientId}`); advertising the AuthKit domain while verifying against the API domain is safe because a WorkOS environment signs every access token with one key, published (identical `kid`) at both hosts, and the verifier pins only signature + expiry + subject.
120120

121121
### Authentication Flow (WorkOS AuthKit — hosted UI)
122122

@@ -137,7 +137,7 @@ Auth uses **WorkOS's hosted AuthKit UI**. Nhimbe no longer ships a self-hosted s
137137
- `src/lib/auth/dev.ts` + `/api/auth/dev-login` — local dev auth bypass (kept).
138138
- `/callback` is the canonical post-auth landing; `/authenticate` is a legacy redirect → `/`.
139139

140-
> **WorkOS environment alignment (critical):** `WORKOS_API_KEY`, `WORKOS_CLIENT_ID`, the `api.identity.nyuchi.com` custom **API** domain (`WORKOS_API_HOSTNAME`), and the `identity.nyuchi.com` hosted **AuthKit UI** domain **must all belong to the same WorkOS environment**. Two distinct domains: `api.identity.nyuchi.com` serves the WorkOS API (authorize/token/JWKS — what the SDK and token verifier call), while `identity.nyuchi.com` serves the hosted sign-in UI (configured in the WorkOS dashboard). A key/client-id/domain split across environments makes the hosted redirect succeed but the callback code exchange fail.
140+
> **WorkOS environment alignment (critical):** `WORKOS_API_KEY`, `WORKOS_CLIENT_ID`, the `auth.mukoko.com` custom **API** domain (`WORKOS_API_HOSTNAME`), and the `accounts.mukoko.com` hosted **AuthKit UI** domain **must all belong to the same WorkOS environment**. Two distinct domains: `auth.mukoko.com` serves the WorkOS API (authorize/token/JWKS — what the SDK and token verifier call), while `accounts.mukoko.com` serves the hosted sign-in UI (configured in the WorkOS dashboard). A key/client-id/domain split across environments makes the hosted redirect succeed but the callback code exchange fail.
141141
142142
### AI — Shamwari (`src/lib/ai/`)
143143

@@ -337,8 +337,19 @@ Set in Vercel (prod + preview) and locally in `.env.local`:
337337
- `WORKOS_CLIENT_ID` — WorkOS Client ID (server-only; no `NEXT_PUBLIC_` prefix).
338338
- `WORKOS_API_KEY` — server-only, used by the AuthKit proxy.
339339
- `WORKOS_COOKIE_PASSWORD` — server-only session-cookie encryption key (≥32 chars).
340-
- `WORKOS_API_HOSTNAME` *(optional)* — defaults to `api.workos.com`; set to `api.identity.nyuchi.com` (the custom **API** domain) to route WorkOS API calls — and the bearer-token verifier's JWKS fetch (`/sso/jwks/{clientId}`) — through it. This is the API surface, **not** the authorization-server or hosted-UI domain.
341-
- `WORKOS_AUTHKIT_DOMAIN` *(optional)* — the hosted **AuthKit** domain (`identity.nyuchi.com` in production; default), which is the OAuth 2.1 **authorization server** MCP clients discover and authenticate against (`/oauth2/{authorize,token,register,jwks}`, DCR enabled). Everything the `.well-known/*` discovery routes and `/auth.md` advertise is built from this via `src/lib/auth/workos-metadata.ts`. Override per environment (e.g. a WorkOS-hosted `*.authkit.app` domain).
340+
- `WORKOS_API_HOSTNAME` *(optional)* — defaults to `api.workos.com`; set to `auth.mukoko.com` (the custom **API** domain) to route WorkOS API calls — and the bearer-token verifier's JWKS fetch (`/sso/jwks/{clientId}`) — through it. This is the API surface, **not** the authorization-server or hosted-UI domain. (Was `api.identity.nyuchi.com` before the Aug 2026 migration.)
341+
- `WORKOS_AUTHKIT_DOMAIN` *(optional)* — the hosted **AuthKit** domain (`accounts.mukoko.com` in production; the code default), which is the OAuth 2.1 **authorization server** MCP clients discover and authenticate against (`/oauth2/{authorize,token,register,jwks}`, DCR enabled). Everything the `.well-known/*` discovery routes and `/auth.md` advertise is built from this via `src/lib/auth/workos-metadata.ts`. Accepts `WORKOS_ISSUER` as the preferred spelling. Override per environment (e.g. `accounts-staging.mukoko.com`, or a WorkOS-hosted `*.authkit.app` domain). (Was `identity.nyuchi.com` before the Aug 2026 migration.)
342+
343+
> **The four hosts, and why they are not interchangeable.** Three `*.mukoko.com` subdomains plus one `nyuchi.com` do four different jobs, and mixing them up fails a long way from the cause:
344+
>
345+
> | Host | What it is |
346+
> | --- | --- |
347+
> | `accounts.mukoko.com` | WorkOS **AuthKit issuer** — the OAuth 2.1 authorization server agents discover. Serves its own self-consistent metadata + DCR. `WORKOS_AUTHKIT_DOMAIN` / `WORKOS_ISSUER`. |
348+
> | `auth.mukoko.com` | WorkOS **auth API** — what the SDK calls and where the JWKS fetch goes. `WORKOS_API_HOSTNAME`. |
349+
> | `api.nyuchi.com` | The **Nyuchi API gateway** (FastAPI on Fly, repo `nyuchi/api-gateway`) — the gateway serving traffic today. Nothing to do with WorkOS. |
350+
> | `api.mukoko.com` | A **separate Mukoko API gateway still being built** — serves nothing usable yet. Also not WorkOS, despite sitting between the two that are. |
351+
>
352+
> Advertising the auth API where the issuer belongs produces metadata that validates but dead-ends: a client following the pointer finds no authorization-server document and the flow dies at discovery, before sign-in.
342353
- `NEXT_PUBLIC_WORKOS_REDIRECT_URI` — usually `${NEXT_PUBLIC_SITE_URL}/callback`. The `NEXT_PUBLIC_` prefix is **required** — AuthKit reads it from the client bundle to form the OAuth start URL.
343354
- `WORKOS_WEBHOOK_SECRET` — server-only signing secret for the WorkOS event webhook (`POST /api/webhooks/workos`, guaranteed user provisioning). Create the webhook endpoint `https://nhimbe.com/api/webhooks/workos` in the WorkOS dashboard (user + organization_membership events) and copy its signing secret here. When unset the endpoint answers 503 and provisioning falls back to the callback + lazy sync.
344355
- `SHAMWARI_AI_GATEWAY_URL`, `SHAMWARI_AI_GATEWAY_TOKEN` — Cloudflare AI Gateway base + provider bearer; optional `SHAMWARI_AI_GATEWAY_AUTH_TOKEN` for the authenticated gateway.

SECURITY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ Concrete detail behind the baseline above, for reviewers and future contributors
5454
only OpenStreetMap tiles (`*.tile.openstreetmap.org`, CyclOSM, OpenTopoMap),
5555
OSM Nominatim geocoding (`nominatim.openstreetmap.org`), the Mukoko weather
5656
embed (`weather.mukoko.com`), Cloudflare R2 assets (`*.mukoko.com`), WorkOS
57-
(`api.workos.com`, `api.identity.nyuchi.com`, `identity.nyuchi.com`) and
57+
(`api.workos.com`, plus the custom domains `auth.mukoko.com` for the auth
58+
API and `accounts.mukoko.com` for the AuthKit issuer) and
5859
Google Fonts (`fonts.googleapis.com`, `fonts.gstatic.com`). `script-src`
5960
still permits `'unsafe-inline'`/`'unsafe-eval'` for Next.js's inline
6061
hydration bootstrap — tightening this to a per-request nonce /

0 commit comments

Comments
 (0)