---
title: "使用 OAuth 連線"
description: "授權 MCP 用戶端與 ThunderPhone CLI，無須分享 API 金鑰。"
---

OAuth 可讓應用程式以你核准的權限連線至一個 ThunderPhone 組織。目錄用戶端預設應使用 OAuth。對於需要手動設定 Bearer 權杖的用戶端，仍可使用組織 API 金鑰。

## 核准連線

在你的 MCP 用戶端中啟動連線。登入 ThunderPhone，確認應用程式名稱與請求的權限，選擇組織，然後選取 **核准**。如果不是你啟動的連線，或你不信任該應用程式，請選取 **拒絕**。應用程式名稱由其開發者提供，並非驗證標章。

讀取權限可存取指定資料；當請求 `calls:read` 時，也包括錄音與逐字稿。寫入權限可變更或刪除資源；通話、行銷活動、購買電話號碼與部署可能產生成本或影響正式環境。你的組織角色權限仍然適用。

如要透過 CLI 連線，請開啟終端機顯示的驗證連結，比對八字元代碼，選擇你的組織並核准。僅開啟連結不會授予存取權。代碼會在 15 分鐘後失效。

## 中斷應用程式連線

在控制台中開啟 **組織 → API 金鑰 → 已授權的應用程式**，然後選擇 **撤銷**。這會撤銷你為目前組織選取的授權，包括其存取權杖與重新整理權杖。若要再次授權，請從該應用程式重新連線。

## MCP 用戶端探索

使用伺服器 URL `https://api.thunderphone.com/v1/mcp`。未提供有效驗證的請求會收到 `401`，內容如下：

```http
WWW-Authenticate: Bearer resource_metadata="https://api.thunderphone.com/.well-known/oauth-protected-resource"
```

擷取該文件，然後擷取位於 `https://api.thunderphone.com/.well-known/oauth-authorization-server` 的授權伺服器中繼資料。資源中繼資料也可從 `/.well-known/oauth-protected-resource/v1/mcp` 取得。請使用傳回的端點，而非自行組合端點。資源識別碼為 `https://api.thunderphone.com/v1/mcp`。

伺服器支援搭配 **S256 PKCE** 的授權碼流程、輪替更新權杖、公開動態用戶端註冊、撤銷，以及裝置授權授與。沒有用戶端密鑰或隱含授與。OpenID 探索也可從 `/.well-known/openid-configuration` 取得；其中包含相同的授權伺服器欄位，以及 `subject_types_supported: ["public"]` 和 userinfo 端點。不支援 ID 權杖與用戶端 ID 中繼資料文件（CIMD）。

### 註冊公開用戶端

將 JSON 傳送至 `POST /v1/oauth/register`：

```json
{
  "client_name": "My MCP client",
  "redirect_uris": ["http://127.0.0.1:8765/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
```

儲存傳回的 `client_id`。重新導向必須使用 HTTPS，或在 `127.0.0.1` 或 `localhost` 上使用 HTTP。註冊精確的回呼 URI，包括其連接埠與路徑。不接受片段與內嵌認證資訊。選用的 `client_uri` 和 `logo_uri` 必須使用 HTTPS；ThunderPhone 不會在註冊期間擷取它們。註冊設有速率限制。已註冊的用戶端不會到期，且連線撤銷後仍會保持註冊狀態。HTTPS 回呼包括 `https://chatgpt.com/connector/oauth/<id>` 與 `https://chatgpt.com/connector_platform_oauth_redirect`。每個連線都可以註冊自己的用戶端。

### 授權碼

開啟探索到的授權端點，並帶入 `client_id`、精確註冊的 `redirect_uri`、`response_type=code`、隨機的 `state`、`scope`、`code_challenge`、`code_challenge_method=S256` 與 `resource=https://api.thunderphone.com/v1/mcp`。將新的高熵 PKCE 驗證碼計算為未補齊的 base64url SHA-256 摘要，以取得挑戰值。交換授權碼前，請檢查傳回的 `state` 與 `iss`。每個授權回應都會以 `iss` 識別發行者，包括拒絕與通訊協定錯誤，且必須與探索到的 `issuer` 完全相符。無效用戶端或回呼的錯誤會在本機傳回，不會重新導向至該回呼。

