Быстрый старт
Bototeka работает с любой библиотекой OpenID Connect, которая поддерживает Authorization Code и PKCE S256.
Создайте приложение
Укажите название, callback вашего сервиса и логотип. Callback Google, Apple и Яндекса уже настроены в Bototeka.
Сохраните доступы
Скопируйте Client ID и Client Secret. Секрет показывается только один раз.
Настройте OIDC
Передайте библиотеке issuer. Остальные адреса она получит через Discovery.
Проверьте сценарий
Запустите вход, проверьте state и nonce, обменяйте code и создайте локальную сессию.
- Issuer
- https://bototeka.com/api
- Discovery URL
- https://bototeka.com/api/.well-known/openid-configuration
- Service API
- https://api.bototeka.com/api/identity/v1
- Пример Redirect URI
- https://service.example/auth/callback
Вход и токены
Ваш backend создаёт PKCE, state и nonce, отправляет пользователя в Bototeka и принимает только короткий authorization code.
import { createHash, randomBytes } from "node:crypto";
const clientId = process.env.BOTOTEKA_CLIENT_ID;
if (!clientId) throw new Error("BOTOTEKA_CLIENT_ID is required");
const verifier = randomBytes(32).toString("base64url");
const challenge = createHash("sha256").update(verifier).digest("base64url");
const state = randomBytes(24).toString("base64url");
const nonce = randomBytes(24).toString("base64url");
const authorize = new URL("https://bototeka.com/api/oauth/authorize");
authorize.search = new URLSearchParams({
client_id: clientId,
redirect_uri: "https://service.example/auth/callback",
response_type: "code",
scope: "openid email profile",
code_challenge: challenge,
code_challenge_method: "S256",
state,
nonce,
}).toString();
// Save verifier, state and nonce in the user's server-side session.
console.log(authorize.toString());curl --request POST 'https://bototeka.com/api/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=YOUR_CLIENT_ID' \
--data-urlencode 'client_secret=YOUR_CLIENT_SECRET' \
--data-urlencode 'code=CODE_FROM_CALLBACK' \
--data-urlencode 'redirect_uri=https://service.example/auth/callback' \
--data-urlencode 'code_verifier=SAVED_PKCE_VERIFIER'curl 'https://bototeka.com/api/oauth/userinfo' \
--header 'Authorization: Bearer ACCESS_TOKEN'Регистрационные данные по API
Создайте API-ключ в настройках компании и запросите клиентов или события только своего приложения.
/applications/{applicationId}/customersВозвращает sub, подтверждённый email, имя и даты активности.
/applications/{applicationId}/eventsВозвращает регистрацию или вход, сумму, валюту, статус и время события.
curl 'https://api.bototeka.com/api/identity/v1/applications/APPLICATION_ID/customers?limit=50' \
--header 'Authorization: Bearer btk_v1_YOUR_API_KEY'curl 'https://api.bototeka.com/api/identity/v1/applications/APPLICATION_ID/events?limit=50' \
--header 'Authorization: Bearer btk_v1_YOUR_API_KEY'Для чтения нужен scope identity:read. Передавайте ключ как Bearer token и храните его только на сервере.
Вебхуки
Подпишитесь на события в настройках компании. URL должен использовать HTTPS, а секрет подписи показывается один раз.
identity.registration.completed.v1Регистрация завершена и зафиксирована в журнале использования.
identity.login.completed.v1Повторный вход завершён и зафиксирован в журнале использования.
Доставка выполняется как минимум один раз. Дедуплицируйте события по event ID, принимайте их в любом порядке и отвечайте 2xx после сохранения.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyBototekaWebhook(rawBody, headers, secret) {
const timestamp = headers["x-bototeka-webhook-timestamp"];
const received = headers["x-bototeka-webhook-signature"];
if (!timestamp || !received) return false;
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = "v1=" + createHmac("sha256", secret)
.update(timestamp + "." + rawBody)
.digest("hex");
const left = Buffer.from(expected);
const right = Buffer.from(received);
return left.length === right.length && timingSafeEqual(left, right);
}Проверки перед production
- Используйте точный HTTPS Redirect URI и не принимайте callback на произвольный адрес.
- Генерируйте новый state, nonce и PKCE verifier для каждой попытки и храните их в серверной сессии.
- Не сохраняйте Client Secret, API-ключи, access token и refresh token в браузере или мобильной аналитике.
- Разрешайте offline_access только когда вашему backend действительно нужна длительная сессия.
- Проверяйте подпись вебхука по необработанному телу и защищайтесь от повторной доставки.
Ошибки и восстановление
invalid_requestЧто произошло
Не хватает параметра или он некорректен.
Что делать
Проверьте Redirect URI, PKCE, state, nonce и Content-Type.
invalid_grantЧто произошло
Code истёк, уже использован или не соответствует verifier.
Что делать
Начните новый вход. Не повторяйте обмен того же code.
temporarily_unavailableЧто произошло
На балансе приложения недостаточно средств.
Что делать
Пополните баланс в настройках OAuth и повторите новый вход.
429Что произошло
Превышен лимит запросов или попыток подтверждения.
Что делать
Уважайте Retry-After и примените задержку с разбросом.
| Код | Что произошло | Что делать |
|---|---|---|
| invalid_request | Не хватает параметра или он некорректен. | Проверьте Redirect URI, PKCE, state, nonce и Content-Type. |
| invalid_grant | Code истёк, уже использован или не соответствует verifier. | Начните новый вход. Не повторяйте обмен того же code. |
| temporarily_unavailable | На балансе приложения недостаточно средств. | Пополните баланс в настройках OAuth и повторите новый вход. |
| 429 | Превышен лимит запросов или попыток подтверждения. | Уважайте Retry-After и примените задержку с разбросом. |
База знаний
Когда списывается оплата?
После успешной выдачи токена: первая сессия клиента считается регистрацией, следующие сессии считаются входами. Неуспешные и отменённые попытки, refresh, UserInfo и logout не тарифицируются.
Можно ли использовать только email?
Да. Bototeka поддерживает регистрацию по email, письма с кодом, подтверждение адреса и восстановление пароля без социальных провайдеров.
Можно ли подключить SPA?
Да, как public client с PKCE и коротким access token в памяти. Для production предпочтителен backend или BFF с HttpOnly-сессией.
Что хранить как ID клиента?
Храните связку issuer и sub. Email может измениться и не является стабильным идентификатором.
Как сменить секрет?
Создайте или ротируйте API-ключ в настройках компании. Новый секрет скопируйте сразу, затем обновите серверную конфигурацию и отзовите старый доступ.
Готовы подключить
Создайте приложение, сохраните секрет и используйте issuer из этой документации.