Что отправляете
Обычный authorize-запрос, но со scopechannel:
Что получаете
После обмена кода на токены — два дополнительных claim:
Без scope
channel типизированных claims не будет. Они не попадут ни в access_token, ни в
id_token, а значит не появятся и в ответе /connect/userinfo — он зеркалит access token.
Гейт действует во всех режимах поставки, включая встроенный. Даже когда Veriqa подключена
библиотекой в ваш же процесс, приложение получает идентичность штатным обменом
authorize → code → token, а не готовым принципалом. Отдельного «внутреннего» пути, отдающего
claims канала мимо scope, не существует.
Как связать
sub — стабильный идентификатор субъекта; пара channel_type + channel_user_id — адрес
пользователя в конкретном мессенджере. Привязка хранится на вашей стороне: Veriqa не ведёт
учётных записей вашего приложения и не сопоставляет их с каналами.
Практическое следствие: sub для вас — ключ учётной записи. Так же поступают и готовые CMS —
Drupal и TYPO3 хранят его как имя внешней идентичности.
Если аккаунт уже существует
Сценарий работает и как «дозаведение канала» к существующей учётной записи: пользователь, вошедший обычным способом, проходит authorize со scopechannel, а вы сохраняете полученную пару
рядом со своим аккаунтом. Отдельного API для этого нет — тот же самый запрос.
Привязка с сервера без входа
Бывает, что привязку не к чему прицепить — редиректа браузера нет: ваш сервер сам хочет получить канал для учётной записи — например, вошедший пользователь просит бота поддержки подключить мессенджер. Это делается через server-to-server вход подтверждения и следующий за ним обмен на токен. Что включает интегратор у клиента:AllowClientCredentials, AllowConfirmationTokenGrant и
openid в AllowedScopes (и channel, если нужны типизированные claims). Разрешение — интегратора:
ни один параметр вашего запроса раскрытие не расширяет, а claims не выходят за AllowedScopes.
-
Создайте транзакцию подтверждения —
POST /api/transaction/confirmation, безexpected_identities: вы ещё не знаете, какой аккаунт канала подтвердит, — именно это вы и хотите узнать. Покажите пользователюchannel_entryиз ответа. -
Дождитесь исхода —
GET /api/transaction/{id}/resultдоconfirmed. Без ожидаемых идентичностейmatched_typeравенnull— это не отказ, токен выдаётся всё равно. -
Обменяйте транзакцию на токен:
Ответ — обычный ответ token endpoint:
access_token,token_type,expires_inиid_token, безrefresh_token.scope— по RFC 6749 §5.1: он возвращается, только если выданный набор scopes отличается от запрошенного. Обмен выдаёт ровно запрошенный набор, поэтому члена в ответе нет — не требуйте его. -
Сохраните
subизid_tokenрядом со своей учётной записью.
scopeобязан содержатьopenid;channelдобавляетchannel_typeиchannel_user_id; всё остальное — в пределахAllowedScopes.offline_accessотвергается сinvalid_scope, как иscopeбезopenid. Нетtransaction_id—invalid_request.- Транзакция погашается однократно. Повторный обмен получает
invalid_grant— ту же ошибку с тем же описанием, что и неизвестный идентификатор, транзакция другого клиента, ещё не завершённая транзакция или уже удалённая по истеченииCompletedRetentionSeconds. Ответы намеренно не различают эти причины, поэтому обменивайте сразу послеconfirmed. Из двух одновременных обменов токен получает ровно один. - Однократность относится к погашению, а не к чтению claims:
access_tokenтого же ответа несёт те же claims и до истечения отдаёт их через/connect/userinfo. - Ключ привязки —
subцеликом. При резолвере идентичности по умолчанию он формируется как{channel_type}:{channel_user_id}, но хост со своим резолвером вправе составить его иначе — не разбирайте строку; если нужна пара, запрашивайте scopechannel.
string в контракте сообщения типа действия; каждый такой
слот обязан объявить max_length, потолок — 128 символов. Текстовку пишет интегратор, а слоты
(slot_values) при создании заполняет ваш бэкенд тем, что он видит об устройстве, — тот, кто
перешлёт QR, подделать эти значения не может: вашим бэкендом он не владеет. Например, шаблон:
service, device и location — слоты, которые заполняет ваш бэкенд. Слот {app}
подтверждения сервер заполняет сам — идентификатором приложения транзакции (client_id), а не
DisplayName клиента, — поэтому
человекочитаемое имя приложения попадает в текстовку только через ваш собственный слот. Пример синтетический: таких имён слотов и такой текстовки в
поставляемой конфигурации нет. Как объявить контракт и шаблон и как выглядят запрос и ответ целиком — в
API серверного подтверждения.
Один вход, два канала
Для чувствительных операций подтверждение можно потребовать в двух разных доверенных каналах.