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

Step-Up MFA верификация

Используйте MFA Verification API от VoxKey для подтверждения чувствительных действий -- переводов денег, смены пароля, административных операций -- проверяя пользователей через их зарегистрированные MFA факторы. Ваше приложение делегирует весь MFA-поток VoxKey: не нужно самим реализовывать валидацию TOTP, rate limiting или защиту от brute-force.

Зачем нужен step-up MFA?

MFA при логине подтверждает кто пользователь. Step-up MFA подтверждает что пользователь всё ещё здесь в самый важный момент -- удаление аккаунта, перевод денег, смена пароля или создание API-ключа.

Без step-up верификации скомпрометированная сессия (украденный токен, незаблокированный ноутбук) позволяет выполнить любое действие, на которое пользователь авторизован.

Быстрый старт: TOTP верификация

Простейшая интеграция требует три API-вызова: получить список факторов, создать challenge, затем проверить код.

const REALM = 'your-realm-uuid';
const BASE = `https://app.voxkey.io/api/v1/${REALM}/mfa`;

// 1. Получаем зарегистрированные факторы пользователя
const factors = await fetch(`${BASE}/factors`, {
headers: { Authorization: `Bearer ${accessToken}` },
}).then(r => r.json());

const totp = factors.enrolled.find(f => f.type === 'totp');
if (!totp) throw new Error('У пользователя не зарегистрирован TOTP фактор');

// 2. Создаём challenge
const challenge = await fetch(`${BASE}/challenges`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
factor_type: 'totp',
purpose: 'transaction.approve',
}),
}).then(r => r.json());

// 3. Проверяем код пользователя
const result = await fetch(`${BASE}/challenges/${challenge.challenge_id}/verify`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ code: userEnteredCode }),
}).then(r => r.json());

if (result.verified) {
// Выполняем чувствительное действие
// result.step_up_token доступен если был установлен issue_step_up_token
}

Сценарии интеграции

Традиционное веб-приложение (серверное)

Ваш бэкенд вызывает VoxKey напрямую. Результат верификации является доверенным, поскольку не покидает сервер.

Браузер --> Ваш бэкенд --> VoxKey MFA API (challenge + verify) --> verified: true --> выполнение действия
  1. Пользователь нажимает "Подтвердить перевод"
  2. Бэкенд создаёт challenge для фактора пользователя
  3. Пользователь вводит код в вашем интерфейсе
  4. Бэкенд отправляет код на endpoint верификации
  5. Если verified === true, действие выполняется немедленно

Step-up токен не нужен -- сервер уже имеет доверенный результат.

Single-Page Application (SPA)

SPA вызывает VoxKey (через прокси на бэкенде или напрямую, если access token имеет нужный scope). После верификации API возвращает step-up токен -- короткоживущий подписанный JWT, который ваш бэкенд валидирует перед выполнением действия.

Браузер --> VoxKey MFA API (challenge + verify) --> step_up_token
Браузер --> Ваш бэкенд (действие + заголовок X-Step-Up-Token)
Ваш бэкенд --> валидация JWT через JWKS --> выполнение действия

Запросите токен, установив issue_step_up_token: true при создании challenge. См. Step-Up Token для деталей валидации.

Микросервисы

Сервис A верифицирует пользователя и передаёт step_up_token сервису B, который выполняет действие. Сервис B валидирует токен независимо через JWKS -- callback к VoxKey не требуется.

SPA --> API Gateway --> VoxKey MFA API --> step_up_token
--> API Gateway --> Payment Service (валидация токена через JWKS) --> выполнение

Токен является самодостаточным (подписанный JWT), поэтому downstream-сервисам не нужно повторно обращаться к VoxKey для его проверки.

Конвенции purpose

Поле purpose -- обязательная строка, которая привязывает challenge к конкретному действию. Используйте точечные namespace'ы для организации.

NamespaceПримерыОписание
reauthreauthОбщее "подтвердите, что это вы" перед чувствительной страницей
account.*account.delete, account.email_changeДействия с жизненным циклом аккаунта
password.*password.change, password.resetОперации с паролем
billing.*billing.payment_method, billing.subscriptionФинансовые и биллинговые операции
transaction.*transaction.approve, transaction.withdrawПодтверждение транзакций
admin.*admin.role_change, admin.config_changeАдминистративные действия

Purpose валидируется по паттерну: /^[a-z][a-z0-9_.]{2,50}$/

подсказка

Step-up proof для account.delete нельзя переиспользовать для billing.subscription. Ваш бэкенд всегда должен проверять, что claim purpose в токене соответствует выполняемому действию.

Что происходит при ошибке

  • Неверный код: Счётчик попыток challenge увеличивается. После 5 неудачных попыток challenge блокируется и возвращает 410 Gone.
  • Истёкший challenge: У challenge есть TTL (5 минут для TOTP/WebAuthn, 10 минут для email/SMS). После истечения создайте новый challenge.
  • Rate limit: Более 5 созданий challenge в минуту на пользователя возвращает 429 Too Many Requests. Отступите и повторите попытку.

Следующие шаги

  • Справочник MFA API -- полный справочник endpoints со всеми параметрами и ответами
  • Step-Up Token -- структура токена и валидация для SPA и микросервисов
  • Типы MFA факторов -- сравнение поддерживаемых факторов с rate limits и TTL