Перейти к содержимому

Вход через внешний сервис - регистрация приложения, привязка, первый вход

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

Механика

Внешний вход не заменяет учётную запись, а привязывается к ней. На сайте по-прежнему есть пользователь со своим номером, правами и группами, а связь с внешним сервисом хранится отдельной записью: сервис, идентификатор аккаунта в нём и номер пользователя.

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

Второй и все последующие входы идут уже по сохранённой связи. Сервис возвращает тот же идентификатор аккаунта, платформа находит по нему пользователя и авторизует его, не спрашивая ничего больше. Именно поэтому смена идентификатора на стороне сервиса выглядит как «пользователь потерял свой профиль».

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

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

Шаги

  1. Зарегистрировать приложение в самом сервисе и получить его ключ и секрет.
  2. Указать там адрес возврата, который сервис вызовет после согласия пользователя.
  3. Включить провайдера в настройках модуля соцсервисов и вписать ключи.
  4. Вывести кнопки входа компонентом на странице авторизации и в форме заказа.
  5. Разобрать первый вход: группы нового пользователя, почта, обязательные поля.
  6. Проверить повторный вход, отвязку аккаунта и вход с другого устройства.

Код

Проверяем адрес возврата:

Окно терминала
curl -I 'https://example.org/bitrix/tools/oauth/yandex.php'
# сервис вызывает этот адрес сам: он обязан отвечать снаружи, без авторизации
# 200 или 302 - нормально, 403 и 404 означают, что вход не заработает

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

Показываем кнопки входа:

// шаблон страницы авторизации
$APPLICATION->IncludeComponent('bitrix:socserv.auth.form', '.default', [
'AUTH_SERVICES' => 'Y',
'SHOW_TITLES' => 'N',
// список сервисов берётся из настроек модуля, а не из параметров вызова
'AUTH_URL' => '/auth/',
]);
// кнопки появляются только у включённых провайдеров с заполненными ключами

Кнопки рисует отдельный компонент, а не форма входа. Пустое место вместо кнопок почти всегда означает, что провайдер выключен или у него не заполнены ключи, а не ошибку в шаблоне.

Смотрим привязки учётной записи:

\Bitrix\Main\Loader::includeModule('socialservices');
$rows = \Bitrix\Socialservices\UserTable::getList([
'select' => ['ID', 'USER_ID', 'EXTERNAL_AUTH_ID', 'XML_ID'],
'filter' => ['=USER_ID' => $userId],
])->fetchAll();
// EXTERNAL_AUTH_ID - какой сервис, XML_ID - идентификатор аккаунта в нём
foreach ($rows as $row) {
printf("%s аккаунт %s связь %s\n", $row['EXTERNAL_AUTH_ID'], $row['XML_ID'], $row['ID']);
}
// у одного пользователя таких строк столько, сколько сервисов он привязал

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

Разбираем первый вход:

/local/php_interface/init.php
\Bitrix\Main\EventManager::getInstance()->addEventHandler('main', 'OnAfterUserAdd',
function (&$fields) {
if (!empty($fields['EXTERNAL_AUTH_ID']) && $fields['RESULT']) {
CUser::SetUserGroup($fields['ID'], [4, 12]); // свои группы новичку
AddMessage2Log('внешний вход: создан пользователь ' . $fields['ID'], 'vendor.shop');
}
});

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

Проверяем обязательные поля:

// внешний сервис редко отдаёт телефон, а магазину он нужен
if ($USER->IsAuthorized() && !CUser::GetByID($USER->GetID())->Fetch()['PERSONAL_PHONE']) {
LocalRedirect('/personal/profile/?fill=phone'); // просим дозаполнить профиль
}
// требовать это прямо на входе не нужно: половина посетителей уйдёт
// а вот перед оформлением заказа телефон спрашивают уже обязательным полем

Профиль из сервиса приходит неполным. Недостающее просят дозаполнить позже, на первом шаге, где оно действительно нужно, а не превращают вход в анкету из восьми полей.

Отдельно стоит решить, что делать с покупателем, который сначала оформил заказ гостем, а потом вошёл через сервис. Заказы гостя привязаны к другому номеру посетителя, и без явного сопоставления по почте или телефону они в личном кабинете не появятся. Сопоставление это делают руками менеджера или отдельным шагом с подтверждением, но не молча.

Отвязываем аккаунт от сервиса:

\Bitrix\Socialservices\UserTable::delete($linkId);
// после отвязки вход той же кнопкой создаст нового пользователя
// поэтому перед отвязкой сотруднику назначают обычный пароль
$user = new CUser();
$user->Update($userId, ['PASSWORD' => $newPass, 'CONFIRM_PASSWORD' => $newPass]);

Отвязка убирает только связь, а не саму учётную запись. Пользователь остаётся со своими заказами и правами, но войти прежним способом уже не может, и об этом его предупреждают заранее.

Ограничения

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

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

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

Проверять всё это удобнее на отдельной учётной записи в самом сервисе, а не на своей рабочей. Второй аккаунт даёт честный первый вход столько раз, сколько нужно, а на своём этот сценарий воспроизводится ровно один раз в жизни.

Журнал внешних входов стоит завести с первого дня. Вопрос «почему у покупателя две учётные записи» приходит через месяц после запуска, и ответ на него живёт только в записях о том, каким сервисом и когда он входил.

Типичные проблемы

После согласия сервис возвращает ошибку.

Адрес возврата в приложении сервиса не совпадает с адресом сайта. Он должен совпадать полностью, включая протокол и домен.

Кнопки входа не появились на странице.

Провайдер выключен или у него не заполнены ключ и секрет. Компонент показывает только те сервисы, которые уже настроены в админке сайта и включены.

У покупателя каждый раз новая учётная запись.

Сервис возвращает разный идентификатор аккаунта при каждом входе. Связь ищется именно по нему, а не по почте.

Новые пользователи попадают не в те группы.

Группы по умолчанию не назначаются при внешнем входе. Их выдают обработчиком события создания пользователя, сразу после его первого входа.

Внешний вход перестал работать за одну ночь.

Сервис поменял правила приложения или отозвал разрешения. Обычный вход по логину и паролю обязан оставаться доступным всегда и для всех.

Частые вопросы

Можно ли привязать вход к существующей учётной записи?

Да, из профиля уже авторизованного пользователя. Автоматическая привязка по совпадению почты небезопасна.

Что делать, если сервис не отдаёт почту?

Создавать пользователя без неё и просить дозаполнить профиль позже. Требовать почту прямо на входе не стоит.

Как отвязать аккаунт от сервиса?

Удалением записи о связи из профиля пользователя. После этого вход кнопкой создаст новую учётную запись.

Нужен ли пароль такому пользователю?

Для покупателя - нет, для сотрудника - обязательно. Иначе отказ сервиса закрывает ему доступ полностью.

Смежное

Первоисточник