Перейти к основному содержимому

POST /api/investors/me/2fa/enable-confirm — Подтверждение включения 2FA (confirmEnableTwoFactor)

Назначение

Второй шаг включения двухфакторной аутентификации: проверить СМС-код из request-enable-two-factor.md и включить 2FA (IsTwoFactorEnabled = true, INV-7: изменение 2FA только по подтверждению кодом). Вызывается аутентифицированным пользователем. После включения вход по паролю/пин-коду дополнительно требует СМС-код.

Metadata

ПолеЗначение
Сервис/контекстidentity
API areapublic
Feature groupsecurity
Статусapproved
Документdocs/06-services/identity/api/investor/confirm-enable-two-factor.md

Актор и доступ

ПроверкаЗначение
Actor typeUser (Bearer JWT)
Auth policy / Permissionsauthenticated; permission identity.security.manage-self
Scope (RBAC)self
Record-levelPending-запрос ищется строго по UserId актора (чужой запрос недостижим)
Tenant isolationне применяется (ADR-0052)
Auditsecurity-change: событие UserSecurityChangedEvent (TwoFactorEnabled)

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/investors/me/2fa/enable-confirm
Success status200
Idempotency headerне применяется; одноразовость — статус запроса Confirmed + оптимистическая блокировка
Correlationсквозной trace через traceparent

Входные данные / параметры запроса (body ConfirmEnableTwoFactorForm)

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
UserIdidentity claims (sub)GuidДавалидный JWT401 без токена
codebodystringДаmin 1, max 16никогда не логируется

Модель ответа ConfirmEnableTwoFactorCommandResult

ПолеТипОбязательностьИсточникMaskingОписание
investorIdstring (Guid)ДаUserEntity.IdИдентификатор пользователя.

Алгоритм

  1. Контекст и доступ. Операцию выполняет аутентифицированный пользователь над собственной записью (UserId из токена).
  2. Проверка входа. code непустой, ≤ 16 символов — иначе ValidationError.
  3. Проверки состояния. Получить пользователя; он Active — иначе IDENTITY_USER_BLOCKED; 2FA ещё не включена — иначе IDENTITY_TWO_FACTOR_ALREADY_ENABLED. Найти его активный (Pending) запрос с целью EnableTwoFactor (активный запрос на цель единственный, INV-S1) — нет запроса ⇒ IDENTITY_CONFIRMATION_INVALID; запрос не истёк (INV-S3) — иначе IDENTITY_CONFIRMATION_EXPIRED.
  4. Правила и переходы. Подтверждение кода: счётчик попыток увеличивается до сравнения (INV-S2; 5-я неверная ⇒ статус Exhausted + IDENTITY_CONFIRMATION_EXHAUSTED); сравнение HMAC-SHA256(code) с хэшем в постоянном времени (INV-S5) — неверно ⇒ IDENTITY_CONFIRMATION_INVALID; верно ⇒ запрос Confirmed, ConfirmedAt = now. Пользователь: IsTwoFactorEnabled = true, TwoFactorChangedAt = now; фиксируется доменное событие UserSecurityChangedEvent (TwoFactorEnabled).
  5. Что меняется. В одном согласованном изменении обновляются запрос и пользователь; неверные попытки (счётчик) сохраняются до возврата ошибки; гонка двойного confirm разрешается оптимистической блокировкой — проигравший получает IDENTITY_CONFIRMATION_INVALID.
  6. События. Интеграционных нет (isTwoFactorEnabled не входит в снапшот pin.identity.users.v1); доменное UserSecurityChangedEvent — аудит.
  7. Результат. investorId; HTTP 200.

Диаграмма

Побочные эффекты

ТипДетали
Интеграционные событияnone
Внешние вызовыnone
Проекции / поискnone
Уведомленияуведомление «2FA включена» через сервис notifications

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
— (auth)401Нет/просрочен JWTклиент выполняет re-loginre-login
ValidationError400Пустой/длинный codeСписок ошибок валидацииno retry
IDENTITY_CONFIRMATION_INVALID400Нет Pending-запроса / неверный код (INV-S5)«Неверный код подтверждения»ввести заново
IDENTITY_CONFIRMATION_EXPIRED400ExpiresAt <= now (INV-S3)«Код истёк»новый request-шаг
IDENTITY_CONFIRMATION_EXHAUSTED4005 неверных попыток (INV-S2)«Превышено число попыток»новый request-шаг
IDENTITY_TWO_FACTOR_ALREADY_ENABLED4002FA уже включена«2FA уже включена»no retry
IDENTITY_USER_BLOCKED400Status != Active«Учётная запись заблокирована»no retry

Acceptance Criteria

  • Given активный пользователь с выключенной 2FA и непросроченным Pending-запросом EnableTwoFactor, When confirm с верным code, Then запрос → Confirmed, IsTwoFactorEnabled = true, TwoFactorChangedAt = now, поднимается UserSecurityChangedEvent (TwoFactorEnabled), ответ 200; последующий вход по паролю/пин-коду дополнительно требует СМС-код.
  • Given тот же контекст, но code неверный, When confirm, Then счётчик попыток растёт; на 5-й неверной — запрос → Exhausted и IDENTITY_CONFIRMATION_EXHAUSTED; до этого — IDENTITY_CONFIRMATION_INVALID (400); 2FA остаётся выключенной.
  • Given Pending-запрос истёк (INV-S3), When confirm, Then IDENTITY_CONFIRMATION_EXPIRED (400) — требуется новый request-шаг.
  • Given нет Pending-запроса EnableTwoFactor у актора, When confirm, Then IDENTITY_CONFIRMATION_INVALID (400).
  • Given 2FA уже включена, When confirm, Then IDENTITY_TWO_FACTOR_ALREADY_ENABLED (400); Given Status != Active, Then IDENTITY_USER_BLOCKED (400).
  • Given два параллельных confirm одного запроса, When оба доходят до записи, Then один включает 2FA, второй проигрывает по оптимистической блокировке (IDENTITY_CONFIRMATION_INVALID).

Совместимость и наблюдаемость

  • Logs: UserId, результат подтверждения; metrics: identity_2fa_enable_confirms_total{result}; traces — OTel.

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы