Api.Airforce
OAUTH-ПРОВАЙДЕР

Войти через Airforce

Позволь своим пользователям авторизовать твоё приложение для доступа к их аккаунту Api.Airforce. Стандартный OAuth 2.0 Authorization Code flow с PKCE.

Для кого это

Ты строишь приложение (playground, агент, мобильный клиент, …) и хочешь, чтобы твои пользователи подключили свой аккаунт Airforce, чтобы они могли запускать чат или генерацию изображений через твой интерфейс, не делясь с тобой сырым API-ключом. Используй этот flow.

Ты получишь непрозрачный access_token (24h TTL), ограниченный тем, что запросило твоё приложение и одобрил пользователь. Используй его как Bearer-заголовок к /v1/* -эндпоинтам или /oauth/userinfo.

1. Получить client_id + client_secret

Phase 1 — это регистрация, управляемая админом. Напиши нам с:

  • Название приложения (показывается пользователям на экране согласия)
  • Описание одним предложением
  • URL домашней страницы (опционально, показывается на согласии)
  • URL квадратного логотипа (опционально)
  • Один или несколько точных redirect_uri (только https://, кроме http://localhost для dev)
  • Какие scope нужны (см. ниже)

Мы пришлём тебе обратно client_id и одноразовый client_secret. Храни secret на стороне сервера — если ты чистая SPA / нативное приложение без бэкенда, можешь пропустить secret и полагаться только на PKCE.

2. Scope'ы

ScopeЧто он даёт
profileЧитает объект user через /oauth/userinfo (id, username, plan, привязанные email при их верификации).
chatPOST /v1/chat/completions, /v1/messages, /v1/messages/count_tokens, /v1/responses.
imagesPOST /v1/images/generations.
keys:readЗарезервировано — выводит API-ключи Airforce пользователя (Phase 2).
keys:write (ЧУВСТВИТЕЛЬНЫЙ)Зарезервировано — создаёт + отзывает API-ключи Airforce пользователя (Phase 2). Чувствительный scope.

Несколько scope разделяются пробелами в параметре запроса scope : scope=profile chat images.

3. Flow

3.1 Сгенерировать пару PKCE

В клиенте, перед редиректом на /oauth/authorize, сгенерируй случайный verifier и соответствующий SHA-256 challenge.

// In your app, before redirecting to /oauth/authorize:
const verifier = randomString(64); // 43..128 chars [A-Z][a-z][0-9]-._~
const challenge = base64UrlNoPad(sha256(verifier));
sessionStorage.setItem('airforce_pkce_verifier', verifier);

3.2 Редирект на /oauth/authorize

Построй URL авторизации и отправь туда пользователя. Он увидит наш экран согласия, решит, и его редиректнет обратно к тебе.

https://api.airforce/oauth/authorize?
  response_type=code
  &client_id=airforce_xxxxxxxxxxxxxxxxxxxxxxxxx
  &redirect_uri=https://your.app/oauth/callback
  &scope=profile chat
  &state=<random opaque>
  &code_challenge=<base64url(sha256(verifier))>
  &code_challenge_method=S256

Пользователь попадает на наш экран согласия, входит (если ещё не вошёл) и одобряет или отклоняет. Мы редиректим обратно на твой redirect_uri с короткоживущим ?code=… при одобрении, ?error=access_denied при отказе. Твой исходный state возвращается — проверь, что он совпадает.

3.3 Обменять code на access_token

С твоего бэкенда обменяй code (плюс PKCE verifier) на access token.

curl -X POST https://api.airforce/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=$CODE" \
  --data-urlencode "redirect_uri=https://your.app/oauth/callback" \
  --data-urlencode "code_verifier=$VERIFIER"

Ответ (24h TTL):

{
  "access_token": "airf_oat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "token_type": "Bearer",
  "expires_in": 86400,
  "scope": "profile chat"
}

3.4 Использовать токен

Вызови эндпоинт userinfo или любой маршрут /v1/*, разрешённый предоставленными scope:

# Profile lookup
curl https://api.airforce/oauth/userinfo \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# Chat (requires scope=chat)
curl https://api.airforce/v1/chat/completions \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hi"}]}'

Ответ /oauth/userinfo

{
  "id": "user-uuid",
  "username": "foo",
  "plan": "free",
  "is_admin": false,
  "email": "[email protected]",
  "email_verified": true,
  "github_email": "[email protected]",
  "discord_username": "foo#1234"
}

Поля вроде email, github_email, discord_username появляются только когда пользователь действительно их верифицировал / привязал. Планируй на присутствие и отсутствие.

Отзыв токена

Когда пользователь выходит из твоего приложения, отзови токен, чтобы он не продолжал тарифицировать его аккаунт в фоне:

curl -X POST https://api.airforce/oauth/revoke \
  --data-urlencode "token=$ACCESS_TOKEN"

RFC 7009. Всегда возвращает 200 независимо от того, существовал ли токен (без oracle).

Заметки по безопасности

  • PKCE обязателен для публичных клиентов (без серверного client_secret). Принимаем только S256 — code_challenge_method=plain отклоняется.
  • redirect_uri — точное совпадение. Никакого префикс / wildcard сопоставления. Если нужны разные окружения, зарегистрируй по одному URI на окружение.
  • У токенов 24h TTL и refresh-токенов пока нет — при истечении прогоняй пользователя через /oauth/authorize ещё раз. Для долгоживущих агентов спрашивай при старте и храни токен в защищённом хранилище.
  • Не храни client_secret в клиентском коде. Если твоё приложение — чисто браузерная SPA или нативный клиент, регистрируйся без сервера и полагайся на PKCE.
  • Пользователи могут отозвать твоё приложение в любой момент из dashboard → вкладка Apps. Обрабатывай 401, перезапуская OAuth-flow.
  • Покрытие Phase 1: OAuth bearer-токены работают на /v1/chat/completions, /v1/messages, /v1/messages/count_tokens, /v1/responses, /v1/images/generations, и /oauth/userinfo. Эндпоинты audio, video, voice и character в Phase 1 всё ещё требуют обычного API-ключа Airforce.

Зарегистрироваться

Email на [email protected] или напиши нам на Discord с регистрационными данными, перечисленными выше. Мы пришлём тебе credentials в течение нескольких часов.

Не получается настроить или нужен бесплатный ключ? Спросите сообщество в нашем Discord.

Присоединяйтесь к нашему Discord