Skip to main content
У Битрикса нет своего OIDC-клиента, но есть штатный каркас социальной авторизации, в который Veriqa встраивается как обычный провайдер. Кнопка появляется в стандартной форме входа, поиск, создание и связывание учётных записей делает сама платформа.
Проверено: редакция «Управление сайтом: Бизнес»; каналы Telegram и Email. Работают вход, автосоздание пользователя, связывание в b_socialservices_user, кнопка в штатной форме.

Что не подойдёт

Встроенный COpenIDClient — это OpenID 2.0, а не OpenID Connect: в нём есть openid.ns и check_authentication, но нет ни id_token, ни JWKS, ни PKCE. С Veriqa он несовместим в принципе. По умолчанию в форме входа активны как раз сервисы той эпохи — Livejournal, Rambler, Blogger, Mail.Ru OpenID. Модуль Маркетплейса anighr.openidconnect не проверялся: Маркетплейс требует лицензионный ключ.

Рабочий путь — провайдер socialservices

Схема целиком укладывается в штатные точки расширения:
  1. обработчик события OnAuthServicesBuildList возвращает дескриптор провайдера (ID / CLASS / NAME / ICON) — после этого кнопка появляется в форме входа сама, шаблон формы править не нужно;
  2. класс провайдера отдаёт ссылку на /connect/authorize и обрабатывает возврат;
  3. CSocServAuth::AuthorizeUser() берёт на себя поиск, создание и связывание — связь ложится в b_socialservices_user.
Готовый образец OIDC-провайдера уже лежит в комплекте: apple.php в модуле socialservices.
Callback держите в /local/, а не в /bitrix/tools/oauth/ — каталог /bitrix/ затирается обновлением. Публичного CSocServAuthManager::Authorize($id) для этого достаточно.

Две настройки, без которых вход не состоится

Без них AuthorizeUser возвращает SOCSERV_REGISTRATION_DENY, и снаружи это не похоже на проблему конфигурации — выглядит как сбой входа. На боевом сайте учтите побочный эффект: первая настройка включает и штатную регистрацию Битрикса.

Ловушка CUser::GetList — читать обязательно

CUser::GetList не понимает D7-синтаксис '=ПОЛЕ': нераспознанные ключи фильтра он молча выбрасывает и ведёт себя так, будто фильтра не было. Fetch() отдаёт первую строку — то есть пользователя с id 1, администратора. Код «нашли существующего и авторизовали» превращается во вход администратором при любом успешном OIDC-логине. Дыры в Битриксе тут нет: в самой платформе ключи написаны верно. Виновато смешение диалектов в коде интеграции. Правило простое: либо легаси-ключи в легаси-API, либо D7-синтаксис в UserTable::getList(), но не вперемешку. И «своих» пользователей ищите через связи в b_socialservices_user, а не по полям b_user.

Ещё три грабли каркаса

  • getUrl() вызывается дважды за одну отрисовку формы (GetFormHtml и GetOnClickJs) — генерацию state и PKCE нужно мемоизировать на запрос, иначе второй вызов перезапишет первый.
  • Сессия ядра уже открыта внутри провайдера — своя не нужна. session_write_close() рубит сессию Битрикса посреди отрисовки страницы.
  • session_name() меняется на весь процесс. Если открыть свою сессию и не вернуть имя до prolog_before.php, ядро откроет сессию под чужой cookie: вход «исчезает» на следующем запросе.

Email

Битрикс требует email только у нового пользователя, и требование отключается настройкой main.new_user_email_required. Это мягче, чем у Drupal и Joomla, где адрес нужен на каждом входе. Если адрес не приезжает claim-ом (Telegram его не отдаёт), подставьте синтетический по той же конвенции, что и на других движках: {канал}-{id}@{канал}.veriqa.invalid. Домен .invalid зарезервирован RFC 2606 — письма на такой адрес не уходят никуда, и учётная запись не получит ни уведомлений, ни восстановления пароля. Это осознанная цена, а не бесплатный обход. На канале Email адрес приезжает claim-ом, и синтез не нужен.
Маркер владения учётной записью ставит платформа: у созданной через каркас записи b_user.EXTERNAL_AUTH_ID = socservices — это подставляет сам CSocServAuth::AuthorizeUser. Собственный идентификатор провайдера в это поле не попадает, искать по нему бесполезно.

Приятный побочный эффект

Штатный checkOldUser узнаёт учётные записи «старой схемы» (EXTERNAL_AUTH_ID плюс XML_ID прямо в b_user) и переносит их в таблицу связей без дублей — миграция со старой интеграции проходит сама.