From 9974736587681c8bf9881cc0894bbfc8dd232cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 8 Aug 2026 16:26:06 +0000 Subject: [PATCH] mobile-api-keys: record what the client actually does now MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The client half exists in monorepo-mobile as of f29774a, with OffChat as the proof of concept, so this document is no longer purely forward-looking. Adds a section covering what shipped, the two places the advice here was wrong about the client — one key per app is not possible on mobile (shared-session puts one credential in front of all nineteen apps), and signout was never the problem, distress signout was — and two decisions worth a second opinion: clearing the credential on 401 only, and leaving the traded-in JWT to expire rather than blacklisting it, because that handler clears vault tokens keyed on the user. --- docs/mobile-api-keys.md | 64 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/docs/mobile-api-keys.md b/docs/mobile-api-keys.md index 0fc58dc2..399563b3 100644 --- a/docs/mobile-api-keys.md +++ b/docs/mobile-api-keys.md @@ -6,6 +6,15 @@ planned-but-missing unless it says so. **The mobile apps are not built here.** This document is the boundary: what the server now accepts, what changes in `monorepo-mobile`, and what does not. +> **Update, later on 2026-08-08 — the client side now exists, in `monorepo-mobile`.** `@officer/core` +> gained `services/api-keys.ts` and `useAuth` learned that a key is a session; **OffChat (`apps/chat`) is +> the proof of concept** and is the only app wired up. The other eighteen are untouched and behave +> exactly as before. +> +> **None of it has been compiled or run** — it was written on the Linux box, which has no `node_modules` +> for that repo and no Mac. Read §"What the client actually does now" for what shipped, what it changed +> about the advice below, and the two things this document got wrong about the client. + --- ## The short version @@ -256,6 +265,61 @@ document was written, along with a WebSocket upgrade on `/api/cliamp/ws?token="`, where the app half records only who did the minting. + +**2. "Do not call signout with a key" was not the whole story — `distressSignout` was the real problem.** +Under duress the app clears the credential locally _first_, then best-effort revokes server-side via +`POST /api/auth/revoke`. That endpoint blacklists a JWT and does nothing whatever for a key, so the +moment a phone starts holding keys, distress sign-out silently stops killing the credential. **And it +matters more here than it ever did for a JWT: an unrevoked session token still expires in thirty days, an +unrevoked key never does.** + +`revokeApiKeyByCredential(baseUrl, key)` is the fix — raw `fetch` (the credential is already out of +storage by then, so it cannot go through `request()`), `GET /api/api-keys`, match on the `prefix` the +server kept in the clear, `DELETE /api/api-keys/:id`. + +### Two client-side decisions worth a second opinion + +- **A 401 is now the only thing that clears the credential.** `useAuth`'s `/api/auth/me` query used to + clear on _any_ throw. A network error is `ApiError(0)` and a timeout is `ApiError(408)`, so launching + with no connectivity destroyed a working session and dropped the user at a login screen that needs the + network to be useful — and a 403 produced the logout loop this document warns about. Pre-existing, not + caused by keys, fixed while in there. +- **The traded-in JWT is left to expire, not blacklisted.** Minting a key and keeping it drops a live + thirty-day token on the floor, and `POST /api/auth/signout` is the obvious tidy-up — but that handler + also calls `clearVaultTokens(user.id)`, keyed on the **user**, not the session. Blacklisting the + discarded token would therefore drop a vault session brokered on another of the owner's devices, as a + side effect of signing in. Not worth it for a token nothing holds and nothing will send again. + +### Still not built on the client + +No key-management screen in any app — listing and revoking remain web-only, per "Not built" above. The +service verbs exist (`listApiKeys`, `revokeApiKey`) if that changes. + +--- + ## Where this lives on the server - `src/servers/auth-token.ts` — the key format and `resolveAuthToken`, the single function that turns a