Проверено: редакция «Управление сайтом: Бизнес»; каналы 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
Схема целиком укладывается в штатные точки расширения:
- обработчик события
OnAuthServicesBuildListвозвращает дескриптор провайдера (ID/CLASS/NAME/ICON) — после этого кнопка появляется в форме входа сама, шаблон формы править не нужно; - класс провайдера отдаёт ссылку на
/connect/authorizeи обрабатывает возврат; CSocServAuth::AuthorizeUser()берёт на себя поиск, создание и связывание — связь ложится вb_socialservices_user.
apple.php в модуле socialservices.
Две настройки, без которых вход не состоится
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: вход «исчезает» на следующем запросе.
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) и переносит их в таблицу связей без дублей — миграция со старой интеграции
проходит сама.