feat(api,web): access model v2 P2 - sign once, listen freely#78
Merged
Conversation
One SIWE-style signature opens a ~24h session; every later key request rides
a bearer token instead of a fresh wallet signature. The token proves identity
only - the on-chain access check still runs per track on every request.
Key service:
- sessionTokens.ts: HMAC-SHA256 tokens over a strict claim shape, key
HKDF-derived from CONTENT_KEY_MASTER_SECRET with a dedicated info label (no
new secret, no shared bytes with content keys), constant-time verify, 24h
TTL, jti revocation (in-memory, process-lifetime - documented boundary).
- signatures.ts: canonical SIGN_IN message + verifySignInRequest (expiry ->
signature -> nonce consumption, same fail-closed order).
- POST /api/auth/session (401 on bad signature, 503 when unconfigured so the
per-request path remains) and idempotent POST /api/auth/logout.
- key-request route accepts {sessionToken, purpose}; the chain check runs
against the token's address, never client input. 13 new tests (37 total).
Web:
- ensureDotifySession: reuse the stored per-address token (refresh margin),
else one sign-in signature; requestContentKey rides the token, retries once
with a fresh sign-in on 401, and falls back to per-request signing when the
backend lacks session support (404/503).
- Wallet disconnect also signs the session out server-side and forgets the
stored token.
Threat model gains the session-auth boundary section; ticket 24 delivery
notes updated. API 37/37 + typecheck; web 90 unit, lint 0 errors, build ok.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #77 (P1b). Ticket 24 P2: session-based key delivery - one wallet signature per ~24h instead of one per listen.
Design (as approved in
dotify-v2-access-and-streaming.md)SIGN_INmessage (nonce, expiry, replay-protected - same fail-closed order as key requests: expiry -> signature -> nonce consumption).POST /api/auth/sessionexchanges it for a bearer token.POST /:contentHash/key-requestaccepts{sessionToken, purpose}. The service verifies the token and runs the on-chain access check against the token's address on every request - the token proves identity, never access.ensureDotifySessionreuses the stored per-address token (60s refresh margin); on 401 it re-signs once and retries; backends without session support (404/503) fall back to the per-request signed path unchanged. Wallet disconnect signs out server-side (POST /api/auth/logout, idempotent) and forgets the token.Token design - no new secret, no new dependency
HMAC-SHA256 over a strict claim shape (
address, chainId, jti, iat, exp), key HKDF-derived fromCONTENT_KEY_MASTER_SECRETwith a dedicated info label - token keys and content keys never share bytes, and operators provision nothing new. Constant-time signature comparison; 24h TTL;jtirevocation blocklist.Boundaries (stated in the threat model, same PR)
Verification
tsc --noEmitclean.tsc -b+ build clean.Merge order: #76 -> #77 -> this.