ThunderPhone 2.0을 출시했습니다.셀프서비스로, 분당 2¢부터.출시 소식 보기

Connect tools & data

OAuth로 연결

API 키를 공유하지 않고 MCP 클라이언트와 ThunderPhone CLI를 인증합니다.

OAuth를 사용하면 앱이 사용자가 승인한 권한으로 하나의 ThunderPhone 조직에 연결할 수 있습니다. 디렉터리 클라이언트는 기본적으로 OAuth를 사용해야 합니다. 수동으로 구성한 Bearer 토큰이 필요한 클라이언트에서는 조직 API 키를 계속 사용할 수 있습니다.

연결 승인

MCP 클라이언트에서 연결을 시작합니다. ThunderPhone에 로그인하고, 앱 이름과 요청된 권한을 확인한 다음 조직을 선택하고 승인을 선택합니다. 연결을 시작하지 않았거나 앱을 신뢰하지 않는 경우 거부를 선택합니다. 앱 이름은 개발자가 제공하며 검증 배지가 아닙니다.

읽기 권한은 명시된 데이터를 노출하며, calls:read가 요청된 경우 녹음 및 트랜스크립트도 포함됩니다. 쓰기 권한은 리소스를 변경하거나 삭제할 수 있습니다. 통화, 캠페인, 전화번호 구매 및 배포는 비용을 발생시키거나 프로덕션에 영향을 줄 수 있습니다. 조직 역할은 계속 적용됩니다.

CLI 연결의 경우 터미널에 표시된 확인 링크를 열고, 8자 코드를 비교한 다음 조직을 선택하고 승인합니다. 링크를 여는 것만으로는 접근 권한이 부여되지 않습니다. 코드는 15분 후 만료됩니다.

앱 연결 해제

대시보드에서 조직 → API 키 → 승인된 앱을 열고 철회를 선택합니다. 그러면 접근 토큰과 새로 고침 토큰을 포함하여 현재 조직에 대해 선택한 승인이 철회됩니다. 다시 승인하려면 앱에서 다시 연결합니다.

MCP 클라이언트용 검색

서버 URL https://api.thunderphone.com/v1/mcp을 사용합니다. 유효한 인증이 없는 요청은 다음과 함께 401을 반환합니다.

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)는 지원되지 않습니다.

공개 클라이언트 등록

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를 확인합니다. 거부 및 프로토콜 오류를 포함한 모든 인가 응답은 검색된 issuer와 정확히 일치하는 iss로 발급자를 식별합니다. 잘못된 클라이언트 또는 콜백에 대한 오류는 해당 콜백으로 리디렉션하지 않고 로컬에서 반환됩니다.

폼 인코딩을 사용하여 POST /v1/oauth/token에서 교환합니다(JSON도 허용됨).

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)를 호출합니다. 성공적인 응답에는 다음이 포함됩니다.

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

sub는 안정적인 사용자 식별자이고, org_id는 동의 중에 선택된 조직입니다. 이 엔드포인트는 두 ID 범위를 모두 요구하며 하나라도 없으면 403을 반환합니다. 검증되지 않은 이메일이 신뢰된다고 주장하는 대신, 계정에 검증된 이메일 프로필이 없으면 error=access_denied와 함께 403을 반환합니다. 유효하지 않거나 만료되었거나 폐기되었거나 대상이 잘못된 토큰은 401을 반환합니다. 사람 세션 토큰과 조직 API 키는 userinfo를 호출할 수 없습니다. ID 토큰은 발급되지 않습니다.

사용 가능한 권한

범주범위
에이전트 및 에이전트 가져오기agents:read, agents:write
통화calls:read, calls:write
전화번호, 차단된 발신자 및 VoIPnumbers:read, numbers:write
지식knowledge:read, knowledge:write
캠페인campaigns:read, campaigns:write
통합, 웹훅 엔드포인트, 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의 모든 도구에는 해당 도구의 REST 작업 스코프와 함께 securitySchemes: [{"type": "oauth2", "scopes": ["agents:read"]}]가 포함됩니다. 공개 문서 도구는 빈 스코프 목록을 사용하며 여전히 인증된 연결이 필요합니다.

도구에 필요한 스코프가 없는 유효한 토큰은 isError: true를 포함하는 JSON-RPC result, content의 설명 텍스트 및 _meta["mcp/www_authenticate"]와 함께 HTTP 200을 받습니다. 후자는 resource_metadata, error="insufficient_scope", error_description 및 필요한 scope가 포함된 Bearer 챌린지를 담은 배열입니다. 도구는 실행되지 않습니다. 이 챌린지를 사용하여 확장된 동의를 요청합니다. 누락되었거나 유효하지 않은 인증은 계속해서 WWW-Authenticate와 함께 HTTP 401을 반환하며, REST 스코프 실패는 계속해서 HTTP 403을 반환합니다.

자격 증명 보존

API는 스테이징 및 프로덕션에서 매시간 python manage.py oauth_cleanup를 실행합니다. 이 작업은 만료된 인증 요청과 디바이스 코드를 제거합니다. 만료된 액세스 토큰, 인증 코드 및 리프레시 토큰 해시는 해당 권한 부여가 취소되거나 전체 토큰 계열이 만료된 후에만 정리됩니다. 사용된 해시는 토큰 계열에 사용할 수 있는 리프레시 토큰, 인증 코드, 승인된 디바이스 코드 또는 액세스 토큰이 있는 동안 유지되므로 정리 작업으로 재생 공격 감지를 비활성화할 수 없습니다.