Интеграции
ИНТЕГРАЦИИ

OAuth 2.0 + PKCE

Стандартный OAuth flow для подключения сторонних приложений и MCP-клиентов (Claude Desktop, Cursor, Cline) без копирования root-ключа.

Hubris реализует OAuth 2.0 Authorization Code + PKCE (RFC 6749 + 7636 + 9700 BCP) для авторизации сторонних приложений. Это даёт два преимущества:

  1. Sandboxed-доступ: токен выдаётся под выбранные scopes и опциональный дневной лимит. Если приложение попросит чат — оно не сможет читать ваш баланс.
  2. MCP-connector совместимость: Claude Desktop, Claude.ai, Cursor и любой MCP-клиент с native «Add custom connector» UI проходят discovery + DCR + авторизацию автоматически, без mcp-remote шима.

Discovery (RFC 8414 + RFC 9728)

Клиент начинает с одного из:

GET https://api.hubris.pw/.well-known/oauth-protected-resource
GET https://hubris.pw/.well-known/oauth-authorization-server

Второй — основной, содержит все endpoints. Пример ответа:

{
  "issuer": "https://hubris.pw",
  "authorization_endpoint": "https://hubris.pw/oauth/authorize",
  "token_endpoint": "https://hubris.pw/oauth/token",
  "registration_endpoint": "https://hubris.pw/oauth/clients",
  "revocation_endpoint": "https://hubris.pw/oauth/revoke",
  "scopes_supported": ["chat:write", "models:read", "balance:read"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256"],
  "token_endpoint_auth_methods_supported": ["none"]
}

token_endpoint_auth_methods_supported: ["none"] означает что мы поддерживаем только public clients (PKCE-only, без client_secret).

Scopes

ScopeДоступ
chat:write/v1/chat/completions, /v1/responses, /v1/messages, /mcp chat_complete
models:read/v1/models, /mcp models_list+search+get_pricing
balance:readчтение баланса (/api/internal/me, /mcp balance_get)

Приложение запрашивает подмножество в ?scope=... (space-separated). Юзер видит и подтверждает на consent-странице.

Токены

  • Access token: формат hbr-at-<32 hex>. TTL 1 час. Идёт как Authorization: Bearer ... на /v1/* и /mcp.
  • Refresh token: формат hbr-rt-<48 hex>. TTL 90 дней. Принимается только на POST /oauth/token.
  • Rotation: каждый refresh выпускает новый refresh + новый access. Старый refresh имеет 30-секундное grace-окно (защита от race-conditions при параллельных запросах). После окна — обнаружение reuse и revoke всей семьи токенов.

Поток (Python пример)

import requests, hashlib, base64, secrets

# 1. Регистрация client (one-time per app)
reg = requests.post('https://hubris.pw/oauth/clients', json={
    'client_name': 'My App',
    'redirect_uris': ['http://localhost:8765/callback'],
    'scope': 'chat:write models:read',
})
client_id = reg.json()['client_id']

# 2. PKCE
verifier = secrets.token_urlsafe(32)
challenge = base64.urlsafe_b64encode(
    hashlib.sha256(verifier.encode()).digest()
).rstrip(b'=').decode()

# 3. Браузерный flow — открыть в браузере
auth_url = (
    'https://hubris.pw/oauth/authorize'
    f'?client_id={client_id}'
    '&redirect_uri=http://localhost:8765/callback'
    '&response_type=code'
    f'&code_challenge={challenge}&code_challenge_method=S256'
    '&scope=chat:write+models:read'
    '&state=somestate'
)
# Юзер видит consent, кликает «Разрешить» → редирект на callback?code=...&state=...

# 4. Обмен кода на токены
tok = requests.post('https://hubris.pw/oauth/token', data={
    'grant_type': 'authorization_code',
    'code': received_code,
    'redirect_uri': 'http://localhost:8765/callback',
    'client_id': client_id,
    'code_verifier': verifier,
}).json()

access_token = tok['access_token']

# 5. Используем
resp = requests.post(
    'https://api.hubris.pw/v1/chat/completions',
    headers={'Authorization': f'Bearer {access_token}'},
    json={
        'model': 'openai/gpt-4o-mini',
        'messages': [{'role': 'user', 'content': 'привет'}],
    },
)

Дневной лимит

Приложение может передать daily_limit_kopecks=50000 в query к /oauth/authorize. Юзер видит запрошенную сумму на consent-странице и может снизить перед подтверждением. Если приложение не передало — default 500 ₽/день.

После выдачи токена лимит работает идентично api_keys daily-limit'у — 429 при превышении с метаданными в /usage.

Безопасность

Hubris следует RFC 9700 BCP:

  • PKCE S256 обязательноplain метод не поддерживается.
  • Refresh rotation + reuse detection — обнаруженный replay старого refresh после grace-окна revoke'ит всю семью токенов.
  • redirect_uri exact-match после нормализации (lowercase scheme/host, case-sensitive path) — без prefix-wildcards.
  • Reserved-word check на client_name (hubris, official, admin, support запрещены как substring) — anti-phishing baseline.
  • DCR rate-limit: 5 регистраций/IP/час + 100/день глобально.
  • Все токены — sha256-хэши в БД. Plain text никогда не хранится.

MCP integration

Совместимость с native «Add custom connector» UI в Claude Desktop / Claude.ai:

  1. В клиенте указываешь URL: https://api.hubris.pw/mcp
  2. Клиент сам идёт по discovery → DCR → /authorize (открывает браузер)
  3. Юзер видит consent: «Claude Desktop запрашивает доступ chat:write, models:read, balance:read»
  4. После «Разрешить» — обратно в клиент, токены выпущены, инструменты подключены

Legacy sk-gw- ключи продолжают работать на /mcp без изменений (back-compat).

Phase B (deferred)

  • /profile/connected-apps UI — список авторизованных приложений + revoke
  • Per-scope deny (сейчас all-or-nothing на consent-странице)
  • Step-up auth (re-OTP при выдаче sensitive scopes если auth >7д назад)
  • Domain verification для verified-badge при DCR
  • embeddings:write, usage:read scopes
  • OAuth для /v1/embeddings + /v1/usage routes

Ссылки

OAuth 2.0 + PKCE · Hubris