Per-Reader userscript credential with UI install and rotation (#24) #32
Reference in New Issue
Block a user
Delete Branch "feat/per-reader-userscript-credential"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #24. Child of #18; based on current main (includes Postgres, Reader table, Discord OAuth).
What
Each Reader's userscript credential is derived from
TOKEN_KEY, their Discord id and a token epoch (HMAC-SHA256, hex); only its SHA-256 sits inreaders.token_sha256(newtoken_epochcolumn, migration 0006). One credential authenticates the script download path and the API bearer header.internal/token: derivation + hashing; the seed refreshes the owner's epoch-0 hash only before first rotation, so a restart can never resurrect a rotated-away credentialhttpmw.Auth/ResolveReader: acting Reader resolved from the credential hash, stashed in request context; the retired globalAPI_TOKENresolves to the owner untilAPI_TOKEN_GRACE_UNTIL(enforced in code, logged per use) on both the bearer and script-download paths__API_TOKEN__; a legacy-path request during grace serves the derived credential, so installed devices self-migrate on their next update poll__API_TOKEN__placeholders; the committed global-token literal is removedDesign note
Credentials are derived rather than stored-random because the server must rebuild install URLs after restarts while the DB holds only hashes. HMAC output is high-entropy and unbrute-forceable; the AC's intent (unguessable, DB-leak-proof) is met.
Deploy (also in DEPLOY.md)
TOKEN_KEY(openssl rand -hex 32) — required; changing it later invalidates every credential.API_TOKEN+ setAPI_TOKEN_GRACE_UNTILfor the 14-day window.0ef5286), so rotation is what kills it.Verification