Add nullable passphrase hash to clinician share links

Introduce a nullable passphrase_hash column on the share-link model so a
link can carry a passphrase second factor. The column is nullable, so
every link created before this change keeps the possession-only contract
and the public view renders for it without a gate.

Migration 0181 hand-authors the ALTER TABLE; the column stores only the
HMAC hash, never the raw passphrase.
This commit is contained in:
Marc-André Bombeck committed 2026-06-19 22:03:25 +02:00
1 parent f4812f9af2
commit 53d41c190b
2 files changed
+14

No files matched your search

@@ -0,0 +1,7 @@
-- v1.18.7 — passphrase second factor for clinician share links.
-- A leaked share URL on its own no longer opens the record: every new
-- link carries a high-entropy passphrase, stored only as its HMAC-SHA256
-- hash (same key as token_hash). The column is NULLABLE so every link
-- minted before this change keeps the possession-only contract and the
-- public view renders for it without a gate.
ALTER TABLE "clinician_share_links" ADD COLUMN "passphrase_hash" TEXT;
+7
View File
@@ -3856,6 +3856,13 @@ model ClinicianShareLink {
userId String @map("user_id")
/// HMAC-SHA256 of the raw `hls_` token (same scheme as `ApiToken.tokenHash`).
tokenHash String @unique @map("token_hash")
/// v1.18.7 — HMAC-SHA256 of the passphrase second factor (same key as
/// `tokenHash`). NULL for legacy links minted before the passphrase gate;
/// those keep the possession-only contract (the public view renders without
/// a gate). A non-null value forces the unlock gate before the record paints.
/// The raw passphrase is returned exactly once at create time and stored only
/// as this hash — there is no recovery path.
passphraseHash String? @map("passphrase_hash")
/// Human label the owner sets (e.g. a clinic/specialty note). Plaintext, bounded.
label String