feat(auth): mandatory 2FA (TOTP + WebAuthn passkeys) with hard enrollment gate, AAL2 step-up, and single-use recovery codes
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
-- 0002_mfa_recovery.sql
|
||||
-- Additive, idempotent migration for mandatory-MFA recovery codes.
|
||||
-- Safe to run multiple times.
|
||||
|
||||
-- 1. Recovery codes -------------------------------------------------------
|
||||
-- One row per single-use recovery code. We only ever store a SHA-256 hash of
|
||||
-- the code; the plaintext is shown to the user exactly once at generation.
|
||||
create table if not exists public.mfa_recovery_codes (
|
||||
id bigint generated always as identity primary key,
|
||||
user_id uuid not null,
|
||||
code_hash text not null,
|
||||
used_at timestamptz,
|
||||
created_at timestamptz not null default now()
|
||||
);
|
||||
|
||||
create index if not exists mfa_recovery_codes_user_id_idx
|
||||
on public.mfa_recovery_codes (user_id);
|
||||
|
||||
-- Fast lookup of an unused code by (user, hash) during redemption.
|
||||
create unique index if not exists mfa_recovery_codes_user_hash_uidx
|
||||
on public.mfa_recovery_codes (user_id, code_hash);
|
||||
|
||||
-- 2. Lock down with RLS, NO policies ---------------------------------------
|
||||
-- RLS enabled with NO policies so anon/authenticated roles cannot read or
|
||||
-- write recovery codes at all. The application generates and redeems codes
|
||||
-- exclusively through the service-role client, which bypasses RLS.
|
||||
alter table public.mfa_recovery_codes enable row level security;
|
||||
|
||||
comment on table public.mfa_recovery_codes is
|
||||
'Single-use MFA recovery codes (SHA-256 hashed). RLS enabled with no policies: service-role only.';
|
||||
Reference in New Issue
Block a user