---
title: "เชื่อมต่อด้วย OAuth"
description: "อนุญาต MCP ไคลเอนต์และ ThunderPhone CLI โดยไม่ต้องแชร์คีย์ API"
---

OAuth ช่วยให้แอปเชื่อมต่อกับองค์กร ThunderPhone หนึ่งองค์กรด้วยสิทธิ์ที่คุณอนุมัติ ไคลเอนต์ไดเรกทอรีควรใช้ OAuth เป็นค่าเริ่มต้น คีย์ API ขององค์กรยังคงพร้อมใช้งานสำหรับไคลเอนต์ที่ต้องใช้โทเค็น Bearer ซึ่งกำหนดค่าด้วยตนเอง

## อนุมัติการเชื่อมต่อ

เริ่มการเชื่อมต่อในไคลเอนต์ 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 token และเอกสาร Client ID Metadata Document (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 หรือ HTTP บน `127.0.0.1` หรือ `localhost` ลงทะเบียน 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` คำนวณแชลเลนจ์เป็นไดเจสต์ SHA-256 แบบ base64url ที่ไม่มีการเติมของตัวตรวจสอบ PKCE ใหม่ที่มีเอนโทรปีสูง ตรวจสอบ `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` พร้อมกับขอบเขตการดำเนินการที่ไคลเอนต์ต้องการ เรียก `userinfo_endpoint` ที่ค้นพบ (`GET /v1/oauth/userinfo`) ด้วยโทเค็นการเข้าถึงในส่วนหัว Bearer การตอบกลับที่สำเร็จประกอบด้วย:

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

`sub` คือตัวระบุผู้ใช้แบบคงที่ ส่วน `org_id` คือองค์กรที่เลือกไว้ระหว่างการให้ความยินยอม ปลายทางนี้ต้องมีขอบเขตข้อมูลประจำตัวทั้งสองรายการ และจะส่งกลับ `403` หากไม่มีรายการใดรายการหนึ่ง จะส่งกลับ `403` พร้อม `error=access_denied` หากบัญชีไม่มีโปรไฟล์อีเมลที่ยืนยันแล้ว แทนที่จะยืนยันว่าอีเมลที่ยังไม่ได้ยืนยันนั้นเชื่อถือได้ โทเค็นที่ไม่ถูกต้อง หมดอายุ ถูกเพิกถอน หรือมีผู้รับไม่ถูกต้องจะส่งกลับ `401` โทเค็นเซสชันของผู้ใช้และคีย์ API ขององค์กรไม่สามารถเรียก userinfo ได้ ไม่มีการออก ID token

## สิทธิ์ที่ใช้ได้

| กลุ่ม | ขอบเขตสิทธิ์ |
| --- | --- |
| เอเจนต์และการนำเข้าเอเจนต์ | `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 key และการตั้งค่าบัญชีของผู้ใช้ ไม่พร้อมใช้งานผ่าน OAuth หากการดำเนินการ REST ไม่มีสิทธิ์ที่จำเป็น ระบบจะส่งคืน `403` พร้อม `WWW-Authenticate: Bearer error="insufficient_scope", scope="..."` โทเค็นการเข้าถึงที่ไม่ถูกต้องหรือหมดอายุจะส่งคืน `401`


### สัญญาณการตรวจสอบสิทธิ์ของเครื่องมือ MCP

ทุกเครื่องมือใน `tools/list` มี `securitySchemes: [{"type": "oauth2", "scopes": ["agents:read"]}]` โดยใช้ขอบเขตสิทธิ์ของการดำเนินการ REST ของเครื่องมือนั้น เครื่องมือเอกสารสาธารณะใช้รายการขอบเขตสิทธิ์ว่างเปล่าและยังคงต้องมีการเชื่อมต่อที่ผ่านการตรวจสอบสิทธิ์

โทเค็นที่ถูกต้องแต่ไม่มีขอบเขตสิทธิ์ของเครื่องมือจะได้รับ HTTP `200` พร้อม JSON-RPC `result` ที่มี `isError: true` ข้อความอธิบายใน `content` และ `_meta["mcp/www_authenticate"]` ส่วนหลังเป็นอาร์เรย์ที่มีคำขอท้าทายแบบ Bearer พร้อม `resource_metadata`, `error="insufficient_scope"`, `error_description` และ `scope` ที่จำเป็น เครื่องมือจะไม่ถูกดำเนินการ ใช้คำขอท้าทายนี้เพื่อขอความยินยอมเพิ่มเติม การตรวจสอบสิทธิ์ที่หายไปหรือไม่ถูกต้องจะยังคงส่งคืน HTTP `401` พร้อม `WWW-Authenticate` ส่วนความล้มเหลวของขอบเขตสิทธิ์ REST จะยังคงส่งคืน HTTP `403`

## การเก็บรักษาข้อมูลรับรอง

API รัน `python manage.py oauth_cleanup` ทุกชั่วโมงใน staging และ production โดยจะลบคำขออนุญาตและรหัสอุปกรณ์ที่หมดอายุ โทเค็นการเข้าถึง รหัสอนุญาต และแฮชของ refresh token ที่หมดอายุจะถูกตัดออกก็ต่อเมื่อการให้สิทธิ์ถูกเพิกถอน หรือตระกูลโทเค็นทั้งหมดใช้งานไม่ได้แล้ว แฮชที่ใช้แล้วจะถูกเก็บไว้ตราบใดที่ตระกูลนั้นยังมี refresh token รหัสอนุญาต รหัสอุปกรณ์ที่อนุมัติแล้ว หรือโทเค็นการเข้าถึงที่ใช้งานได้ เพื่อให้การล้างข้อมูลไม่สามารถปิดใช้การตรวจจับการนำกลับมาใช้ซ้ำได้
