Статья описывает подключение Simple2FA к собственному веб-приложению заказчика через REST API локального инстанса. Сценарий: приложение само проверяет логин и пароль (первый фактор), затем передаёт пользователя в Simple2FA для подтверждения второго фактора и по возвращении проверяет результат.
Запросы выполняются к бэкенду локального инстанса — адресу, по которому опубликован сервис local-backend (значение LOCAL_URL из .env при установке; в примерах ниже — https://<адрес-бэкенда-локального-инстанса>). Адрес панели администратора для вызовов API не используется.
Используются два метода API локального инстанса:
- POST /api/initiate-2fa — инициация сессии второго фактора, возвращает ссылку для редиректа пользователя;
- POST /api/auth/status-external — получение результата аутентификации по идентификатору сессии.
Как это работает
- Пользователь вводит логин и пароль в вашем приложении. Приложение проверяет первый фактор своими средствами.
- Приложение вызывает /api/initiate-2fa, передавая логин пользователя, URL возврата и свой идентификатор сессии (
externalAuthId). - В ответ приходит
wizardUrl— приложение перенаправляет браузер пользователя по этой ссылке. - Пользователь подтверждает вход выбранным способом (PUSH, TOTP, Telegram, MAX). Если второй фактор ещё не настроен, визард сначала проведёт активацию.
- После подтверждения визард возвращает пользователя на
redirectUrl. - Приложение вызывает /api/auth/status-external с тем же
externalAuthIdи получает статус. Сессия в приложении создаётся только при статусеAUTHENTICATED.
Важно: сам факт возврата пользователя на redirectUrl не является подтверждением успешной аутентификации. Результат всегда проверяется серверным вызовом /api/auth/status-external.
Подготовка: создание ресурса в локальном инстансе
В панели администратора локального инстанса перейдите в раздел Ресурсы, нажмите «+ Добавить» и создайте ресурс с протоколом HTTPS. Подробное описание всех полей — в статье Ресурсы; для протокола HTTPS блок «Настройки RADIUS» не отображается.
Основные настройки:
- Название — произвольное имя вашего приложения. Отображается в журналах и в уведомлениях пользователю при подтверждении входа.
- Протокол —
HTTPS. - Порт —
443. - API ключ — генерируется автоматически при сохранении ресурса. После сохранения откройте карточку ресурса и скопируйте значение по ссылке «Скопировать». Ключ передаётся в поле
apiKeyкаждого запроса к API. Храните его на стороне сервера приложения и не передавайте в браузер.
Блок «Пользователи и 2FA»:
- Автоматическое создание пользователей — если включено, пользователь, которого ещё нет в Simple2FA, будет создан при первом вызове
/api/initiate-2fa. Без этой опции для неизвестного логина API вернёт ошибку 404. - Группа пользователей по умолчанию — группа, в которую попадают автосозданные пользователи. Если автосоздание включено, а группа не задана, API вернёт ошибку 400.
- Использовать кеширование сессий и Длительность кеша — после успешного подтверждения второй фактор не запрашивается повторно у той же пары «пользователь + IP» в течение заданного окна. В этом случае
/api/initiate-2faвернётrequires2fa: false.
Логин в запросах должен совпадать с логином пользователя в Simple2FA (созданного вручную, импортированного из каталога или автосозданного).
Шаг 1. Инициация второго фактора
POST /api/initiate-2fa
Параметры запроса (JSON):
- apiKey — API-ключ ресурса. Обязательный.
- username — логин пользователя. Обязательный.
- redirectUrl — URL вашего приложения, на который пользователь вернётся после подтверждения.
- externalAuthId — идентификатор сессии на стороне вашего приложения. Используется затем для запроса статуса. Рекомендуется генерировать случайное непредсказуемое значение (например, UUID) и хранить его в серверной сессии пользователя.
- ip — необязательный. IP-адрес клиента определяется автоматически, когда пользователь открывает визард; передавать поле явно не требуется.
Пример запроса:
curl -X POST https://<адрес-бэкенда-локального-инстанса>/api/initiate-2fa \
-H "Content-Type: application/json" \
-d '{
"apiKey": "<API-ключ ресурса>",
"username": "john.doe",
"redirectUrl": "https://myapp.example.com/auth/2fa/callback",
"externalAuthId": "7d6a2d2f-4c5d-4e7d-8b9f-123456789abc"
}'
Пример ответа:
{
"requires2fa": true,
"wizardUrl": "https://<адрес-визарда>/wizard/f7f06d6b-561b-416a-b1f0-008761a9c54f",
"setupComplete": true,
"selectedProviderName": "push",
"isActive": true,
"configuredMethods": [
{ "id": "push", "modelName": "iPhone 15" },
{ "id": "otp" }
]
}
Поля ответа:
- requires2fa — требуется ли второй фактор для этого пользователя. Если
false, второй фактор не запрашивается (для пользователя не включена политика 2FA либо сработало кеширование сессий ресурса) — приложение может завершить вход без редиректа. - wizardUrl — ссылка, на которую нужно перенаправить браузер пользователя. Последний сегмент ссылки — токен сессии второго фактора; его можно использовать вместо
externalAuthIdпри запросе статуса (полеauthToken). - setupComplete — настроен ли у пользователя второй фактор в соответствии с политикой. Если
false, по ссылкеwizardUrlпользователь сначала пройдёт активацию. - selectedProviderName — способ подтверждения по умолчанию.
- isActive — активна ли учётная запись пользователя в Simple2FA.
- configuredMethods — список настроенных и разрешённых политикой способов.
Коды ошибок:
- 400 — не передан
usernameили у ресурса не задана группа по умолчанию. - 401 — неверный API-ключ.
- 402 — нет свободных лицензий для создания пользователя.
- 404 — пользователь не найден и автосоздание запрещено.
- 502 — облачный сервис недоступен.
Шаг 2. Редирект пользователя на визард
Приложение отвечает браузеру пользователя редиректом (HTTP 302) на wizardUrl. Пользователь подтверждает вход; по завершении визард перенаправляет его на redirectUrl, указанный на шаге 1.
Обработчик redirectUrl в приложении должен найти externalAuthId в серверной сессии пользователя (cookie) и перейти к шагу 3. Не следует принимать externalAuthId из параметров запроса браузера без сверки с сессией.
Шаг 3. Проверка результата
POST /api/auth/status-external
Параметры запроса (JSON):
- apiKey — API-ключ ресурса. Обязательный.
- externalAuthId — идентификатор, переданный на шаге 1;
- authToken — токен сессии из конца
wizardUrl. АльтернативаexternalAuthId: достаточно передать одно из двух полей.
Пример запроса:
curl -X POST https://<адрес-бэкенда-локального-инстанса>/api/auth/status-external \
-H "Content-Type: application/json" \
-d '{
"apiKey": "<API-ключ ресурса>",
"externalAuthId": "7d6a2d2f-4c5d-4e7d-8b9f-123456789abc"
}'
Пример ответа:
{
"status": "AUTHENTICATED",
"redirectUrl": "https://myapp.example.com/auth/2fa/callback",
"selectedMethod": "push",
"clientIp": "203.0.113.5",
"resourceName": "Корпоративный портал"
}
Возможные значения status:
- PENDING_SETUP — пользователь ещё не настроил второй фактор.
- PENDING_AUTH — второй фактор настроен, подтверждение ещё не получено.
- AUTHENTICATED — вход подтверждён. Единственный статус, при котором приложение создаёт сессию пользователя.
- FAILED — подтверждение отклонено или код введён неверно.
- EXPIRED — сессия второго фактора истекла. Время жизни сессии задаётся в разделе Глобальные настройки панели администратора.
- AUTH_EXPIRED — истекло время ожидания подтверждения.
- UNKNOWN — статус не установлен.
Коды ошибок:
- 400 — некорректное тело запроса.
- 401 — неверный API-ключ.
- 404 — сессия с указанным
externalAuthId/authTokenне найдена. - 502 — облачный сервис недоступен.
Ограничений на число запросов статуса нет. Его можно запрашивать не только после возврата пользователя, но и периодически (polling) — например, если приложение показывает собственный экран ожидания вместо редиректа на визард. Рекомендуемый интервал опроса — 2–3 секунды.
Рекомендации по безопасности
- API-ключ хранится только на сервере приложения. Вызовы
/api/initiate-2faи/api/auth/status-externalвыполняются сервер-сервер, не из браузера. - externalAuthId — случайное значение, уникальное для каждой попытки входа. Повторное использование не допускается.
- Проверка статуса обязательна. Редирект на
redirectUrlбез проверкиAUTHENTICATEDне является подтверждением. - Привязка к сессии.
externalAuthIdдолжен быть связан с серверной сессией пользователя, в которой уже пройден первый фактор, чтобы исключить подмену. - Сетевой доступ. Доступ к API локального инстанса ограничивается адресами серверов приложения.
Типовые ситуации
- Пользователь не настроил второй фактор. В ответе initiate-2fa придёт
setupComplete: false; визард поwizardUrlпроведёт активацию и затем подтверждение входа. Дополнительных действий от приложения не требуется. - requires2fa: false. Второй фактор не требуется для этого входа; приложение завершает аутентификацию по первому фактору.
- Пользователь закрыл визард и вернулся позже. Статус будет
EXPIREDилиAUTH_EXPIRED; приложение инициирует новую сессию с новымexternalAuthId. Время жизни сессии — в глобальных настройках. - Ответ 502. Нет связи локального инстанса с облаком. Приложение должно отказать во входе или показать сообщение о временной недоступности — не пропускать пользователя без второго фактора.
