Files
HIPCTF2/docs/database/admin-operation-tokens.md
T
2026-07-23 12:10:36 +00:00

3.3 KiB

type, title, description, resource, tags, timestamp
type title description resource tags timestamp
database Admin Operation Tokens Single-use confirmation tokens backing the destructive admin System operations (restore-backup, reset-scores, wipe-challenges). backend/src/database/entities/admin-operation-token.entity.ts
database
admin
system
backup
restore
danger-zone
tokens
2026-07-23T12:04:50Z

Schema

Column Type Description
id TEXT PK UUIDv4 generated client-side; the primary key.
user_id TEXT FK user.id of the administrator who issued the token. FK ON DELETE CASCADE to user.
operation TEXT One of restore-backup, reset-scores, wipe-challenges (mirrors ADMIN_OPERATION_KINDS).
token_hash TEXT Hex-encoded SHA-256 of the raw token. The raw token is returned once and never logged or stored. Unique.
issued_at TEXT ISO-8601 timestamp at issuance.
expires_at TEXT ISO-8601 timestamp after which the token is invalid (default TTL: SYSTEM_OP_CONFIRM_TOKEN_TTL_MS, 5 min).
consumed_at TEXT null ISO-8601 timestamp when the token was consumed; null while still valid.

Indexes

Index Purpose
uq_admin_op_token_hash UNIQUE on token_hash — guarantees one-shot use.
idx_admin_op_token_user_op (user_id, operation) — fast per-admin/per-op lookup.
idx_admin_op_token_expiry expires_at — enables the periodic expiry sweep.

Lifecycle

  1. Admin re-authenticates with their current password via POST /api/v1/admin/system/confirmations. The backend verifies the password with Argon2id, then ConfirmationTokenService.issue() generates a 32-byte base64url token, persists only its SHA-256 hash, and returns the raw token plus its expiresAt.
  2. The client passes the raw token (and the stagingId for restores) to the matching destructive endpoint (/restore/commit, /scores/reset, /challenges/wipe).
  3. ConfirmationTokenService.consume() runs the consumption inside a transaction: it looks the token up by hash, validates user_id + operation (+ stagingId for restores), checks expires_at > now, and finally performs a conditional update WHERE consumedAt IS NULL so only the first concurrent caller succeeds. A second attempt returns SYSTEM_TOKEN_REUSED.
  4. purgeExpired() deletes expired rows and consumed rows older than 24h.

Related