| title | Auth Training Wheels — Implementation Plan | ||
|---|---|---|---|
| description | User stories, architecture, and build phases for the opt-in token-auth demo API | ||
| audience |
|
||
| status | awaiting-decisions |
Wiki Home › Future Features › Plans
Plan for the Auth Training Wheels proposal: one clearly-marked demo API requiring a bearer token minted with a single click — no account, no email. The mechanics are ordinary Express middleware; the delicate part is protecting the site's "no keys, no sign-up" promise, which is why this feature's decision log starts with a go/no-go framing decision (D0).
- Meet the 401. As a learner, when I fetch the demo API without credentials I get a 401 whose body tells me exactly what's missing and where to get it — the failure is the first lesson.
- Mint without friction. As a learner, I can get a token with one click on the site (or one
POST /auth-demo/tokenfrom code) — no form, no email, nothing stored about me. - Use the header. As a learner, I add
Authorization: Bearer <token>to my Playground fetch and the same request now returns 200 — experiencing the exact mechanic every real API will demand. - Experience expiry. As a learner, I can watch a token expire and see the expired 401 (worded differently from the missing 401), then re-mint — the full lifecycle, compressed into one sitting.
- The promise holds. As any visitor, every other API remains keyless; the demo is visibly labeled a lesson, and nothing about the site suggests auth is creeping in.
sequenceDiagram
participant L as Learner
participant T as POST /auth-demo/token
participant M as Auth middleware
participant J as jsonRouter (vault dataset)
L->>M: GET /auth-demo/vault (no header)
M-->>L: 401 { error, message: "No Authorization header…", hint }
L->>T: POST (optionally ?ttl=60)
T-->>L: { token, tokenType: "Bearer", expiresIn }
L->>M: GET + Authorization: Bearer …
M->>J: verified → pass through
J-->>L: 200 data
- No server-side storage. Token =
base64url(header).base64url(payload).base64url(HMAC-SHA256(secret, header.payload))— the real JWT wire format, built with Node's built-incrypto. Zero new dependencies, consistent with the owned-code posture of Why a Custom JSON Router, and what learners see decodes on jwt.io like the tokens they'll meet in the wild. (Hand-rolled vs.jsonwebtokendependency is D2.) - Payload:
{ iat, exp }and nothing else — deliberately no user identity, reinforcing "no accounts". - Secret:
AUTH_DEMO_SECRETenv var with a dev fallback; documented in deployment. Rotating it invalidates outstanding tokens, which is acceptable (and even a realistic lesson). - Lifetime: default per D3; a
?ttl=mint parameter (floor ~30 s) exists so classrooms can demonstrate expiry in minutes, not by waiting.
POST /auth-demo/token— mints and returns{ token, tokenType: "Bearer", expiresIn }. Rate-limited (minting is cheap but shouldn't be a loop target).- Auth middleware guards
/auth-demo/vault/*, then hands off to the standard jsonRouter over a newvault.jsondataset — full CRUD/query behavior behind the header, so everything learners know still works once authenticated. - Every failure teaches, in the documented error shape plus a
hint:- No header → 401 "No Authorization header — the format is
Authorization: Bearer <token>; mint one at POST /auth-demo/token." - Malformed / bad signature → 401 "That doesn't look like a token from this API…"
- Expired → 401 "Your token expired at — tokens here live N minutes so you can practice re-authenticating."
- All 401s carry
WWW-Authenticate: Bearer(witherror="invalid_token"where applicable), and CORS must expose it (extends theexposedHeaderslist from the HTTP Inspector plan).
- No header → 401 "No Authorization header — the format is
- Whether an
?api_key=variant also exists is D1.
- One small themed dataset (
vault.json— e.g. "secret lab inventory": the content should make locked-ness feel playful, not corporate). Normal.json.backuptwin, participates in data reset like everything else. - Registry integration: the API registry entry gets a
requiresAuth: truemetadata flag so the client can badge it and the details page can render the mint UI. The registry generator and the/frontendpayload carry the flag through. - Mount order note:
/auth-demomounts as its own router in sampleapis.js; the vault dataset must not also be auto-mounted keyless at/vaultby the base-apis loop — either its file lives outside the auto-registered pattern or the registry flag excludes it from the default mount. This is the one integration subtlety; a test should pin it.
Scope per D4; the recommended v1:
- Lock badge on the API card and details page, with one explanatory sentence ("This demo API teaches token auth — every other API stays keyless").
- A "Mint token" button on the details page showing the token, a copy button, and a live expiry countdown.
- Playground starter snippets for this API: the three-act flow (fetch → 401, mint, fetch with header → 200) as separate tabs, following snippets.ts. The sandbox sets arbitrary fetch headers already — no Playground changes needed.
| Phase | Scope | Done when |
|---|---|---|
| 1. Token module | Sign/verify per D2, ttl handling, unit tests incl. tampered and expired tokens |
Jest: valid/expired/tampered/garbage tokens all classified correctly |
| 2. Routes + middleware | Mint route, middleware, teaching 401 bodies, WWW-Authenticate, rate limit |
supertest: full lifecycle incl. every failure body; keyless /vault mount does not exist |
| 3. Dataset + registry | vault.json + backup, requiresAuth registry flag through /frontend |
Reset restores it; client receives the flag |
| 4. Client UI + snippets | Badge, mint button + countdown, starter snippets per D4 | The 401 → mint → 200 → expiry walkthrough works in the Playground |
| 5. Copy & docs | A docs/api/ page for the auth demo; wording pass against the D0 guardrails |
Docs reviewed with the promise-protection checklist from D0 |
- Server: this feature is highly testable — token classification, every 401 variant's body and header, TTL floor/ceiling, mount-order pinning. All Jest + supertest in server/tests.
- Manual: run the full learner journey in the Playground against local, ideally with the HTTP Inspector showing the 401s and
WWW-Authenticate— the two features compose into the complete lesson.
- OAuth flows, refresh tokens, scopes/roles — the lesson is "credential in a header", not identity architecture.
- More than one protected dataset. One vault; the promise stays visibly intact.
- Any persistence of who minted what (there is nothing to persist).
- server/routes — new
auth-demo.jsrouter - server/utils/jsonRouter.js — reused behind the middleware
- server/sampleapis.js — mounting
- server/utils/getAPIListData.js — registry flag plumbing
- client/src/components/Playground/snippets.ts — auth starter snippets
- Auth Training Wheels — Decisions — including the D0 go/no-go
- Proposal · Roadmap
- Error Responses · API Registry
- Guided Challenges plan — a future auth track consumes this