Api.Airforce
OAUTH 提供方

使用 Airforce 登入

讓你的使用者授權你的應用程式存取他們的 Api.Airforce 帳號。帶 PKCE 的標準 OAuth 2.0 Authorization Code 流程。

適用對象

你正在打造一個應用程式(playground、代理、行動裝置客戶端、…),希望你的使用者接上他們的 Airforce 帳號,以便透過你的 UI 執行聊天或圖像生成,而不必把原始 API 金鑰分享給你。請使用此流程。

你會得到一個不透明的 access_token (24h TTL),其作用域限定為你的應用程式請求且使用者批准的範圍。將其作為 Bearer 標頭用於 /v1/* 端點或 /oauth/userinfo.

1. 取得 client_id + client_secret

Phase 1 是管理員管理的註冊。請聯絡我們並提供:

  • 應用程式名稱(在同意畫面上展示給你的使用者)
  • 一句話描述
  • 首頁 URL(選填,會顯示在同意畫面)
  • 方形 logo URL(選填)
  • 一個或多個精確的 redirect_uri(僅 https://,除開發用 http://localhost)
  • 你需要哪些 scope(見下文)

我們會回傳給你 client_id 以及一個一次性的 client_secret. 把 secret 存放在伺服器端 — 如果你是純 SPA / 原生應用程式且沒有後端,可以略過 secret,僅依賴 PKCE。

2. Scope

Scope授予的權限
profile透過 /oauth/userinfo 讀取 user 物件(id、username、plan、已驗證時的關聯信箱)。
chatPOST /v1/chat/completions, /v1/messages, /v1/messages/count_tokens, /v1/responses.
imagesPOST /v1/images/generations.
keys:read保留 — 列出使用者的 Airforce API 金鑰(Phase 2)。
keys:write (敏感)保留 — 建立 + 撤銷使用者的 Airforce API 金鑰(Phase 2)。敏感 scope。

多個 scope 在 scope 查詢參數中以空格分隔: scope=profile chat images.

3. 流程

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

建構 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 使用 token

呼叫 userinfo 端點或所授予 scope 允許的任何 /v1/* 路由:

# 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 之類的欄位,只有在使用者確實已驗證 / 關聯時才會出現。請同時考量存在與不存在的情況。

撤銷 token

當使用者從你的應用程式登出時,撤銷 token 以避免它在背景持續扣款:

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

RFC 7009。無論 token 是否存在,永遠回傳 200 (無 oracle)。

安全注意事項

  • PKCE 對公開客戶端是強制的 (沒有伺服器端 client_secret)。我們僅接受 S256 — code_challenge_method=plain 會被拒絕。
  • redirect_uri 為精確比對。 沒有前綴 / 萬用字元比對。如果你需要多個環境,請為每個環境註冊一個 URI。
  • Token 擁有 24h TTL 且暫時沒有 refresh token — 過期時讓使用者重新走一遍 /oauth/authorize 。對於長期執行的代理,啟動時提示並把 token 存到安全儲存。
  • 不要把 client_secret 存放在客戶端程式碼中。 如果你的應用程式是純瀏覽器 SPA 或原生客戶端,請在無伺服器的情況下註冊並依賴 PKCE。
  • 使用者可隨時從他們的 dashboard → Apps 分頁撤銷你的應用程式。透過重新執行 OAuth 流程來處理 401。
  • Phase 1 涵蓋範圍: OAuth bearer token 適用於 /v1/chat/completions, /v1/messages, /v1/messages/count_tokens, /v1/responses, /v1/images/generations, 和 /oauth/userinfo. 音訊、影片、語音和 character 端點在 Phase 1 仍需要一般的 Airforce API 金鑰。

註冊

寄信至 [email protected] 或在 Discord 上聯絡我們,並附上上面列出的註冊細節。我們將在數小時內把你的憑證寄回。

設定卡關,或想要免費金鑰?到我們的 Discord 問問社群吧。

加入我們的 Discord