> For the complete documentation index, see [llms.txt](https://docs.johncms.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.johncms.com/10.0/polzovateli/vkhod-cherez-vneshnie-servisy.md).

# Вход через внешние сервисы

Пользователи могут входить на сайт через GitHub, Google, VK и Яндекс. Список не закрыт: любой из них выключается в админке, а новые добавляются модулями (см. раздел для разработчиков ниже).

Это **не отдельный способ аутентификации**: флоу заканчивается обычной сессией, той же самой, что и вход по паролю. Дальше человек ходит по сайту с обычной кукой `jc_auth`, а привязка живёт в таблице `user_identities`.

## Про VK

VK подключается через **VK ID** (`id.vk.com`, OAuth 2.1), а не через классический `oauth.vk.com`: приложение, зарегистрированное сейчас, классический флоу проходить не будет — VK ответит `Security Error` без пояснений. У VK ID три особенности, все обязательные: PKCE, `device_id` из callback в запросе токена и профиль из `/oauth2/user_info` (ни в токене, ни в `users.get` адреса нет).

## Настройка

Экран `/admin/auth/providers` (право `admin.settings.manage`):

* `Client ID` и `Client secret` приложения, зарегистрированного у провайдера;
* переключатель «Включён» — сервис показывается на экранах входа, только если включён **и** оба ключа заполнены;
* адрес возврата (Callback URL) — его нужно скопировать в настройки приложения у провайдера.

Ключи сохраняются в `config/autoload/auth.local.php` — этот файл в `.gitignore`, рядом с паролем БД. **Не переносите их в `auth.global.php`**: он попадает в репозиторий.

Сохранённый секрет никогда не отправляется обратно в браузер: поле пустое, а заполнять его нужно только чтобы заменить.

## Что происходит при входе

1. **Аккаунт уже привязан** — вход, `last_login_at` обновляется, в журнал пишется `oauth.login`.
2. **Адрес совпал с существующим аккаунтом.** Автоматическая привязка допустима, только если провайдер подтвердил адрес **и** адрес подтверждён на сайте. Иначе — отказ с предложением войти по паролю и привязать сервис вручную. Это защита от захвата аккаунта: иначе достаточно зарегистрироваться где-то с чужим адресом.
3. **Никого не нашли** — экран «Завершение регистрации»: логин и адрес. Он нужен всегда: VK может не отдать адрес вовсе, а отображаемое имя почти никогда не годится в качестве уникального логина.
4. **Регистрация закрыта** (у роли `guest` нет права `registration.register`) — существующие аккаунты через провайдер входят, новые не создаются.

Созданный так аккаунт **не имеет пароля**. Это не пустой пароль: хэшер отказывает сразу, поэтому войти «пустым паролем» в такой аккаунт нельзя.

## Привязанные аккаунты

Раздел профиля `/profile/accounts`: список привязанных сервисов, кнопка «Привязать другой сервис» и отвязка.

**Последний способ входа отвязать нельзя.** Если пароля нет, а сервис один — отвязка запрещена: восстанавливать было бы нечего, восстановление пароля работает только там, где пароль есть.

Сервис, чей модуль удалили, показывается в списке как «Недоступен» — привязку всё ещё можно снять, а строки в БД при выключении сервиса не удаляются, чтобы включение обратно ничего не потеряло.

## Журнал

В [журнал входов](/10.0/polzovateli/zhurnal-vkhodov.md) пишутся `oauth.login`, `oauth.register`, `oauth.linked` и `oauth.unlinked` — с ключом провайдера в контексте.

## Безопасность round trip

`state` одноразовый, живёт 10 минут и хранится в PHP-сессии; поверх него — PKCE. Без этого callback можно воспроизвести: злоумышленник начинает флоу со своим аккаунтом у провайдера и подсовывает ссылку жертве, после чего его аккаунт оказывается привязан к чужому.

Маршрут `/auth/{provider}/callback` освобождён от CSRF-проверки (провайдер не носит наш токен) — именно `state` и PKCE его и заменяют.
