---
title: "Hubungkan dengan OAuth"
description: "Otorisasi klien MCP dan CLI ThunderPhone tanpa membagikan kunci API."
---

OAuth memungkinkan aplikasi terhubung ke satu organisasi ThunderPhone dengan izin yang Anda setujui. Klien direktori sebaiknya menggunakan OAuth secara default. Kunci API organisasi tetap tersedia untuk klien yang memerlukan token Bearer yang dikonfigurasi secara manual.

## Setujui koneksi

Mulai koneksi di klien MCP Anda. Masuk ke ThunderPhone, periksa nama aplikasi dan izin yang diminta, pilih organisasi, lalu pilih **Setujui**. Pilih **Tolak** jika Anda tidak memulai koneksi atau tidak memercayai aplikasi tersebut. Nama aplikasi diberikan oleh pengembangnya dan bukan lencana verifikasi.

Izin baca mengekspos data yang disebutkan, termasuk rekaman dan transkrip saat `calls:read` diminta. Izin tulis dapat mengubah atau menghapus sumber daya; panggilan, kampanye, pembelian nomor telepon, dan deployment dapat menimbulkan biaya atau memengaruhi produksi. Peran organisasi Anda tetap berlaku.

Untuk koneksi CLI, buka tautan verifikasi yang ditampilkan di terminal Anda, bandingkan kode delapan karakter, pilih organisasi Anda, lalu setujui. Hanya membuka tautan tidak memberikan akses. Kode kedaluwarsa setelah 15 menit.

## Putuskan koneksi aplikasi

Buka **Organisasi → Kunci API → Aplikasi terotorisasi** di dashboard dan pilih **Cabut**. Tindakan ini mencabut otorisasi yang Anda pilih untuk organisasi saat ini, termasuk token akses dan token refresh-nya. Hubungkan kembali dari aplikasi jika Anda ingin mengotorisasinya lagi.

## Penemuan untuk klien MCP

Gunakan URL server `https://api.thunderphone.com/v1/mcp`. Permintaan tanpa autentikasi yang valid menerima `401` dengan:

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

Ambil dokumen tersebut, lalu ambil metadata server otorisasi di `https://api.thunderphone.com/.well-known/oauth-authorization-server`. Metadata resource juga tersedia di `/.well-known/oauth-protected-resource/v1/mcp`. Gunakan endpoint yang dikembalikan alih-alih menyusunnya sendiri. Pengidentifikasi resource adalah `https://api.thunderphone.com/v1/mcp`.

Server mendukung kode otorisasi dengan **S256 PKCE**, refresh token bergilir, registrasi klien dinamis publik, pencabutan, dan grant otorisasi perangkat. Tidak ada client secret atau implicit grant. Penemuan OpenID juga tersedia di `/.well-known/openid-configuration`; penemuan ini mencakup field server otorisasi yang sama ditambah `subject_types_supported: ["public"]` dan endpoint userinfo. Token ID dan Dokumen Metadata Client ID (CIMD) tidak didukung.

### Daftarkan klien publik

Kirim JSON ke `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"]
}
```

Simpan `client_id` yang dikembalikan. Redirect harus menggunakan HTTPS, atau HTTP pada `127.0.0.1` atau `localhost`. Daftarkan URI callback yang tepat, termasuk port dan jalurnya. Fragmen dan kredensial yang disematkan ditolak. `client_uri` dan `logo_uri` opsional harus menggunakan HTTPS; ThunderPhone tidak mengambilnya selama pendaftaran. Pendaftaran dibatasi laju permintaannya. Klien terdaftar tidak kedaluwarsa dan tetap terdaftar saat koneksi dicabut. Callback HTTPS mencakup `https://chatgpt.com/connector/oauth/<id>` dan `https://chatgpt.com/connector_platform_oauth_redirect`. Setiap koneksi dapat mendaftarkan kliennya sendiri.

### Kode otorisasi

Buka endpoint otorisasi yang ditemukan dengan `client_id`, `redirect_uri` terdaftar yang tepat, `response_type=code`, `state` acak, `scope`, `code_challenge`, `code_challenge_method=S256`, dan `resource=https://api.thunderphone.com/v1/mcp`. Hitung challenge sebagai digest SHA-256 base64url tanpa padding dari verifier PKCE baru dengan entropi tinggi. Periksa `state` dan `iss` yang dikembalikan sebelum menukar kode. Setiap respons otorisasi, termasuk penolakan dan kesalahan protokol, mengidentifikasi issuer dengan `iss`, yang harus sama persis dengan `issuer` yang ditemukan. Kesalahan untuk klien atau callback yang tidak valid dikembalikan secara lokal tanpa mengarahkan ulang ke callback tersebut.

