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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions