mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-17 07:21:32 +02:00
The shared server-nonce store every signed action consumes (admin login,
first-run setup, self-service erasure). DB-backed so consumption is atomic and
correct under concurrent requests and a future load-balanced fleet:
DELETE FROM challenges WHERE nonce=$1 AND purpose=$2 AND expires_at > NOW()
RETURNING subject
Winning the race returns one row; zero rows = replay/expired/wrong-purpose =>
reject (no TOCTOU). issue_challenge mints a 256-bit nonce bound to a purpose and
optional subject; consume_challenge is the atomic single-use step that pairs with
verify_signed_action (already built, §0.1). Periodic expiry sweep bounds the
table; the table holds no secret and is documented as backup-excluded
(ephemeral, links pubkey->time).
migration 20260625000001; ConsumedChallenge exported; 3 L1 tests (consume-once /
replay-rejected, wrong-purpose rejected, expired rejected).
24 lines
1.3 KiB
SQL
24 lines
1.3 KiB
SQL
-- Unified server-nonce (challenge) store for signed actions.
|
|
--
|
|
-- One mechanism for every server-issued single-use nonce — admin login, first-run
|
|
-- setup, and self-service erasure — consumed atomically so a nonce can be used at
|
|
-- most once even across concurrent requests / fleet nodes:
|
|
-- DELETE FROM challenges WHERE nonce=$1 AND purpose=$2 AND expires_at > NOW()
|
|
-- RETURNING subject;
|
|
-- Winning the race returns exactly one row; zero rows = replay/expired ⇒ reject.
|
|
-- This is the DB-backed variant (correct under a shared database and a future
|
|
-- load-balanced fleet); it carries no long-lived secret.
|
|
--
|
|
-- Operational note: this table is ephemeral (it links "pubkey acted at time T")
|
|
-- and should be EXCLUDED from backups.
|
|
CREATE TABLE challenges (
|
|
nonce TEXT PRIMARY KEY, -- 32 random bytes, hex
|
|
purpose TEXT NOT NULL, -- 'admin_login' | 'setup' | 'erasure_request' | ...
|
|
subject TEXT, -- optional bound subject (e.g. target pubkey/id)
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
expires_at TIMESTAMPTZ NOT NULL
|
|
);
|
|
|
|
-- Supports the periodic expiry sweep (the consume query is keyed by the PK).
|
|
CREATE INDEX idx_challenges_expires_at ON challenges (expires_at);
|