Tukar pada `POST /v1/oauth/token` menggunakan pengodean formulir (JSON juga diterima):

```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
```

Parameter `resource` opsional diterima pada permintaan otorisasi maupun token (termasuk pertukaran refresh dan perangkat). Jika dihilangkan, nilainya secara default adalah resource MCP yang ditemukan. Jika ada, nilainya harus sama persis dengan resource tersebut; nilai lain mengembalikan `invalid_target`. Access token membawa audiens tersebut, dan MCP menolak token dengan audiens yang tidak ada atau berbeda dengan `401` dan challenge penemuan.

Permintaan otorisasi dan kode kedaluwarsa setelah 10 menit. Setiap respons token berisi `access_token`, `token_type` (`Bearer`), `expires_in` (3600 detik secara default), `refresh_token`, `scope`, `organization_id`, dan `organization_name`. Kirim access token hanya melalui header `Authorization: Bearer`. Jangan pernah menaruh token dalam URL, log, source control, atau chat.

### Refresh dan cabut

Lakukan refresh dengan `grant_type=refresh_token`, `client_id`, `refresh_token`, dan `resource` pada endpoint token. Simpan refresh token baru secara atomik dan berhenti menggunakan token lama. Refresh token kedaluwarsa setelah 30 hari tanpa refresh yang berhasil. `scope` opsional dapat mempersempit izin yang diberikan. `offline_access` selalu disertakan dan refresh token selalu diterbitkan. Permintaan awal tanpa `scope` hanya memberikan `offline_access`, sehingga klien harus meminta izin yang diperlukan.

Penggunaan ulang kode dan penggunaan ulang refresh token yang telah digilir mencabut seluruh otorisasi. Serialkan refresh dalam satu klien; memutar ulang kredensial yang berhasil ditukar bukan strategi percobaan ulang yang aman.

Untuk memutuskan koneksi, kirim `token` dan `client_id` ke `POST /v1/oauth/revoke`. Mencabut salah satu token akan mencabut otorisasinya, termasuk semua token yang diterbitkan darinya. Token yang tidak dikenal mengembalikan keberhasilan tanpa mengungkapkan apakah token tersebut ada. Sesi Dasbor dapat mencantumkan otorisasi miliknya sendiri pada `GET /v1/oauth/grants` dan mencabut satu otorisasi pada `DELETE /v1/oauth/grants/<id>`, dengan `X-ThunderPhone-Org` memilih organisasi.

### Otorisasi perangkat

Otorisasi perangkat dibatasi untuk klien yang telah terdaftar sebelumnya; klien yang didaftarkan secara dinamis menerima `unauthorized_client`. Klien publik yang telah terdaftar sebelumnya, `thunderphone-cli`, mendukung otorisasi perangkat dan refresh. Kirim `client_id=thunderphone-cli` dan `scope` ke `POST /v1/oauth/device/code`. Tampilkan `user_code` dan `verification_uri` kepada pengguna, atau buka `verification_uri_complete`.

Poll endpoint token dengan `grant_type=urn:ietf:params:oauth:grant-type:device_code`, `client_id`, dan `device_code`, dengan menunggu setidaknya `interval` yang dikembalikan (5 detik). Lanjutkan pada `authorization_pending`. Pada `slow_down`, gunakan `interval` baru yang dikembalikan dalam respons (bertambah 5 detik) untuk setiap permintaan berikutnya. Berhenti pada `access_denied`, `expired_token`, atau kesalahan lainnya. Jangan pernah menyetujui kode perangkat secara otomatis.

Klien `thunderphone-mcp` yang telah terdaftar sebelumnya menerima `http://127.0.0.1/callback` dan `http://localhost/callback` pada port apa pun. Skema, host, jalur, dan query harus cocok; pertukaran token harus menggunakan URI redirect yang tepat (termasuk port) dari otorisasi. Klien yang didaftarkan secara dinamis memerlukan pencocokan URI redirect yang tepat, termasuk port. Daftarkan callback lain secara dinamis jika Anda memerlukan jalur lain.

### Identitas akun dan pemeriksaan domain ruang kerja