使用表單編碼在 `POST /v1/oauth/token` 交換權杖（也接受 JSON）：

```text
grant_type=authorization_code
client_id=<your client id>
code=<single-use authorization code>
redirect_uri=<exact registered redirect URI>
code_verifier=<original PKCE verifier>
resource=https://api.thunderphone.com/v1/mcp
```

選用的 `resource` 參數可用於授權與權杖請求（包括更新與裝置交換）。省略時，會預設為探索到的 MCP 資源。提供時，必須與該資源完全相符；其他值會傳回 `invalid_target`。存取權杖會帶有該目標對象，而 MCP 會以 `401` 和探索挑戰拒絕缺少目標對象或目標對象不同的權杖。

授權請求與授權碼會在 10 分鐘後到期。每個權杖回應都包含 `access_token`、`token_type`（`Bearer`）、`expires_in`（預設為 3600 秒）、`refresh_token`、`scope`、`organization_id` 與 `organization_name`。僅透過 `Authorization: Bearer` 標頭傳送存取權杖。切勿將權杖放入 URL、日誌、原始碼控制或聊天內容中。

### 更新與撤銷

在權杖端點中使用 `grant_type=refresh_token`、`client_id`、`refresh_token` 與 `resource` 進行更新。以原子方式儲存新的更新權杖，並停止使用舊權杖。更新權杖若 30 天未成功更新即會到期。選用的 `scope` 可縮小已授與的權限。`offline_access` 一律會包含在內，且一律會簽發更新權杖。未帶有 `scope` 的初始請求只會授與 `offline_access`，因此用戶端應請求所需權限。

重複使用授權碼或已輪替的更新權杖，會撤銷整個授權。請在單一用戶端內序列化更新作業；重播已成功交換的認證資訊不是安全的重試策略。

若要中斷連線，請將 `token` 與 `client_id` 傳送至 `POST /v1/oauth/revoke`。撤銷任一權杖都會撤銷其授權，包括由其簽發的所有權杖。未知權杖會傳回成功，但不會透露其是否存在。控制台工作階段可透過 `GET /v1/oauth/grants` 列出自己的授權，並透過 `DELETE /v1/oauth/grants/<id>` 撤銷其中一項，使用 `X-ThunderPhone-Org` 選取組織。

### 裝置授權

裝置授權僅限預先註冊的用戶端；動態註冊的用戶端會收到 `unauthorized_client`。預先註冊的公開用戶端 `thunderphone-cli` 支援裝置授權與更新。將 `client_id=thunderphone-cli` 與 `scope` 傳送至 `POST /v1/oauth/device/code`。向使用者顯示 `user_code` 與 `verification_uri`，或開啟 `verification_uri_complete`。

使用 `grant_type=urn:ietf:params:oauth:grant-type:device_code`、`client_id` 與 `device_code` 輪詢權杖端點，且至少等待傳回的 `interval`（5 秒）。收到 `authorization_pending` 時繼續輪詢。收到 `slow_down` 時，後續每個請求都應使用回應中傳回的新 `interval`（增加 5 秒）。收到 `access_denied`、`expired_token` 或任何其他錯誤時停止。切勿自動核准裝置代碼。

預先註冊的 `thunderphone-mcp` 用戶端接受任何連接埠上的 `http://127.0.0.1/callback` 與 `http://localhost/callback`。通訊協定、主機、路徑與查詢必須相符；權杖交換必須使用授權時的精確重新導向 URI（包括連接埠）。動態註冊的用戶端需要精確比對重新導向 URI，包括連接埠。如需其他路徑，請動態註冊不同的回呼。

