Server design-review finding (independent of the core review, though it rhymes with core #56's "never silently widen/narrow" principle).
Problem
The auth middleware maps a failed token lookup to auth = null — the same state as no Authorization header at all (src/middleware/auth.ts:22-23):
const result = await ctx.adapter.lookupToken(token);
c.set('auth', result ? { entityId: result.entityId } : null);
On requireAuth() routes this surfaces as 401, which is fine. But on the optional-auth routes — GET /records, GET /records/:id, permissions/associations/versions reads, GET /attachments/:fileId — a request bearing an expired, revoked, or garbage token is silently served as anonymous: the caller gets the public-only subset with a 200.
That's the dangerous shape:
- A client whose token was revoked (or expired mid-session) doesn't find out. Its queries "work" and quietly return a narrower result set than the same query returned yesterday — silent capability degradation on a read of trust, the exact failure mode the review kept killing on the wire (core #56, #50).
- It also contradicts the spec, which is already unambiguous: "The adapter sends the token if configured; the server returns 401 if missing or invalid" (spec §Authentication). Missing ⇒ anonymous is the useful reading for public access, but invalid must be 401.
Direction
Distinguish the three states in the middleware: no Authorization header → anonymous (auth = null); valid token → authenticated; present-but-invalid token → 401 immediately, with the bad token case using #33's error body. A malformed header (Authorization: Basic …, missing Bearer prefix) should also be 401 rather than silently anonymous.
Worth a conformance fixture once #32 lands — adapter-api needs to be able to tell "your session died" from "empty result."
Work items
Refs #32, #33; principle kin: haverstack/core#56.
Server design-review finding (independent of the core review, though it rhymes with core #56's "never silently widen/narrow" principle).
Problem
The auth middleware maps a failed token lookup to
auth = null— the same state as noAuthorizationheader at all (src/middleware/auth.ts:22-23):On
requireAuth()routes this surfaces as 401, which is fine. But on the optional-auth routes —GET /records,GET /records/:id, permissions/associations/versions reads,GET /attachments/:fileId— a request bearing an expired, revoked, or garbage token is silently served as anonymous: the caller gets the public-only subset with a 200.That's the dangerous shape:
Direction
Distinguish the three states in the middleware: no
Authorizationheader → anonymous (auth = null); valid token → authenticated; present-but-invalid token → 401 immediately, with thebad tokencase using #33's error body. A malformed header (Authorization: Basic …, missingBearerprefix) should also be 401 rather than silently anonymous.Worth a conformance fixture once #32 lands —
adapter-apineeds to be able to tell "your session died" from "empty result."Work items
codevocabulary, structured bodies, 400/422 discipline (core #53) #33Refs #32, #33; principle kin: haverstack/core#56.