docs: accounts-service email plan (Resend SMTP, verification, password reset), TODO phase 11 closed
This commit is contained in:
parent
f4e15b45c9
commit
12ce6216b6
2 changed files with 53 additions and 4 deletions
7
TODO.md
7
TODO.md
|
|
@ -101,12 +101,11 @@ boilerplate на класс), а в Rust/TypeScript тот же объём ло
|
||||||
GitHub под лентой «временно не работает»; jar 64,4 МБ живёт на VDS и
|
GitHub под лентой «временно не работает»; jar 64,4 МБ живёт на VDS и
|
||||||
отдаётся gateway (`GET /downloads/*` → `/api/downloads/lovisual.jar`),
|
отдаётся gateway (`GET /downloads/*` → `/api/downloads/lovisual.jar`),
|
||||||
проверено curl'ом и в браузере; кнопки лендинга/футера ведут на страницу
|
проверено curl'ом и в браузере; кнопки лендинга/футера ведут на страницу
|
||||||
- [ ] Фаза 11: Серверный плагин — сервер может отключать функции мода —
|
- [x] Фаза 11: Серверный плагин — сервер может отключать функции мода —
|
||||||
дизайн + КОД ГОТОВЫ 2026-09-30 (Velocity-плагин `server-plugin/`, клиент
|
ЗАВЕРШЕНА 2026-09-30 (Velocity-плагин `server-plugin/`, клиент
|
||||||
`features/platform/serverpolicy/`, шлюз в `ModuleLifecycleHelper`,
|
`features/platform/serverpolicy/`, шлюз в `ModuleLifecycleHelper`,
|
||||||
коммиты af2b4172, 711fcafc, 33cff90c, fdc239db; обе сборки зелёные:
|
коммиты af2b4172, 711fcafc, 33cff90c, fdc239db; обе сборки зелёные:
|
||||||
плагин 9 тестов, мод 449 тестов, checkFolderLimit OK); осталась ручная
|
плагин 9 тестов, мод 449 тестов, checkFolderLimit OK)
|
||||||
приёмка на стенде владельца (см. Фазу 11 в конце файла)
|
|
||||||
- [ ] Фаза 8.5.3: остаток разбивки гигантов мода (файлы >200 → ≤200) — топ-4
|
- [ ] Фаза 8.5.3: остаток разбивки гигантов мода (файлы >200 → ≤200) — топ-4
|
||||||
закрыт 2026-09-30 (крупнейшие: ConfigDiffPanelComponent 393→167,
|
закрыт 2026-09-30 (крупнейшие: ConfigDiffPanelComponent 393→167,
|
||||||
RotationManager 391→199, ScriptedTriangulatorHudPanel 386→189,
|
RotationManager 391→199, ScriptedTriangulatorHudPanel 386→189,
|
||||||
|
|
|
||||||
50
backend/accounts-service/EMAIL_PLAN.md
Normal file
50
backend/accounts-service/EMAIL_PLAN.md
Normal 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).
|
||||||
Loading…
Add table
Add a link
Reference in a new issue