Skip to content

Authentication

uwwoe’s authentication model is intentionally minimal: anonymous bearer tokens, no accounts, no OAuth, no API keys.

Tokens

A token is issued by POST /api/v1/state (the only endpoint that mints one). It is:

  • ≥256 bits of cryptographic randomness, base64url-encoded with no padding.
  • Returned in the response body exactly once.
  • Sent on subsequent requests as Authorization: Bearer <token>.

The backend stores only an HMAC verifier under a rotatable key — not the token itself. (ADR 0011)

Required vs optional

Endpoint familyToken required?
GET /api/v1/health, /diagnostics, /indexNo
GET /api/v1/courses, /courses/{..}/{..}, /credentials, /credentials/{..}, /source-references/{..}No
POST /api/v1/query/course-unlock, course-impact, credential-progress, credential-gap-summary, what-if, advisoryRequired when querying against persisted state; optional with state_mode: "supplied"
POST /api/v1/graph/views/*Required for state-overlay views (unlock-overlay, target-relevance); optional for stateless views
POST /api/v1/stateNo (mints the token)
GET/PATCH/PUT/DELETE /api/v1/state/current, /state/current/export, /state/current/migration-previewRequired
GET /internal/metricsInternal operator bearer (UWSCRAPE_INTERNAL_TOKEN), separate from state tokens

Header only

The token is only accepted via Authorization: Bearer <token>.

Authorization: Bearer <token>

Tokens supplied as:

  • Query parameters (?token=...) → rejected.
  • URL fragments → ignored (browsers don’t send fragments to the server, but the backend would reject them anyway).
  • Cookies → not used at all.

This is enforced server-side and asserted by test gates.

What never logs the token

  • The runtime access log.
  • The /internal/metrics endpoint.
  • Atlas worker payloads.
  • Shareable view URLs.
  • Test artifacts and CI logs.

Gate 4 of every phase verifies this.

Errors

StatusCodeCause
401missing_tokenA token-required endpoint was called without Authorization.
401unauthorizedToken did not match any stored verifier (revoked, hard-deleted, or never existed).
403token_in_queryA token was supplied via query parameter — rejected by policy.

A 401 unauthorized after a hard delete is indistinguishable from a 401 unauthorized for any never-existing token. This is by design (ADR 0019).

Lifecycle

  1. MintPOST /api/v1/state → receive token once.
  2. Use — every subsequent state-bearing call carries Authorization: Bearer <token>.
  3. Lose — token gone, plan unrecoverable (no identity to recover against).
  4. DeleteDELETE /api/v1/state/current with explicit confirmation → hard delete, tombstone, token retired.

Want more?