shinkan-jinkendo/backend/migrations/096_login_lockout_and_totp.sql
Lars 32b28a93f3
Some checks failed
Deploy Development / deploy (push) Successful in 49s
Test Suite / pytest-backend (push) Successful in 47s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 15s
Test Suite / k6 /health Baseline (push) Successful in 35s
Test Suite / playwright-tests (push) Failing after 5m43s
Harden login, sessions, and media access before the dev deploy.
Limit brute-force attempts, require admin TOTP, and keep session tokens out of media URLs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 07:45:53 +02:00

30 lines
1.1 KiB
SQL

-- Login-Sperre, Sitzungszweck (Vollzugriff / 2FA-Einrichtung / 2FA-Challenge), TOTP.
ALTER TABLE profiles
ADD COLUMN IF NOT EXISTS failed_login_count INTEGER NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS locked_until TIMESTAMP,
ADD COLUMN IF NOT EXISTS totp_secret TEXT,
ADD COLUMN IF NOT EXISTS totp_pending_secret TEXT,
ADD COLUMN IF NOT EXISTS totp_enabled BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE sessions
ADD COLUMN IF NOT EXISTS purpose VARCHAR(32) NOT NULL DEFAULT 'full',
ADD COLUMN IF NOT EXISTS failed_attempts INTEGER NOT NULL DEFAULT 0;
UPDATE sessions
SET purpose = 'password_reset'
WHERE token LIKE 'reset_%' AND purpose = 'full';
CREATE INDEX IF NOT EXISTS idx_sessions_profile_purpose ON sessions (profile_id, purpose);
CREATE TABLE IF NOT EXISTS profile_recovery_codes (
id SERIAL PRIMARY KEY,
profile_id INTEGER NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
code_hash VARCHAR(64) NOT NULL,
used_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_profile_recovery_codes_profile
ON profile_recovery_codes (profile_id);