POST /api/investors/me/2fa/enable-request — Запрос включения 2FA (requestEnableTwoFactor)
Назначение
Первый шаг включения двухфакторной аутентификации: отправить СМС-код на текущий подтверждённый телефон
пользователя и создать запрос подтверждения с целью EnableTwoFactor. Вызывается аутентифицированным
пользователем. Включение применит confirm-enable-two-factor.md (INV-7: 2FA
меняется только после подтверждения кодом).
| Поле | Значение |
|---|
| Сервис/контекст | identity |
| API area | public |
| Feature group | security |
| Статус | approved |
| Документ | docs/06-services/identity/api/investor/request-enable-two-factor.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User (Bearer JWT) |
| Auth policy / Permissions | authenticated; permission identity.security.manage-self |
| Scope (RBAC) | self |
| Record-level | только собственная запись; код уходит только на телефон владельца |
| Tenant isolation | не применяется (ADR-0052) |
| Audit | security-sensitive: факт запроса логируется |
HTTP-контракт
| Поле | Значение |
|---|
| Method | POST |
| Route | /api/investors/me/2fa/enable-request |
| Success status | 200 |
| Idempotency header | не применяется (повтор гасит предыдущий Pending — INV-S1 Superseded) |
| Correlation | сквозной trace через traceparent |
Входные данные / параметры запроса
Body отсутствует.
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
UserId | identity claims (sub) | Guid | Да | валидный JWT | 401 без токена |
IpAddress / UserAgent | middleware | string? | Нет | ≤ 45 / ≤ 512 | rate-limit / анти-фрод |
Модель ответа RequestEnableTwoFactorCommandResult
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|
twoFactorConfirmationRequestId | string (Guid) | Да | SignInConfirmationRequest.Id | — | Идентификатор запроса (confirm ищет Pending по пользователю + цели; id — для трассировки/UI). |
realConfirmationCodeDispatched | bool | Да | verifier | — | false — код не отправлялся реально (тестовый контур). |
Алгоритм
- Контекст и доступ. Операцию выполняет аутентифицированный пользователь над собственной записью
(
UserId из токена); из middleware известны IpAddress/UserAgent.
- Проверка и лимиты. Rate-limit (INV-S4, по телефону пользователя + IP) — превышение ⇒
IDENTITY_CONFIRMATION_RATE_LIMITED (429).
- Проверки состояния. Получить пользователя; он
Active — иначе IDENTITY_USER_BLOCKED; 2FA ещё не
включена — иначе IDENTITY_TWO_FACTOR_ALREADY_ENABLED (идемпотентность цели).
- Правила и маппинг. Сгенерировать код (CSPRNG, 4–6 цифр); создать запрос подтверждения (INV-S8):
цель
EnableTwoFactor, UserId, PhoneNumber = текущий телефон пользователя (не из входа), хэш кода
(HMAC-SHA256), ExpiresAt = now + 5 мин, IpAddress, UserAgent; фиксируется доменное событие
ConfirmationCodeRequestedEvent (→ СМС-verifier).
- Что меняется. В одном согласованном изменении: прежний активный (
Pending) запрос той же цели
гасится в Superseded (INV-S1) и создаётся новый запрос.
- События. Интеграционных нет; доменное
ConfirmationCodeRequestedEvent → СМС-verifier.
- Результат.
twoFactorConfirmationRequestId, realConfirmationCodeDispatched; HTTP 200.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|
| Интеграционные события | none |
| Внешние вызовы | СМС-verifier (timeout 5 с, 1 retry) |
| Проекции / поиск | rate-limit-счётчик |
| Уведомления | СМС с кодом на текущий номер |
Предусловия и постусловия
- Пред: пользователь аутентифицирован и
Active; 2FA ещё выключена (IsTwoFactorEnabled = false);
есть текущий подтверждённый телефон (код уходит на него, не из входа).
- Пост (успех): создан
Pending-запрос цели EnableTwoFactor (TTL 5 мин); прежний Pending той же
цели погашен в Superseded (INV-S1); СМС-код отправлен. IsTwoFactorEnabled не меняется — включение
применит только confirm-enable-two-factor.md по коду (INV-7).
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
| — (auth) | 401 | Нет/просрочен JWT | клиент выполняет re-login | re-login |
IDENTITY_TWO_FACTOR_ALREADY_ENABLED | 400 | 2FA уже включена | «2FA уже включена» | no retry |
IDENTITY_USER_BLOCKED | 400 | Status != Active | «Учётная запись заблокирована» | no retry |
IDENTITY_CONFIRMATION_RATE_LIMITED | 429 | INV-S4 | «Слишком много запросов кода» | retry ≥ 60 сек |
IDENTITY_SMS_DISPATCH_FAILED | 400 | Отказ verifier | «Не удалось отправить код» | retry |
Acceptance Criteria
- Given
Active-пользователь с выключенной 2FA, When POST .../2fa/enable-request, Then
создан Pending-запрос EnableTwoFactor, код отправлен на текущий телефон владельца, ответ 200
с twoFactorConfirmationRequestId и realConfirmationCodeDispatched; IsTwoFactorEnabled не тронут.
- Given уже есть активный
Pending-запрос EnableTwoFactor, When повторный запрос, Then
прежний → Superseded (INV-S1), создаётся новый, отправляется новый код.
- Given 2FA уже включена, When запрос, Then
IDENTITY_TWO_FACTOR_ALREADY_ENABLED (400,
идемпотентность цели).
- Given учётка
Blocked/Archived, When запрос, Then IDENTITY_USER_BLOCKED (400).
- Given превышен лимит отправки кода (INV-S4, по телефону + IP), When запрос, Then
IDENTITY_CONFIRMATION_RATE_LIMITED (429), retry ≥ 60 сек.
- Given verifier отказал после retry, When запрос, Then
IDENTITY_SMS_DISPATCH_FAILED (400).
- Given запрос без валидного JWT, When запрос, Then
401, клиент уходит на re-login.
Совместимость и наблюдаемость
- Новые optional поля — additive.
- Logs:
UserId, маскированный номер; metrics: identity_2fa_enable_requests_total; traces — OTel.
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы