Работа с токенами
Любой сценарий заканчивается одним и тем же: access_token, который вы отправляете с каждым запросом, и refresh_token, по которому получаете новую пару, когда первый истечёт.
Обмен кода авторизации
Одинаково для Flow A и Flow B.
Запрос
POST /auth/oauth/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"code": "abc123def456",
"client_id": "69c50a2d50f7b939923a469b",
"client_secret": "4f3c1a9e8b27d605f1ac93e7d284b6510ff7a3c9e15d8b2470a6cd91e83f4b27"
}| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
grant_type | string | Да | Всегда "authorization_code" |
code | string | Да | Код авторизации, полученный от Bito |
client_id | string | Да | Ваш OAuth client id |
client_secret | string | Да | Секрет вашего клиента |
Ответ
{
"code": 0,
"message": "Success",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 86400,
"token_type": "Bearer",
"scope": "read write"
}
}expires_in указан в секундах от момента выдачи.
WARNING
Код авторизации одноразовый и живёт 20 минут. Повторный обмен вернёт Invalid credentials, а не вторую пару токенов.
Обновление токенов
Запрос
POST /auth/oauth/token
Content-Type: application/json
{
"grant_type": "refresh_token",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"client_id": "69c50a2d50f7b939923a469b",
"client_secret": "4f3c1a9e8b27d605f1ac93e7d284b6510ff7a3c9e15d8b2470a6cd91e83f4b27"
}Формат ответа тот же, что при обмене кода.
WARNING
Обе части пары меняются при каждом обновлении. Использованный refresh-токен перестаёт работать — сохраняйте новую пару атомарно, иначе следующее обновление упадёт и клиенту придётся переустановить интеграцию.
Обновление токена не возвращает удалённую интеграцию. Если клиент вас удалил, refresh продолжает работать против неустановленной записи, поэтому источником правды считайте хук удаления.
Client credentials
Для серверных задач без участия пользователя. Идёт на другой эндпоинт — /auth/oauth/auth, а не /auth/oauth/token, — и сразу возвращает токены, без промежуточного кода.
WARNING
Условие: клиент уже должен установить вашу интеграцию. Этот способ проверяет секрет конкретной установки, поэтому до первой установки сверять нечего.
Запрос
POST /auth/oauth/auth
Content-Type: application/json
{
"response_type": "client_credentials",
"client_id": "69c50a2d50f7b939923a469b",
"client_secret": "4f3c1a9e8b27d605f1ac93e7d284b6510ff7a3c9e15d8b2470a6cd91e83f4b27",
"redirect_uri": "https://example-app.com/callback",
"scope": ["read", "write"],
"state": "optional_state"
}| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
response_type | string | Да | Всегда "client_credentials" |
client_id | string | Да | Ваш OAuth client id |
client_secret | string | Да | Секрет установленной интеграции |
redirect_uri | string | Да | Должен быть корректным URL. Проверяется, хотя редиректа здесь нет — отправьте свой зарегистрированный адрес. |
scope | string[] | Да | Запрашиваемые права, массивом |
state | string | Нет | Произвольное значение, возвращается как есть |
Ответ
{
"code": 0,
"message": "Success",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 86400,
"token_type": "Bearer",
"scope": "read write"
}
}Использование access-токена
Отправляйте токен в заголовке Authorization при каждом запросе.
POST /integration/api/v2/product/get-paging
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
Accept-Language: ru
{
"limit": 20,
"page": 1
}| Заголовок | Обязателен | Описание |
|---|---|---|
Authorization | Да | Bearer <access_token> |
Accept-Language | Нет | Язык ответа: uz, uz_cyrillic, ru, en, kk, tg, ky. По умолчанию uz. |
organization_id | Нет | Организация внутри аккаунта. Используется, только если в настройках интеграции (fields) есть поле organization_id, — иначе заголовок игнорируется. Если заголовок не передан, берётся значение этого поля. |
warehouse_id | Нет | Склад. Используется, только если в настройках интеграции (fields) есть поле warehouse_id, — иначе заголовок игнорируется. Если заголовок не передан, берётся значение этого поля. |
price_id | Нет | Прайс-лист для цен. Используется, только если в настройках интеграции (fields) есть поле price_id, — иначе заголовок игнорируется. Если заголовок не передан, берётся значение этого поля. |
role_id | Нет | Роль, от которой выполняется запрос. Используется, только если в настройках интеграции (fields) есть поле role_id, — иначе заголовок игнорируется. Если заголовок не передан, берётся значение этого поля. |
user_id | Нет | Выполнить запрос от имени этого пользователя. Пользователь должен принадлежать этому аккаунту и не быть заблокирован, иначе запрос отклоняется. Если не передан — от имени владельца аккаунта. |
Идентификатор аккаунта передавать не нужно — он уже внутри токена.
Сроки жизни токенов
| Токен | Срок | Примечание |
|---|---|---|
| Код авторизации | 20 минут | Одноразовый, обменять нужно внутри этого окна |
| Access-токен | 24 часа | Передаётся в заголовке Authorization |
| Refresh-токен | 90 дней | Меняется на новую пару; ротируется при каждом использовании |
| Одноразовый токен | 24 часа | Одноразовый, подписан секретом клиента — см. Одноразовые токены |
WARNING
Смена секрета клиента убивает все access- и refresh-токены, выданные под старым секретом, сразу во всех аккаунтах клиентов.
Ограничение частоты
/auth/oauth/auth и /auth/oauth/token принимают 20 запросов в минуту с одного IP. Обновляйте токен по истечении срока, а не по таймеру и тем более не перед каждым запросом.