> ## Documentation Index
> Fetch the complete documentation index at: https://veriqa.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 1С-Битрикс

> Вход в Битрикс через Veriqa на штатном каркасе socialservices: почему встроенный OpenID не подходит, две обязательные настройки и ловушка CUser::GetList, стоящая прав администратора.

У Битрикса нет своего OIDC-клиента, но есть штатный каркас социальной авторизации, в который
Veriqa встраивается как обычный провайдер. Кнопка появляется в стандартной форме входа, поиск,
создание и связывание учётных записей делает сама платформа.

<Note>
  Проверено: редакция «Управление сайтом: Бизнес»; каналы Telegram и Email. Работают вход,
  автосоздание пользователя, связывание в `b_socialservices_user`, кнопка в штатной форме.
</Note>

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

Встроенный `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`.

<Warning>
  Callback держите в `/local/`, а не в `/bitrix/tools/oauth/` — каталог `/bitrix/` затирается
  обновлением. Публичного `CSocServAuthManager::Authorize($id)` для этого достаточно.
</Warning>

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

```
main.new_user_registration = Y
socialservices.allow_registration = Y
```

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

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

`CUser::GetList` **не понимает D7-синтаксис `'=ПОЛЕ'`**: нераспознанные ключи фильтра он молча
выбрасывает и ведёт себя так, будто фильтра не было. `Fetch()` отдаёт первую строку — то есть
пользователя с id 1, администратора. Код «нашли существующего и авторизовали» превращается во
**вход администратором при любом успешном OIDC-логине**.

| Вызов | Результат на несуществующем идентификаторе |
| - | - |
| `CUser::GetList(['=XML_ID' => …])` | **все пользователи** |
| `CUser::GetList(['THIS_FIELD_DOES_NOT_EXIST' => …])` | все пользователи — идентично |
| `CUser::GetList(['XML_ID' => …])` — легаси-ключи | 0, верно |
| `UserTable::getList(['=XML_ID' => …])` — D7 | 0, верно |

Дыры в Битриксе тут нет: в самой платформе ключи написаны верно. Виновато **смешение диалектов** в
коде интеграции. Правило простое: либо легаси-ключи в легаси-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](https://www.rfc-editor.org/rfc/rfc2606) — письма на такой адрес не
уходят никуда, и учётная запись не получит ни уведомлений, ни восстановления пароля. Это
осознанная цена, а не бесплатный обход.

На канале Email адрес приезжает claim-ом, и синтез не нужен.

<Note>
  Маркер владения учётной записью ставит платформа: у созданной через каркас записи
  `b_user.EXTERNAL_AUTH_ID = socservices` — это подставляет сам `CSocServAuth::AuthorizeUser`.
  Собственный идентификатор провайдера в это поле не попадает, искать по нему бесполезно.
</Note>

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

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


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.