fix(server-beta): accept X-Api-Key as fallback to Bearer in auth middleware#2719
Conversation
…leware The server-beta auth middleware (`requireServerAuth` and `requirePostgresServerAuth`) only reads `Authorization: Bearer <key>`. Clients using `@better-auth/api-key` default config send raw keys via the `X-Api-Key` header — including the worker bundle shipped from the Windows-canary line (PR thedotmack#2699). The mismatch produces: POST /v1/events X-Api-Key: cmem_... → 401 {"error":"Unauthorized","message":"Missing bearer API key"} Direct-LAN setups don't hit this because no proxy strips/normalizes headers and the worker's own retry path eventually round-trips; remote setups behind a proxy (Cloudflare → Express in our case) fail closed and never capture. Fix: middleware accepts either header. Bearer remains canonical so existing clients are unaffected; `X-Api-Key` is the fallback when Bearer is absent. Same change applied to the bun:sqlite-backed `auth.ts` and the Postgres-backed `postgres-auth.ts` (the latter is what server-beta actually uses). Tests added in `tests/server/auth-api-key.test.ts`: - accepts X-Api-Key when Bearer absent (positive) - prefers Bearer when both present (defense-in-depth) - rejects requests with neither header (negative regression) No new test for postgres-auth.ts (no existing suite that wires the PG pool); the parser logic is byte-identical to auth.ts so the bun:sqlite suite covers the behavioral contract. Verified end-to-end against a deployed server-beta: curl -X POST https://mem.late.app.br/v1/events \ -H 'X-Api-Key: cmem_...' -d '{"events":[]}' # before: 401 "Missing bearer API key" # after: 400 ValidationError (auth ok, body invalid — expected) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Greptile SummaryThis PR makes both auth middleware implementations (
Confidence Score: 5/5Safe to merge — the change is a minimal, additive fallback that does not alter the existing Bearer path. Both middleware files receive the same three-line change. Bearer remains canonical and wins when both headers are present; X-Api-Key is only consulted when Bearer is absent. The 401 error message is updated consistently in both files. Three targeted tests cover the new fallback path, the precedence rule, and the all-absent rejection case. No existing auth behaviour is modified. No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Incoming Request] --> B{local-dev bypass met?}
B -->|Yes| C[Attach local-dev authContext and call next]
B -->|No| D{Parse Authorization Bearer header}
D -->|non-null| E[rawKey = Bearer value]
D -->|null| F{X-Api-Key header present?}
F -->|Yes| G[rawKey = X-Api-Key value]
F -->|No| H[rawKey = null]
H --> I[401 - Missing API key]
E --> J{verifyApiKey}
G --> J
J -->|invalid or expired| K[403 - Forbidden]
J -->|valid| L[Attach api-key authContext and call next]
Reviews (2): Last reviewed commit: "fix(server-beta): update 401 message to ..." | Re-trigger Greptile |
…aders Addresses Greptile P2 feedback on thedotmack#2719: the 401 body still said "Missing bearer API key" after the middleware started accepting X-Api-Key, which made debugging harder for clients sending only X-Api-Key with a typo'd header name. Message now lists both accepted forms in both auth.ts and postgres-auth.ts, keeping the two backends consistent. Test that asserts the body in the both-absent rejection path updated to match. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
requireServerAuth/requirePostgresServerAuthcurrently only readAuthorization: Bearer <key>. Clients using@better-auth/api-keydefault config send raw keys viaX-Api-Key— including the worker bundle shipped from the Windows-canary line (#2699). The mismatch produces:Direct-LAN setups don't hit this because no proxy strips/normalizes headers and the worker's own retry path round-trips through other endpoints. Remote setups behind a proxy (e.g. Cloudflare → Express) fail closed and never capture against the public hostname.
This PR makes the middleware accept either header:
X-Api-Keyis the fallback when Bearer is absent.X-Api-Keyleaks in via a proxy or stale env var).Same change applied to:
src/server/middleware/auth.ts(bun:sqlite-backed)src/server/middleware/postgres-auth.ts(PG-backed — what theserver-betaruntime actually uses)End-to-end verification
Against a
server-betadeployment behind Cloudflare (mem.late.app.br→ Express):X-Api-Key: cmem_...onPOST /v1/eventsAuthorization: Bearer cmem_...(control)Tests
New tests in
tests/server/auth-api-key.test.ts:middleware accepts X-Api-Key header as fallback when Bearer is absentmiddleware prefers Bearer over X-Api-Key when both are presentmiddleware rejects requests with neither Bearer nor X-Api-KeyNo new test for
postgres-auth.ts— the existing repo has no suite that wires a real PG pool for the middleware. The parser logic is byte-identical toauth.ts, so the bun:sqlite suite covers the behavioral contract.Test plan
auth-api-key.test.tsstill pass (Bearer canonical path unchanged)server-betadeployment via Cloudflare confirms regression fixedpostgres-auth.tschange is equivalent (same 3-line edit, comments inline)Related: #2699 (Windows Canary — bundle includes the client using
@better-auth/api-keydefault).🤖 Generated with Claude Code