Аутентификация
Все IDCloud API (контракты Web & SDK и API) используют OAuth2 с JWT Bearer Grant Type (RFC 7523). Вы генерируете кратковременный JWT assertion на своём бэкенде, обмениваете его на Bearer-токен и используете этот токен в каждом последующем запросе.
JWT assertion должен генерироваться исключительно на вашем бэкенде. Никогда не раскрывайте закрытый ключ во фронтенд-коде, мобильных приложениях, репозиториях или журналах.
Получение учётных данных
Прежде чем генерировать токены, вам нужен сервисный аккаунт, выданный Unico. Обратитесь в поддержку Unico и предоставьте:
- Название сервисного аккаунта (не более 12 символов)
- Имя, email и телефон ответственного лица (только номера Бразилии, США или Мексики)
Вы получите:
- Уникальное название аккаунта
- Tenant ID
- Базовую полезную нагрузку JWT
- Файл закрытого ключа (формат
.pem)
Используйте отдельные сервисные аккаунты для UAT и Production.
Создание JWT assertion
Assertion — это JWT в компактном формате JWS: {Base64url(Header)}.{Base64url(Payload)}.{Base64url(Signature)}.
{
"alg": "RS256",
"typ": "JWT"
}
| Claim | Значение | Примечания |
|---|---|---|
iss | <account_name>@<tenant_id>.iam.acesso.io | Предоставляется вместе с учётными данными |
aud | https://identityhomolog.acesso.io (UAT) или https://identity.acesso.io (Production) | Должен совпадать с хос том конечной точки токена |
scope | * | Предоставляет все разрешения |
iat | Временная метка Unix (в секундах) | Время выдачи JWT |
exp | iat + не более 3600 | Не может превышать 1 час от iat |
{
"aud": "https://identity.acesso.io",
"scope": "*",
"iat": 1738086000,
"exp": 1738089600
}
Подпишите заголовок и полезную нагрузку с помощью RS256 (RSA + SHA-256), используя закрытый ключ .pem, предоставленный Unico.
Любое поле, не указанное выше (например, sub, jti, nbf), вызовет ошибку 1.2.22. Используйт е только перечисленные claims.
Запрос токена
Конечная точка токена одинакова для обоих контрактов:
| Среда | Конечная точка |
|---|---|
| Production | POST https://identity.acesso.io/oauth2/token |
| UAT | POST https://identityhomolog.acesso.io/oauth2/token |
| Параметр | Значение |
|---|---|
Content-Type | application/x-www-form-urlencoded |
grant_type | urn:ietf:params:oauth:grant-type:jwt-bearer |
assertion | Ваш подписанный JWT |
- cURL
- Node.js
- Python
curl -X POST https://identity.acesso.io/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
-d "assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
import jwt from 'jsonwebtoken';
import fs from 'fs';
import qs from 'querystring';
const privateKey = fs.readFileSync('./private-key.pem');
const now = Math.floor(Date.now() / 1000);
const assertion = jwt.sign(
{
aud: 'https://identity.acesso.io',
scope: '*',
iat: now,
exp: now + 3600,
},
privateKey,
{ algorithm: 'RS256' }
);
const res = await fetch('https://identity.acesso.io/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: qs.stringify({
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion,
}),
});
const { access_token, expires_in } = await res.json();
import time
import jwt # PyJWT
import requests
with open("private-key.pem", "rb") as f:
private_key = f.read()
now = int(time.time())
assertion = jwt.encode(
{
"aud": "https://identity.acesso.io",
"scope": "*",
"iat": now,
"exp": now + 3600,
},
private_key,
algorithm="RS256",
)
response = requests.post(
"https://identity.acesso.io/oauth2/token",
data={
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": assertion,
},
)
token_data = response.json()
access_token = token_data["access_token"]
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
| Поле | Тип | Описание |
|---|---|---|
access_token | string | JWT токен доступа. Используйте в Authorization: Bearer <token> во всех вызовах API. |
expires_in | integer | Время истечения срока действия в секундах. Пример: 3600. |
token_type | string | Всегда Bearer. |
Использование токена
Добавьте токен в заголовок Authorization каждого API-запроса. Заголовок одинаков для обоих контрактов — различается только хост и путь вызываемого API:
| Контракт | Production-хост | UAT-хост |
|---|---|---|
| Web & SDK | https://api.idcloud.unico.app | https://api.idcloud.uat.unico.app |
| API | https://api.id.unico.app | https://api.id.uat.unico.app |
Web & SDK — Authorization — единственный необходимый заголовок аутентификации:
curl -X POST https://api.idcloud.unico.app/client/v1/process \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ ... }'
API — Authorization используется вместе с заголовком APIKEY:
curl -X POST https://api.id.unico.app/processes/v1 \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "APIKEY: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ ... }'
Обновление токена
Токены истекают через 3600 секунд. Реализуйте упреждающее обновление на своём бэкенде:
- Отслеживайте
expires_inиз ответа с токеном и сохраняйте временную метку истечения. - Запрашивайте новый токен, когда до истечения остаётся 10 минут или меньше.
- Никогда не ждите
401в production для запуска обновления.
Справочник API
Полная спецификация OpenAPI для конечной точки токена доступна в authentication.yaml.
| Метод | Путь | Описание |
|---|---|---|
POST | /oauth2/token | Обмен подписанного JWT assertion на Bearer токен доступа |
Коды ошибок
| Код | Описание | Действие |
|---|---|---|
1.0.1 | ID, указанный в iss, некорректен | Убедитесь, что поле iss совпадает с Tenant ID, предоставленным при генерации закрытого ключа |
1.0.14 | Приложение неактивно | Уточните у менеджера проекта, активно ли используемое приложение |
1.1.1 | Параметр scope не указан | Добавьте "scope": "*" в полезную нагрузку JWT |
1.2.4 | JWT assertion недействителен | JWT assertion более не действителен. Две причины: (а) текущее время превышает exp (JWT действительно истёк — генерируйте новый assertion для каждого запроса токена); или (б) exp превышает iat + 3600 (срок действия слишком долгий — ограничьте exp значением iat + 3600). |
1.2.5 | Ошибка валидации JWT | JWT не удалось проверить. Проверьте параметры и убедитесь, что он подписан с помощью RS256 и правильного закрытого ключа |
1.2.6 | Закрытый ключ более недействителен | Закрытый ключ, использованный для подписи JWT, больше не принимается. Запросите новые учётные данные для аккаунта |
1.2.7 | JWT уже использован | JWT больше не принимается, так как уже был использован. Генерируйте новый assertion для каждого запроса токена |
1.2.11 | Аккаунт неактивен | Используемый аккаунт неактивен |
1.2.14 | Аккаунту не хватает необходимых разрешений | Используемый аккаунт не имеет необходимых разрешений |
1.2.18 | Аккаунт временно заблокирован | Аккаунт временно заблокирован из-за превышения числа неудачных попыток аутентификации |
1.2.19 | Несанкционированное олицетворение пользователя | JWT содержит claim sub, указывающий на аккаунт, которому не разрешено олицетворение. Удалите claim sub из полезной нагрузки. |
1.2.20 | Ошибка декодирования JWT | Не удалось декодировать JWT. Проверьте формат токена и убедитесь, что он подписан с помощью RS256. |
1.2.21 | Неверный закрытый ключ / Ошибка аутентификации | Подпись JWT не удалось проверить ни одним из известных ключей для этого аккаунта. Убедитесь, что вы используете правильный закрытый ключ .pem для данного сервисного аккаунта и среды. |
1.2.22 | Недопустимые поля в полезной нагрузке | JWT содержит лишние поля, которые не разрешены. Удалите все claims, не указанные в этом руководстве (например, sub, jti, nbf). Примечание: если вы включили claim sub и получили ошибку 1.2.19, эта ошибка имеет приоритет. |
1.3.1 | Ограничение доступа по IP | Ваш IP не включён в список разрешённых для этого аккаунта |
1.3.2 | Ограничение доступа по времени | Запрос выполнен вне допустимого временного окна для этого аккаунта |
Что дальше
- Среды — хосты sandbox и production
- Web & SDK — Создание процесса — первый вызов после аутентификации
- API — Создание процесса — первый вызов после аутентификации
- SSO / SAML — аутентифицируйте своих пользователей в продуктовых порталах Unico с помощью собственного провайдера идентификации вместо описанного выше потока server-to-server