### 帳戶身分與工作區網域檢查

請將 `openid email` 與你的用戶端所需操作範圍一併請求。使用 Bearer 標頭中的存取權杖呼叫探索到的 `userinfo_endpoint`（`GET /v1/oauth/userinfo`）。成功回應包含：

```json
{
  "sub": "123",
  "email": "person@example.com",
  "email_verified": true,
  "name": "Example User",
  "org_id": 456
}
```

`sub` 是穩定的使用者識別碼；`org_id` 是同意授權期間所選取的組織。此端點需要兩個身分範圍，任一缺少時會傳回 `403`。若帳戶沒有已驗證的電子郵件設定檔，會傳回帶有 `error=access_denied` 的 `403`，而非宣稱未驗證的電子郵件可信任。無效、已到期、已撤銷或目標對象錯誤的權杖會傳回 `401`。人類工作階段權杖與組織 API 金鑰無法呼叫 userinfo。不會簽發 ID 權杖。

## 可用權限

| 類別 | 範圍 |
| --- | --- |
| 智慧體與智慧體匯入 | `agents:read`, `agents:write` |
| 通話 | `calls:read`, `calls:write` |
| 電話號碼、封鎖的來電者與 VoIP | `numbers:read`, `numbers:write` |
| 知識 | `knowledge:read`, `knowledge:write` |
| 活動 | `campaigns:read`, `campaigns:write` |
| 整合、Webhook 端點、MCP 伺服器 | `integrations:read`, `integrations:write` |
| 語音 | `voices:read` |
| 測試情境、測試執行、驗證 | `testing:read`, `testing:write` |
| 帳務 | `billing:read` |
| 帳戶身分與已驗證的電子郵件 | `openid`、`email`（兩者皆為 userinfo 所需） |
| 持續連線 | `offline_access`（一律包含） |

GET、HEAD 與 OPTIONS 使用讀取範圍；其他方法使用寫入範圍。無法透過 OAuth 變更語音與帳務；`POST /v1/voices/preview` 使用 `voices:read`，因為它會預覽語音而不變更其設定。MCP 工具會強制套用其底層 REST 操作的範圍。即使授權使用者同時屬於兩個組織，跨組織移轉仍會遭到拒絕。其他 API 類別（包括 API 金鑰管理與人類帳戶設定）無法透過 OAuth 使用。REST 操作缺少權限時，會傳回含有 `WWW-Authenticate: Bearer error="insufficient_scope", scope="..."` 的 `403`。無效或過期的存取權杖會傳回 `401`。


### MCP 工具驗證訊號

`tools/list` 中的每個工具都包含 `securitySchemes: [{"type": "oauth2", "scopes": ["agents:read"]}]`，其範圍為該工具 REST 操作的範圍。公開文件工具使用空白範圍清單，但仍需要已驗證的連線。

有效權杖若缺少工具所需的範圍，會收到 HTTP `200`，其中 JSON-RPC `result` 包含 `isError: true`、`content` 中的說明文字，以及 `_meta["mcp/www_authenticate"]`。後者是陣列，包含帶有 `resource_metadata`、`error="insufficient_scope"`、`error_description` 與必要 `scope` 的 Bearer 挑戰。工具不會執行。使用此挑戰要求擴充同意授權。缺少或無效的驗證仍會傳回帶有 `WWW-Authenticate` 的 HTTP `401`；REST 範圍失敗仍會傳回 HTTP `403`。

## 憑證保留

API 每小時會在測試環境與正式環境執行 `python manage.py oauth_cleanup`。它會移除過期的授權要求與裝置代碼。過期的存取權杖、授權代碼及重新整理權杖雜湊，只有在其授權已撤銷或整個家族已失效後才會清除。只要該家族仍有可用的重新整理權杖、授權代碼、已核准的裝置代碼或存取權杖，已使用的雜湊就會保留，因此清理作業不會停用重播偵測。
