axe/internal/db/migrations/0007_password_reset.sql
2026-10-18 22:07:25 +03:00

43 lines
2.7 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 0007_password_reset — сброс пароля по одноразовому токену.
--
-- SMTP в проекте нет по решению: токен выдаёт админ
-- (POST /users/:id/password-reset) и передаёт его пользователю сам —
-- любым каналом, кроме почты внутри приложения. Пользователь меняет
-- пароль через POST /auth/reset-password.
--
-- В таблице лежит ТОЛЬКО sha256 токена: утекшая база не даёт
-- переиспользовать выпущенные токены. Свойство «увидел — значит чужой»:
-- зная хеш, токен не восстановить (256 бит поискового пространства).
--
-- Правила, проверяемые в SQL, а не в хендлерах:
-- * токен одноразовый: WHERE used_at IS NULL в самом UPDATE;
-- * срок: WHERE expires_at > now(), срок всегда позже выпуска (CHECK);
-- * у пользователя действует ровно один свежий токен — выпуск нового
-- гасит предыдущие (WITH в CreateResetToken);
-- * вместе с паролем закрываются refresh-сессии (в коде, сразу после
-- успешного UPDATE — смена пароля без разлогина бессмысленна).
--
-- Строки живут после использования (история), чистка — по желанию:
-- просроченные токены бесполезны, таблица растёт медленно.
--
-- Применённый файл не редактировать (checksum в schema_migrations).
CREATE TABLE password_reset_tokens (
id bigserial PRIMARY KEY,
user_id uuid NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
-- sha256 токена в hex; сам токен в базе не хранится
token_hash text NOT NULL UNIQUE,
created_at timestamptz NOT NULL DEFAULT now(),
expires_at timestamptz NOT NULL,
-- NULL — ещё не использован; момент смены пароля
used_at timestamptz,
CONSTRAINT password_reset_tokens_expiry
CHECK (expires_at > created_at)
);
-- Выпуск нового токена гасит старые конкретного пользователя,
-- поэтому выборка «незакрытые токены пользователя» должна быть быстрой.
CREATE INDEX IF NOT EXISTS idx_password_reset_tokens_user
ON password_reset_tokens (user_id, created_at);