Signup ceremony: paste URI, validate against posta-server, passkey + PRF #5

Open
opened 2026-05-14 23:17:15 +02:00 by arne · 0 comments
Owner

Parent

#2

What to build

End-to-end signup. A user clicks the invite URL minted in Slice 2 (/register?code=<code>), sees a single textarea labeled "Paste your posta+v1:// URI", and submits. The backend parses the URI, calls GET <server>/api/v1/identity with the pasted bearer token to validate, then runs the WebAuthn registration ceremony with the PRF extension. On ceremony success, AES-256-GCM-encrypts the bearer token using the PRF-derived key and stores the user row; the invite is marked consumed atomically with the row insert. Any failure leaves the invite reusable.

Acceptance criteria

  • GET /register?code=<valid> returns the paste page
  • GET /register?code=<expired|consumed|unknown> returns a generic "this invite link is invalid" page
  • POST with a malformed URI returns the paste page with an inline error
  • POST with a URI whose posta-server returns 401 on /identity returns "token rejected by your posta-server"
  • POST with a URI whose posta-server is unreachable returns "couldn't reach your posta-server"
  • On valid URI: passkey ceremony initiates with the PRF extension via navigator.credentials.create()
  • PRF salt is fixed per deployment (configurable); the 32-byte PRF output is used as the AES-256-GCM key
  • Token ciphertext + per-message random nonce are persisted alongside credential_id and credential_public_key
  • Invite consumption + user row insert happen in a single SQLite transaction
  • Aborting the passkey ceremony (browser cancel) leaves the invite reusable; no user row exists
  • On success, user is redirected to / (will resolve to inbox after Slice 5)
  • Unit tests in internal/auth cover: PRF-derived AES-GCM encrypt+decrypt roundtrip; ceremony state machine rejects malformed challenges; same PRF output decrypts the same ciphertext deterministically

Blocked by

## Parent #2 ## What to build End-to-end signup. A user clicks the invite URL minted in Slice 2 (`/register?code=<code>`), sees a single textarea labeled "Paste your `posta+v1://` URI", and submits. The backend parses the URI, calls `GET <server>/api/v1/identity` with the pasted bearer token to validate, then runs the WebAuthn registration ceremony with the PRF extension. On ceremony success, AES-256-GCM-encrypts the bearer token using the PRF-derived key and stores the user row; the invite is marked consumed atomically with the row insert. Any failure leaves the invite reusable. ## Acceptance criteria - [ ] GET `/register?code=<valid>` returns the paste page - [ ] GET `/register?code=<expired|consumed|unknown>` returns a generic "this invite link is invalid" page - [ ] POST with a malformed URI returns the paste page with an inline error - [ ] POST with a URI whose posta-server returns 401 on `/identity` returns "token rejected by your posta-server" - [ ] POST with a URI whose posta-server is unreachable returns "couldn't reach your posta-server" - [ ] On valid URI: passkey ceremony initiates with the PRF extension via `navigator.credentials.create()` - [ ] PRF salt is fixed per deployment (configurable); the 32-byte PRF output is used as the AES-256-GCM key - [ ] Token ciphertext + per-message random nonce are persisted alongside `credential_id` and `credential_public_key` - [ ] Invite consumption + user row insert happen in a single SQLite transaction - [ ] Aborting the passkey ceremony (browser cancel) leaves the invite reusable; no user row exists - [ ] On success, user is redirected to `/` (will resolve to inbox after Slice 5) - [ ] Unit tests in `internal/auth` cover: PRF-derived AES-GCM encrypt+decrypt roundtrip; ceremony state machine rejects malformed challenges; same PRF output decrypts the same ciphertext deterministically ## Blocked by - #4
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
posta/chat#5
No description provided.