Подключение
Инвойсбокс ID работает по OAuth 2.0, поток Authorization Code с расширением PKCE. Пользователь подтверждает вход на стороне Инвойсбокса, ваш сервис получает одноразовый код и обменивает его на токен — уже со своего сервера.
Шаг 1. Подготовьте PKCE
PKCE защищает обмен кода без секрета приложения: сервис создаёт случайную строку codeVerifier,
передаёт в Инвойсбокс ID только её хеш, а при обмене показывает исходную строку. Перехваченный код без
codeVerifier бесполезен.
const b64url = (buf) =>
btoa(String.fromCharCode(...new Uint8Array(buf)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
const random = (len = 64) => {
const arr = new Uint8Array(len);
crypto.getRandomValues(arr);
return b64url(arr.buffer).slice(0, len);
};
const codeVerifier = random(64);
const state = random(32);
const codeChallenge = b64url(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier)));
// verifier и state понадобятся после возврата — держите их в sessionStorage
sessionStorage.setItem('ib_pkce_verifier', codeVerifier);
sessionStorage.setItem('ib_oauth_state', state);
state — случайная строка, которую Инвойсбокс ID вернёт без изменений. Сравнив её после возврата, вы
убеждаетесь, что пришли из своего же запроса, а не по чужой ссылке.
Шаг 2. Отправьте пользователя на Инвойсбокс ID
const params = new URLSearchParams({
applicationId: 'ваш-идентификатор-приложения',
redirectUri: `${location.origin}/auth/callback/`,
state,
codeChallenge,
codeChallengeMethod: 'S256',
});
location.href = `https://id.invoicebox.ru/?${params}`;
redirectUri должен точно совпадать с тем, который вы прислали в
службу поддержки при получении идентификатора приложения:
Инвойсбокс ID возвращает пользователя только на заранее известный адрес.
Шаг 3. Примите возврат
Инвойсбокс ID вернёт пользователя на redirectUri с параметрами code и state. Страница возврата
должна сверить state, забрать codeVerifier и отдать оба значения на свой сервер — обмен кода в
браузере оставил бы токен в JavaScript, откуда его достанет любой сторонний скрипт.
const url = new URL(location.href);
const code = url.searchParams.get('code');
const returned = url.searchParams.get('state');
if (!code || returned !== sessionStorage.getItem('ib_oauth_state')) {
// чужой или повторно открытый адрес — вход не начинаем
location.replace('/');
} else {
await fetch('/api/auth/exchange', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, codeVerifier: sessionStorage.getItem('ib_pkce_verifier') }),
});
sessionStorage.removeItem('ib_pkce_verifier');
sessionStorage.removeItem('ib_oauth_state');
}
Шаг 4. Обменяйте код на токен
Обмен делает ваш сервер:
POST /v3/security/api/auth/token
Content-Type: application/json
Accept: application/json
{
"applicationId": "ваш-идентификатор-приложения",
"redirectUri": "https://ваш-сервис.ru/auth/callback/",
"code": "полученный-код",
"codeVerifier": "сохранённый-verifier"
}
В ответе приходит accessToken в свойстве data. Полный разбор ответа и остальных запросов — в
справочнике.
Секрет приложения в запросе не нужен: приложения Инвойсбокс ID публичные, подлинность обмена
подтверждает codeVerifier.
Шаг 5. Проверьте, кто вошёл
Токен без проверки ничего не значит: вызовите метод профиля и убедитесь, что userId не пустой.
GET /v3/security/api/auth/auth
Authorization: Bearer полученный-токен
Accept: application/json
200 с userId: null означает, что токен не передан или не распознан — это не успешный вход, см.
авторизацию.
Дальше токен нужно положить в серверную сессию и не отдавать в браузер — как это сделать, описано в сессии и безопасности.