К содержимому

Стандартный OAuth 2.1

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

Поток из справочника — наш исторический: параметры в camelCase, ответы в общей оболочке data. Он работает и останется работать; если у вас уже написан обмен по нему, переделывать его не нужно.

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

Важно

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

Что нужно получить заранее

Идентификатор приложения (client_id), список адресов возврата и набор прав запрашиваются у службы поддержки. Права — это именованные наборы, те же, что видны в личном кабинете: приложение не может попросить больше, чем ему разрешили при регистрации, а пользователь на экране согласия видит их человеческими названиями, а не кодами.

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

Метаданные сервера авторизации

Адреса не нужно хранить в коде — сервер сообщает их сам (RFC 8414).

  • метод: GET
  • ресурс: /.well-known/oauth-authorization-server
{
  "issuer": "https://api.invoicebox.ru",
  "authorization_endpoint": "https://id.invoicebox.ru/oauth/authorize",
  "token_endpoint": "https://api.invoicebox.ru/oauth/token",
  "revocation_endpoint": "https://api.invoicebox.ru/oauth/revoke",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256"],
  "token_endpoint_auth_methods_supported": ["client_secret_post", "none"]
}

Библиотеки OAuth читают этот документ сами: обычно достаточно указать им issuer. Ответ 404 означает, что стандартные адреса в этой установке ещё не включены.

Шаг 1. Экран согласия

Пользователя отправляют на authorization_endpoint из метаданных.

ПараметрЧто означает
response_type *code
client_id *идентификатор приложения
redirect_uri *адрес возврата; должен совпадать с зарегистрированным
state *случайная строка; вернётся без изменений, по ней сверяется подлинность возврата
code_challenge *SHA-256 от code_verifier в base64url
code_challenge_method *S256
scopeзапрашиваемые права через пробел; без него запрашиваются все права приложения
resourceадрес сервиса, для которого нужен токен (см. ниже)
https://id.invoicebox.ru/oauth/authorize?response_type=code&client_id=my-service&redirect_uri=https%3A%2F%2Fmy-service.ru%2Fauth%2Fcallback%2F&state=8f2c…&code_challenge=k7Yb…&code_challenge_method=S256&scope=merchant-read

Пользователь входит (или уже вошёл) и видит, какое приложение просит доступ и какие права. Дальше одно из двух:

  • нажал «Разрешить» — возврат на redirect_uri с параметрами code и state;
  • нажал «Отклонить» — возврат на redirect_uri с error=access_denied и тем же state.

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

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

Шаг 2. Обмен кода на токен

  • метод: POST
  • ресурс: token_endpoint из метаданных
  • тело: application/x-www-form-urlencoded
  • вызывается с сервера: токен не должен попадать в браузер
ПараметрЧто означает
grant_type *authorization_code
client_id *идентификатор приложения
code *код из параметров возврата
redirect_uri *тот же адрес, что в запросе авторизации
code_verifier *исходная строка, от которой считался code_challenge
client_secretтолько если для приложения выпущен секрет
resourceтот же адрес, что в запросе авторизации
curl -X POST https://api.invoicebox.ru/oauth/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=authorization_code' \
  -d 'client_id=my-service' \
  -d 'code=e3b0c44298fc1c14' \
  -d 'redirect_uri=https://my-service.ru/auth/callback/' \
  -d 'code_verifier=s9Zx1kQd…'

Ответ — без общей оболочки data, поля называются по стандарту:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "def50200a1b2…",
  "scope": "merchant-read"
}

expires_in — срок в секундах от момента ответа. Код обмена одноразовый: повторный запрос с тем же code вернёт invalid_grant.

С полученным токеном запросы к API подписываются заголовком Authorization: Bearer <токен> — как и при обычной авторизации.

Обновление токена

  • те же адрес и формат тела
  • grant_type=refresh_token, client_id, refresh_token
curl -X POST https://api.invoicebox.ru/oauth/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=refresh_token' \
  -d 'client_id=my-service' \
  -d 'refresh_token=def50200a1b2…'

В ответе приходит новая пара токенов. Прежний refresh_token при этом перестаёт действовать — храните только последний.

Внимание

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

Так сделано намеренно. Если старый токен обновления пришёл во второй раз, значит он есть у кого-то ещё, — и правильный ответ здесь не «отказать в одном запросе», а прекратить доступ по всей цепочке. Практический вывод для вас: не обновляйте токен из двух процессов одновременно и не повторяйте запрос обновления при таймауте, не проверив, не пришёл ли ответ.

Отзыв доступа

  • метод: POST
  • ресурс: revocation_endpoint из метаданных
  • тело: application/x-www-form-urlencoded, параметры client_id, token, необязательный token_type_hint (refresh_token или access_token)
curl -X POST https://api.invoicebox.ru/oauth/revoke \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'client_id=my-service' \
  -d 'token=def50200a1b2…' \
  -d 'token_type_hint=refresh_token'

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

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

Ограничение токена одним сервисом

Параметр resource (RFC 8707) говорит, для какого нашего сервиса нужен токен. Указали — токен принимается только там; в другом сервисе он не сработает, даже если прав хватает.

Это защита от посредника. Приложение, которому доверили доступ к одному сервису, не должно уметь тем же токеном сходить в другой — а без указанного ресурса токен принимается всюду, где хватает прав.

Значение — адрес сервиса, целиком и без якоря, например https://mcp.invoicebox.ru/mcp. Незнакомый адрес отклоняется с invalid_target: список известных ресурсов задаём мы, иначе токен получил бы аудиторию, которую никто не проверяет. Указанный на первом шаге ресурс должен повториться при обмене кода — иначе приложение получило бы доступ не туда, куда согласился пользователь.

PKCE обязателен

code_challenge и code_verifier нужны всем приложениям, включая серверные с секретом. Причина не в формальности: код возвращается через браузер пользователя, а секрет приложения в этом обмене не участвует — перехваченный код без code_verifier бесполезен, с секретом или без.

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

Ошибки

Отказы приходят по стандарту: код в поле error, пояснение для разработчика — в error_description.

{
  "error": "invalid_grant",
  "error_description": "code: authorization code has expired"
}
КодЧто случилосьЧто делать
invalid_requestне хватает параметра или он пустойисправить запрос
invalid_clientclient_id неизвестен или приложение отключенопроверить идентификатор; отвечает 401
invalid_grantкод или токен обновления не подошёл: истёк, уже использован, выпущен для другого адреса возвратапройти авторизацию заново
unsupported_grant_typeтакой способ получения токена не поддерживаетсяиспользовать authorization_code или refresh_token
invalid_scopeзапрошены права, которых у приложения нетсогласовать права с поддержкой
invalid_targetнезнакомый или несовпадающий resourceпроверить адрес сервиса

Читайте также

Страница помогла?