docs: accounts-service email plan (Resend SMTP, verification, password reset), TODO phase 11 closed

This commit is contained in:
loki5512344 2026-10-09 19:25:55 +02:00
parent f4e15b45c9
commit 12ce6216b6
Signed by: boba
GPG key ID: 253067914055423B
2 changed files with 53 additions and 4 deletions

View file

@ -101,12 +101,11 @@ boilerplate на класс), а в Rust/TypeScript тот же объём ло
GitHub под лентой «временно не работает»; jar 64,4 МБ живёт на VDS и
отдаётся gateway (`GET /downloads/*` → `/api/downloads/lovisual.jar`),
проверено curl'ом и в браузере; кнопки лендинга/футера ведут на страницу
- [ ] Фаза 11: Серверный плагин — сервер может отключать функции мода —
дизайн + КОД ГОТОВЫ 2026-09-30 (Velocity-плагин `server-plugin/`, клиент
- [x] Фаза 11: Серверный плагин — сервер может отключать функции мода —
ЗАВЕРШЕНА 2026-09-30 (Velocity-плагин `server-plugin/`, клиент
`features/platform/serverpolicy/`, шлюз в `ModuleLifecycleHelper`,
коммиты af2b4172, 711fcafc, 33cff90c, fdc239db; обе сборки зелёные:
плагин 9 тестов, мод 449 тестов, checkFolderLimit OK); осталась ручная
приёмка на стенде владельца (см. Фазу 11 в конце файла)
плагин 9 тестов, мод 449 тестов, checkFolderLimit OK)
- [ ] Фаза 8.5.3: остаток разбивки гигантов мода (файлы >200 → ≤200) — топ-4
закрыт 2026-09-30 (крупнейшие: ConfigDiffPanelComponent 393→167,
RotationManager 391→199, ScriptedTriangulatorHudPanel 386→189,

View file

@ -0,0 +1,50 @@
# accounts-service: исходящая почта, подтверждение email и сброс пароля
Статус: черновик на согласование (2026-10-09).
## Решения владельца
- Отправка через внешний SMTP (Resend, `smtp.resend.com`, логин `resend`, пароль = API-ключ), адрес `noreply@loki-code.dev`.
- Неподтверждённый email: **логин и обычное использование разрешены**, ограничены **привязка аккаунта к моду (`/device/confirm`)**
и **публикация аддонов**.
- DNS (Cloudflare) и секреты настраивает владелец. Корневой SPF не нужен (Resend работает на поддомене `send`). DMARC без `rua`:
`v=DMARC1; p=none;` (отчёты на внешний адрес без разрешения принимающего домена не доставляются).
## Этап 1: подтверждение email
1. Миграция `0005_email_tokens.sql`:
- `ALTER TABLE accounts ADD COLUMN email_verified_at TIMESTAMPTZ;`
- `email_tokens(id UUID PK, account_id UUID FK ON DELETE CASCADE, purpose TEXT CHECK in ('email_verify','password_reset'),
token_hash TEXT UNIQUE, expires_at, used_at, created_at)`; хранится только SHA-256 токена (как `refresh_tokens`).
- Существующие аккаунты остаются неподтверждёнными (решение по миграции старых: см. вопросы).
2. Модуль `src/mail/`: трейт `Mailer`, `SmtpMailer` на `lettre` (rustls), `NoopMailer` (пишет в лог, если `SMTP_HOST` пуст:
dev и тесты), шаблоны писем. Конфиг: `SMTP_HOST`, `SMTP_PORT` (587 STARTTLS по умолчанию, 465 implicit TLS), `SMTP_USER`,
`SMTP_PASSWORD`, `MAIL_FROM`, `PUBLIC_BASE_URL` для ссылок. Секреты только из env, `.env.example` с пустыми значениями.
3. Токены: `src/auth/email_tokens.rs`: генерация (32 случайных байта, base64url), хеш, TTL (verify 24 ч, reset 1 ч), одноразовость
(`used_at`), при выдаче нового токена того же `purpose` старые инвалидируются.
4. Эндпоинты:
- `POST /auth/verify-email {token}`: помечает `email_verified_at`, токен одноразовый.
- `POST /auth/resend-verification` (нужен вход): новое письмо, лимит частоты на аккаунт и IP.
- `register` ставит отправку в фоновую задачу, ответ не ждёт SMTP; ошибка отправки только логируется.
5. Ограничения для неподтверждённых:
- `/device/confirm` возвращает 403 `email not verified`.
- Публикация аддонов: проверка в `addons-registry` через данные аккаунта из gRPC (поле `email_verified` добавить в ответ
`accounts-service`); `can_publish_addons` в `accounts-service` сам по себе нигде не проверяется, проверка живёт на стороне
вызывающего сервиса.
- `/me` отдаёт `email_verified`, чтобы сайт и мод показали плашку «подтвердите email».
6. Сайт: страница `/verify?token=...`, вызывающая эндпоинт, и плашка с кнопкой «выслать письмо снова».
7. Тесты: чистая логика токенов (хеш, TTL, одноразовость) и флоу на `axum-test` по образцу `tests/auth_flow`.
## Этап 2: сброс пароля
- `POST /auth/forgot-password {email}`: всегда одинаковый ответ (нет перечисления email), письмо только если аккаунт есть;
лимит частоты; задержка выравнивается как в `login`.
- `POST /auth/reset-password {token, new_password}`: проверка токена, смена хеша пароля, отзыв всех refresh-токенов аккаунта.
## Безопасность
- Токены только хешем, сравнение по хешу в БД, одноразовые, с коротким TTL.
- Ответы эндпоинтов не раскрывают, существует ли email.
- Ссылки только на `PUBLIC_BASE_URL` из конфига, не из заголовков запроса (защита от host header injection).
- API-ключ Resend: права только на отправку, хранится в `.env` на VPS (`chmod 600`), не в репозитории.
## Открытые вопросы
1. Старые аккаунты: считать подтверждёнными автоматически (миграция ставит `email_verified_at = now()`) или требовать
подтверждения? Рекомендую пометить подтверждёнными, чтобы не заблокировать существующих.
2. Где проверяется публикация аддонов в `addons-registry` (нужно прочитать код перед этапом 1, п.5).