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

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

Назначение

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

Metadata

ПолеЗначение
Сервис/контекстidentity
API areapublic
Feature groupsecurity
Статусapproved
Документdocs/06-services/identity/api/investor/confirm-disable-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 (TwoFactorDisabled)

HTTP-контракт

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

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

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

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

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

Алгоритм

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

Диаграмма

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

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

Выключение 2FA — типовой признак компрометации аккаунта: угнанный токен/сессия отключает второй фактор, чтобы закрепиться. Поэтому UserSecurityChangedEvent (TwoFactorDisabled) пишется в аудит, а владельцу сразу уходит security-уведомление о выключении.

Ошибки и failure modes

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

Acceptance Criteria

  • Given активный пользователь с включённой 2FA и непросроченным Pending-запросом DisableTwoFactor, When confirm с верным code, Then запрос → Confirmed, IsTwoFactorEnabled = false, TwoFactorChangedAt = now, поднимается UserSecurityChangedEvent (TwoFactorDisabled), ответ 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-запроса DisableTwoFactor у актора, When confirm, Then IDENTITY_CONFIRMATION_INVALID (400).
  • Given 2FA уже выключена, When confirm, Then IDENTITY_TWO_FACTOR_NOT_ENABLED (400); Given Status != Active, Then IDENTITY_USER_BLOCKED (400).
  • Given два параллельных confirm одного запроса, When оба доходят до записи, Then один выключает 2FA, второй проигрывает по оптимистической блокировке (IDENTITY_CONFIRMATION_INVALID).

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

  • Logs: UserId, результат; metrics: identity_2fa_disable_confirms_total{result}; traces — OTel.

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

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

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