Minta `openid email` bersama scope operasi yang dibutuhkan klien Anda. Panggil `userinfo_endpoint` yang ditemukan (`GET /v1/oauth/userinfo`) dengan access token dalam header Bearer. Respons yang berhasil berisi:

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

`sub` adalah pengidentifikasi pengguna yang stabil; `org_id` adalah organisasi yang dipilih selama persetujuan. Endpoint memerlukan kedua scope identitas dan mengembalikan `403` jika salah satunya tidak ada. Endpoint mengembalikan `403` dengan `error=access_denied` jika akun tidak memiliki profil email terverifikasi, alih-alih menyatakan bahwa email yang belum diverifikasi dapat dipercaya. Token yang tidak valid, kedaluwarsa, dicabut, atau memiliki audiens yang salah mengembalikan `401`. Token sesi manusia dan API key organisasi tidak dapat memanggil userinfo. Tidak ada token ID yang diterbitkan.

## Izin yang tersedia

| Keluarga | Cakupan |
| --- | --- |
| Agen dan impor agen | `agents:read`, `agents:write` |
| Panggilan | `calls:read`, `calls:write` |
| Nomor telepon, penelepon yang diblokir, dan VoIP | `numbers:read`, `numbers:write` |
| Pengetahuan | `knowledge:read`, `knowledge:write` |
| Kampanye | `campaigns:read`, `campaigns:write` |
| Integrasi, endpoint webhook, server MCP | `integrations:read`, `integrations:write` |
| Suara | `voices:read` |
| Skenario pengujian, proses pengujian, validasi | `testing:read`, `testing:write` |
| Penagihan | `billing:read` |
| Identitas akun dan email terverifikasi | `openid`, `email` (keduanya diperlukan untuk userinfo) |
| Koneksi persisten | `offline_access` (selalu disertakan) |

GET, HEAD, dan OPTIONS menggunakan cakupan baca; metode lainnya menggunakan cakupan tulis. Mutasi suara dan penagihan tidak tersedia melalui OAuth; `POST /v1/voices/preview` menggunakan `voices:read` karena melakukan pratinjau suara tanpa mengubah konfigurasinya. Alat MCP menerapkan cakupan operasi REST yang mendasarinya. Transfer lintas organisasi ditolak bahkan ketika pengguna yang memberikan otorisasi tergabung dalam kedua organisasi. Keluarga API lainnya, termasuk pengelolaan API key dan pengaturan akun manusia, tidak tersedia melalui OAuth. Izin yang tidak ada pada operasi REST menghasilkan `403` dengan `WWW-Authenticate: Bearer error="insufficient_scope", scope="..."`. Token akses yang tidak valid atau kedaluwarsa menghasilkan `401`.


### Sinyal autentikasi alat MCP

Setiap alat di `tools/list` menyertakan `securitySchemes: [{"type": "oauth2", "scopes": ["agents:read"]}]`, dengan cakupan operasi REST alat tersebut. Alat dokumentasi publik menggunakan daftar cakupan kosong dan tetap memerlukan koneksi terautentikasi.

Token valid yang tidak memiliki cakupan alat menerima HTTP `200` dengan `result` JSON-RPC yang berisi `isError: true`, teks penjelasan dalam `content`, dan `_meta["mcp/www_authenticate"]`. Yang terakhir adalah array yang berisi tantangan Bearer dengan `resource_metadata`, `error="insufficient_scope"`, `error_description`, dan `scope` yang diperlukan. Alat tidak dijalankan. Gunakan tantangan ini untuk meminta persetujuan yang diperluas. Autentikasi yang tidak ada atau tidak valid tetap menghasilkan HTTP `401` dengan `WWW-Authenticate`; kegagalan cakupan REST tetap menghasilkan HTTP `403`.

## Retensi kredensial

API menjalankan `python manage.py oauth_cleanup` setiap jam di staging dan produksi. Proses ini menghapus permintaan otorisasi dan kode perangkat yang kedaluwarsa. Token akses, kode otorisasi, dan hash token refresh yang kedaluwarsa hanya dipangkas setelah grant-nya dicabut atau seluruh keluarganya tidak lagi aktif. Hash yang telah digunakan dipertahankan selama keluarga tersebut memiliki token refresh, kode otorisasi, kode perangkat yang disetujui, atau token akses yang dapat digunakan, sehingga pembersihan tidak dapat menonaktifkan deteksi pemutaran ulang.
