Reveal Key Endpoint
B1: return the plaintext exactly once, then null the reveal columns. Cookie-authed + Origin-checked (B7).
Item 5 fix: the expired-window branch now calls expire_stale_reveal itself (status='revoked' + revoked_at + cache invalidation) instead of only nulling the reveal columns and leaving status='active' -- otherwise expire_stale_reveal's own lazy auto-revoke (C7) can never fire again once reveal_ciphertext is already None, and the key stays active (occupying one of the 2 active slots) forever with no one who can ever read it.
Item 4 fix: decrypt BEFORE nulling anything. Pre-fix, the reveal columns were nulled and committed FIRST, and plaintext is None (decrypt failure) was only checked after -- so a rotated or misconfigured BILLCOMMONS_REVEAL_KEY destroyed the only copy of a live credential and then 404'd, leaving the key active and unreadable forever. Now a decrypt failure leaves the ciphertext INTACT and returns 503 (retryable once the reveal key is fixed) instead of destroying it.
Fixlist item 9 fix: the initial SELECT now takes FOR UPDATE on the row, and the eventual claim is a conditional UPDATE ... WHERE reveal_ciphertext IS NOT NULL RETURNING id rather than an unconditional assignment + commit. Pre-fix, two concurrent authenticated reveals for the same key could both observe reveal_ciphertext IS NOT NULL (no lock, no conditional consume), both decrypt, both null, both commit, and both return 200 with the plaintext -- violating B1's "shown to the caller exactly ONCE". FOR UPDATE serializes concurrent callers on this row; the conditional UPDATE is the second, defense-in-depth guarantee (it also protects correctness if FOR UPDATE is ever a no-op, e.g. a dialect that doesn't implement row locks) -- a second caller unblocked after the first commits re-reads reveal_ciphertext IS NULL and 404s nothing_to_reveal instead of double-revealing. Kept as a SEPARATE conditional UPDATE (not folded into one UPDATE ... RETURNING) so the decrypt-before-destroy property from item 4 above still holds: a decrypt failure leaves the ciphertext intact rather than being destroyed by an all-in-one claim.
Request
- Base URL: https://api.billcommons.org
- URL: https://api.billcommons.org/api/v1/account/keys/{key_id}/reveal
- Auth: none declared
Path parameters
Response
Successful Response
Changes
No recorded changes to this operation across all 1 revision of this API.