Skip to content

Invalid bearer tokens are silently treated as anonymous instead of 401 #44

Description

@cuibonobo

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions