Bototeka OAuth

Подключите вход за один вечер

Стандартный OpenID Connect, готовые страницы регистрации и входа, управление клиентами и понятная тарификация.

openid-configurationРаботает
{
  "issuer": "https://bototeka.com/api",
  "authorization_endpoint": "https://bototeka.com/api/oauth/authorize",
  "token_endpoint": "https://bototeka.com/api/oauth/token",
  "userinfo_endpoint": "https://bototeka.com/api/oauth/userinfo",
  "scopes_supported": ["openid", "email", "profile", "offline_access"]
}

Быстрый старт

Bototeka работает с любой библиотекой OpenID Connect, которая поддерживает Authorization Code и PKCE S256.

  1. Создайте приложение

    Укажите название, callback вашего сервиса и логотип. Callback Google, Apple и Яндекса уже настроены в Bototeka.

  2. Сохраните доступы

    Скопируйте Client ID и Client Secret. Секрет показывается только один раз.

  3. Настройте OIDC

    Передайте библиотеке issuer. Остальные адреса она получит через Discovery.

  4. Проверьте сценарий

    Запустите вход, проверьте 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.

Создание authorization URL
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());
Обмен code на токены
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'
После callback: сравните state, обменяйте code один раз, проверьте подпись ID Token через JWKS, а также issuer, audience, expiry и nonce. Пользователя храните по паре iss + sub, не по email.

Регистрационные данные по API

Создайте API-ключ в настройках компании и запросите клиентов или события только своего приложения.

GET/applications/{applicationId}/customers

Возвращает sub, подтверждённый email, имя и даты активности.

GET/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 и примените задержку с разбросом.

База знаний

Когда списывается оплата?

После успешной выдачи токена: первая сессия клиента считается регистрацией, следующие сессии считаются входами. Неуспешные и отменённые попытки, refresh, UserInfo и logout не тарифицируются.

Можно ли использовать только email?

Да. Bototeka поддерживает регистрацию по email, письма с кодом, подтверждение адреса и восстановление пароля без социальных провайдеров.

Можно ли подключить SPA?

Да, как public client с PKCE и коротким access token в памяти. Для production предпочтителен backend или BFF с HttpOnly-сессией.

Что хранить как ID клиента?

Храните связку issuer и sub. Email может измениться и не является стабильным идентификатором.

Как сменить секрет?

Создайте или ротируйте API-ключ в настройках компании. Новый секрет скопируйте сразу, затем обновите серверную конфигурацию и отзовите старый доступ.

Готовы подключить

Создайте приложение, сохраните секрет и используйте issuer из этой документации.

Открыть настройки OAuth