Загрузка...

OAuth PKCE: API-ключ для вашего приложения

OAuth PKCE позволяет приложению получить API-ключ пользователя без ручного копирования. Пользователь входит в RouterAI, выбирает аккаунт, название ключа и лимит расходов. Приложение получает обычный ключ sk-….

Регистрировать приложение не нужно. Пути и поля совпадают с OpenRouter, поэтому приложению с «Login with OpenRouter» достаточно заменить хост на https://routerai.ru.

Как это работает

  1. Приложение генерирует code_verifier и code_challenge.
  2. Перенаправляет пользователя на https://routerai.ru/auth.
  3. Пользователь подтверждает выдачу ключа.
  4. RouterAI возвращает пользователя на callback_url с параметром code.
  5. Приложение отправляет code и code_verifier на POST https://routerai.ru/api/v1/auth/keys и получает ключ.
  6. Ключ работает как обычный: https://routerai.ru/api/v1, заголовок Authorization: Bearer sk-….

Шаг 1. code_verifier и code_challenge

code_verifier — случайная строка из 43–128 символов [A-Za-z0-9-._~]. code_challenge — BASE64URL(SHA256(code_verifier)) без = на конце. code_verifier храните у себя и не передавайте в URL.

import { randomBytes, createHash } from 'node:crypto';

const codeVerifier = randomBytes(32).toString('base64url');
const codeChallenge = createHash('sha256').update(codeVerifier).digest('base64url');

Шаг 2. Перенаправление на /auth

https://routerai.ru/auth?callback_url=https://example.com/callback&code_challenge=…&code_challenge_method=S256&state=…&app_name=My%20App
Параметр Обязательный Описание
callback_url нет Адрес возврата. Только https, http допускается лишь для localhost/127.0.0.1 (любой порт). Можно указать собственную схему приложения (myapp://callback). Фрагмент # запрещён. Без параметра включается headless-режим.
code_challenge да BASE64URL(SHA256(code_verifier)), 43 символа
code_challenge_method да Только S256. plain не поддерживается
state нет До 256 символов, возвращается без изменений. Защищает от CSRF: сверьте его перед обменом кода
app_name нет Название приложения на экране согласия
key_label нет Название ключа по умолчанию, пользователь может изменить
limit нет Лимит расходов по умолчанию, ₽ (0 — без лимита). Если не задан — 100 ₽
limit_period нет day, week или month. Если не задан — day

Если в запросе ошибка (например, нет code_challenge или указан недопустимый callback_url), RouterAI показывает её на своей странице и не возвращает пользователя в приложение.

Шаг 3. Callback

  • Успех: callback_url?code=…&state=…&iss=https://routerai.ru
  • Отказ: callback_url?error=access_denied&state=…

Сверьте state с сохранённым значением. Код действует 5 минут и используется один раз.

Шаг 4. Обмен кода на ключ

curl -X POST https://routerai.ru/api/v1/auth/keys \
  -H "Content-Type: application/json" \
  -d '{
    "code": "…",
    "code_verifier": "…",
    "code_challenge_method": "S256",
    "callback_url": "https://example.com/callback"
  }'

Ответ:

{ "key": "sk-…" }

callback_url передавайте тот же, что и в /auth, символ в символ. code_challenge_method необязателен, но если он указан, допустимо только S256. Эндпоинт поддерживает CORS, поэтому обмен можно выполнить прямо из браузера.

Ошибки ({"error": "…", "error_description": "…"}):

HTTP error Причина
400 invalid_grant Код не найден, истёк или уже использован; callback_url не совпадает; неверный code_verifier; у пользователя больше нет прав на выбранный аккаунт
400 invalid_request code_challenge_method отличается от S256
429 slow_down Больше 30 запросов в минуту с одного IP

Если уже использованный код предъявить повторно вместе с верным code_verifier, выданный по нему ключ выключается: значит, утекла вся пара. Повтор одного кода без верного code_verifier ключ не трогает.

Headless-режим

Для CLI и приложений без браузерного callback откройте /auth без callback_url. После подтверждения RouterAI покажет код на экране. Пользователь вставляет его в приложение, и приложение обменивает код через POST /api/v1/auth/keys без callback_url. Правила те же: код действует 5 минут и используется один раз.

Повторная авторизация

Если тот же пользователь снова авторизует то же приложение для того же аккаунта, ключ перевыпускается. Старый секрет сразу перестаёт работать, а название и лимит обновляются. Дубликат не создаётся, статистика ключа сохраняется.

Приложение определяется так:

  • веб-приложение — по адресу (https://example.com) из callback_url;
  • localhost и headless — по app_name.

Ключи localhost- и headless-приложений без app_name не перевыпускаются: каждый раз создаётся новый ключ.

Лимиты и управление ключом

Лимит расходов действует как у обычного ключа: при его достижении или нехватке баланса запросы отклоняются. Пользователь видит ключ в Настройках → API-ключи с пометкой «OAuth». Там можно изменить лимит, выключить или удалить ключ.