# Двухфакторная аутентификация для веб-сервисов (API) Статья описывает подключение Simple2FA к собственному веб-приложению заказчика через REST API локального инстанса. Сценарий: приложение само проверяет логин и пароль (первый фактор), затем передаёт пользователя в Simple2FA для подтверждения второго фактора и по возвращении проверяет результат. Запросы выполняются к бэкенду локального инстанса — адресу, по которому опубликован сервис `local-backend`: это домен `DOMAIN_API`, заданный при [установке инстанса](../../start/local-instance.md) (в режиме без nginx — порт бэкенда на хосте). Адрес панели администратора (`DOMAIN_ADMIN`) для вызовов API не используется. В примерах ниже адрес бэкенда обозначен как `https://<адрес-бэкенда-локального-инстанса>`. Используются два метода API локального инстанса: - **POST /api/initiate-2fa** — инициация сессии второго фактора, возвращает ссылку для редиректа пользователя; - **POST /api/auth/status-external** — получение результата аутентификации по идентификатору сессии. ## Как это работает 1. Пользователь вводит логин и пароль в вашем приложении. Приложение проверяет первый фактор своими средствами. 2. Приложение вызывает `/api/initiate-2fa`, передавая логин пользователя, URL возврата и свой идентификатор сессии (`externalAuthId`). 3. В ответ приходит `wizardUrl` — приложение перенаправляет браузер пользователя по этой ссылке. 4. Пользователь подтверждает вход выбранным способом. Если второй фактор ещё не настроен, визард сначала проведёт активацию. 5. После подтверждения визард возвращает пользователя на `redirectUrl`. 6. Приложение вызывает `/api/auth/status-external` с тем же `externalAuthId` и получает статус. Сессия в приложении создаётся только при статусе `AUTHENTICATED`. !!! warning "Возврат на redirectUrl — не подтверждение входа" Сам факт возврата пользователя на `redirectUrl` не означает успешную аутентификацию. Результат всегда проверяется серверным вызовом `/api/auth/status-external`. ![Последовательность подключения второго фактора к веб-приложению через API локального инстанса](../../assets/img/web-services-2fa/simple2fa_custom_web_api_sequence.png) ## Подготовка: создание ресурса в локальном инстансе В панели администратора локального инстанса перейдите в раздел **Ресурсы**, нажмите **«+ Добавить»** и создайте ресурс с протоколом **HTTPS**. Подробное описание всех полей — в статье [Ресурсы](../../admin/resources.md); для протокола 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-адрес клиента определяется автоматически, когда пользователь открывает визард; передавать поле явно не требуется. Пример запроса: ```bash curl -X POST https://<адрес-бэкенда-локального-инстанса>/api/initiate-2fa \ -H "Content-Type: application/json" \ -d '{ "apiKey": "", "username": "john.doe", "redirectUrl": "https://myapp.example.com/auth/2fa/callback", "externalAuthId": "7d6a2d2f-4c5d-4e7d-8b9f-123456789abc" }' ``` Пример ответа: ```json { "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`: достаточно передать одно из двух полей. Пример запроса: ```bash curl -X POST https://<адрес-бэкенда-локального-инстанса>/api/auth/status-external \ -H "Content-Type: application/json" \ -d '{ "apiKey": "", "externalAuthId": "7d6a2d2f-4c5d-4e7d-8b9f-123456789abc" }' ``` Пример ответа: ```json { "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** — сессия второго фактора истекла. Время жизни сессии задаётся в разделе [«Глобальные настройки»](../../admin/global-settings.md) панели администратора. - **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`. Время жизни сессии — в [глобальных настройках](../../admin/global-settings.md). - **Ответ 502.** Нет связи локального инстанса с облаком. Приложение должно отказать во входе или показать сообщение о временной недоступности — не пропускать пользователя без второго фактора. ## Смотрите также - [Веб-приложения и 1С](index.md) - [Ресурсы](../../admin/resources.md) - [Установка локального инстанса](../../start/local-instance.md) - [Начало работы](../../start/index.md)