Bitrix24

Цикл OAuth

Authorize, обмен кода на токены, обновление по refresh_token и поля, которые портал передаёт фрейму приложения.

На этой странице · 8

Сервер авторизации APIStend отвечает тем же конвертом, что боевой oauth.bitrix.info: те же имена полей, те же коды ошибок, те же сроки жизни. Полный цикл проверяется одним curl, не поднимая приложение.

Два способа получить токены

Приложению с интерфейсом токены приходят сами: портал кладёт их в тело POST-запроса при открытии фрейма — это «упрощённая авторизация», и большинству приложений её достаточно. Полный цикл authorize → token нужен там, где приложение авторизуется само: серверная часть без фрейма, CRest, свой обмен кода.

Authorize

curl -si "http://localhost:8080/oauth/authorize/?client_id=local.aabbccddeeff11.12345678&state=xyz"
HTTP/1.1 302 Found
location: http://localhost:3210/handler?code=77im8soiuda2ru9des1y3pf3yko96ivr&state=xyz
  &domain=localhost%3A8080&member_id=16ae2b1b95d1092ee78bc6d344af2224
  &scope=crm%2Cplacement%2Cuser&server_domain=localhost%3A8080

(в реальном ответе location — одна строка).

Параметры authorize

ПараметрОбязательныйЧто делает
client_idдаприложение; неизвестный или удалённый — invalid_client
redirect_uriнеткуда вернуть код. Без него берётся путь обработчика из карточки
stateнетвозвращается в редиректе как есть
user_idнетот чьего имени выдать код; по умолчанию 1 — администратор портала

Боевой портал redirect_uri в запросе не принимает и берёт адрес из карточки приложения. Здесь он принимается как послабление: у локального приложения такого поля в форме нет, и проверить полный цикл иначе было бы негде.

Экрана входа и согласия нет: код выдаётся сразу. Кем открыто приложение, выбирается в шапке демонстрационного портала, а не в OAuth.

Обмен кода на токены

curl -s -X POST http://localhost:8080/oauth/token/ \
  -d "grant_type=authorization_code" \
  -d "client_id=local.aabbccddeeff11.12345678" \
  -d "client_secret=$CLIENT_SECRET" \
  -d "code=$CODE"
{
  "access_token": "sawe6p7tj4yavjd6ws65ia098gpjpc8b",
  "refresh_token": "nfr1a7wvqzcp6c7z9khovj8yvdkweipb",
  "expires_in": 3600,
  "expires": 1788952526,
  "scope": "crm,placement,user",
  "domain": "localhost:8080",
  "server_endpoint": "http://localhost:8080/oauth/rest/",
  "client_endpoint": "http://localhost:8080/rest/",
  "member_id": "16ae2b1b95d1092ee78bc6d344af2224",
  "status": "L",
  "user_id": 1
}

Имена полей не переименованы ни в одном месте: их разбирает клиентский код разработчика. Метод принимается и как GET, и как POST; ответ отдаётся с Access-Control-Allow-Origin: *.

  • domain — хост портала, без схемы: приложение склеивает адрес само, подставляя схему из PROTOCOL.
  • client_endpoint — база REST, куда идут вызовы методов.
  • server_endpoint — база сервера авторизации.
  • member_id — постоянный идентификатор портала. Считается от песочницы и не меняется от смены домена, как и в бою.
  • status — вид приложения; у локального всегда L.

Обновление

curl -s -X POST http://localhost:8080/oauth/token/ \
  -d "grant_type=refresh_token" \
  -d "client_id=$CLIENT_ID" -d "client_secret=$CLIENT_SECRET" \
  -d "refresh_token=$REFRESH_TOKEN"

Ответ — тот же конверт с новой парой. Прежняя пара гасится: старый refresh_token на второй обмен отвечает invalid_grant, а старый access_token — expired_token. Именно expired_token, а не NO_AUTH_FOUND: у приложения могла остаться копия прежнего токена (например, во фрейме, пока серверная часть уже обновилась), и она обязана узнать «пора обновиться», а не «такого токена не существует».

Обновлять по расписанию нельзя

Документация Bitrix24 предписывает дождаться ошибки expired_token и только после неё идти за новой парой. Обновление раз в час или перед каждым вызовом там прямо запрещено.

Сроки жизни

ЧтоПо умолчаниюНастраивается
access_token3600 секунд, как в боюот 5 секунд до суток
refresh_token180 суток, как в боюот 60 секунд до 180 суток
авторизационный код30 секунднет

Тридцати секунд достаточно, чтобы обменять код сразу, и недостаточно, чтобы «пока сохраню, потом обменяю». Код одноразовый: повторный обмен — invalid_grant.

Срок токенов настраивается в карточке приложения — это единственное место, где APIStend сознательно расходится с боевым порталом. Причина в том, что проверять надо поведение приложения после отказа, а в бою срок всегда час и ускорить его нечем. Настроенный срок уходит приложению честно: в expires_in ответа сервера авторизации и в AUTH_EXPIRES при открытии фрейма.

Кнопка «Состарить сейчас» в карточке состаривает все действующие пары немедленно. Токены именно состариваются, а не отзываются: приложение обязано получить expired_token и пойти обновляться.

Перед переносом верните 3600

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

Что приходит во фрейм

Приложение с интерфейсом открывается POST-запросом, и данные разделены между адресом и телом так же, как в бою: в query-строке токенов нет.

ГдеПолеЧто в нём
queryDOMAINхост портала
queryPROTOCOL0 — http, 1 — https
queryLANGязык интерфейса; здесь всегда ru
queryAPP_SIDидентификатор сеанса приложения; связывает BX24.js с окружением
телоAUTH_IDaccess-токен пользователя, открывшего фрейм
телоAUTH_EXPIRESсколько секунд токен ещё живёт
телоREFRESH_IDrefresh-токен
телоSERVER_ENDPOINTадрес сервера авторизации
телоAPPLICATION_TOKENтот же токен приходит в событиях
телоAPPLICATION_SCOPEвыданные права через запятую
телоmember_idидентификатор портала
телоstatusвид приложения; у локального всегда L
телоPLACEMENTкод точки встраивания, из которой открыт обработчик
телоPLACEMENT_OPTIONSконтекст вызова строкой JSON, а не объектом

Каждое открытие фрейма выпускает новую пару токенов, а прежняя продолжает действовать до своего срока — как и в бою: копия приложения в соседней вкладке не должна внезапно терять доступ.

PLACEMENT_OPTIONS разбирается на стороне приложения. Кроме ключей самой точки встраивания портал кладёт туда URI — путь страницы, с которой открыт виджет: для вкладки в карточке сделки это /crm/deal/details/<ID>/.

Токены в событиях

Событие, адресованное приложению, несёт в auth[] действующий access_token — обработчику есть чем ответить, не поднимая своё хранилище. Тело — application/x-www-form-urlencoded с PHP-скобками: event, event_handler_id, data[...], ts, auth[domain], auth[client_endpoint], auth[server_endpoint], auth[member_id], auth[application_token], auth[status], auth[access_token], auth[expires_in], auth[scope].

Два исключения:

  • в ONAPPINSTALL дополнительно приходит auth[refresh_token] — это единственный момент, когда приложению негде взять долгоживущую авторизацию иначе;
  • в ONAPPUNINSTALL нет ни access_token, ни expires_in, ни scope: права уже сняты.

Ошибки

Сервер авторизации отвечает на ошибку вместо пары токенов, всегда со статусом 400:

Ответы /oauth/token

КодКогда
invalid_clientнеизвестный client_id, неверный client_secret или удалённое приложение
invalid_grantкод неизвестен, просрочен или уже использован; refresh_token погашен или просрочен
invalid_requestgrant_type не authorization_code и не refresh_token (error_description: Unsupported grant type)

Ошибки контекста приложения приходят в конверте REST:

Ответы /rest/…

КодHTTPКогда
expired_token401токен просрочен или замещён обновлением — нужно обновить пару
NO_AUTH_FOUND401токена не существует, он отозван или приложение удалено
insufficient_scope403у приложения нет права на этот метод