비대칭 키 암호화 방식을 사용해 백엔드 서비스를 Zonos와 인증합니다 — 공유 비밀키가 필요 없습니다.
Zonos는 OAuth 2.0 JWT Bearer 토큰 부여(RFC 7523)를 통한 서버 간(machine-to-machine) 인증을 지원합니다. 서비스가 RSA 개인 키로 단기 유효 JWT에 서명하면, Zonos는 등록된 공개 키로 이를 검증하고 조직 범위로 한정된 Bearer 토큰을 반환합니다.
흐름 요약:
RSA 키 페어를 생성하고 공개 키를 Zonos에 등록합니다.
실행 시점에 개인 키로 JWT assertion에 서명하고 토큰 엔드포인트로 POST합니다.
Zonos가 단기 유효 액세스 토큰을 반환합니다.
모든 API 요청에 액세스 토큰을 Authorization: Bearer <token> 형식으로 포함합니다.
Step 1 — 공개 키 등록(최초 1회 설정)
4096비트 RSA 키 페어를 생성하고 공개 키를 Zonos와 공유합니다. 이 작업은 온보딩 과정에서 한 번만 수행합니다.
OAuth 2.0 인증
OAuth 2.0 인증
비대칭 키 암호화 방식을 사용해 백엔드 서비스를 Zonos와 인증합니다 — 공유 비밀키가 필요 없습니다.
Zonos는 OAuth 2.0 JWT Bearer 토큰 부여(RFC 7523)를 통한 서버 간(machine-to-machine) 인증을 지원합니다. 서비스가 RSA 개인 키로 단기 유효 JWT에 서명하면, Zonos는 등록된 공개 키로 이를 검증하고 조직 범위로 한정된 Bearer 토큰을 반환합니다.
흐름 요약:
Authorization: Bearer <token>형식으로 포함합니다.Step 1 — 공개 키 등록(최초 1회 설정)
4096비트 RSA 키 페어를 생성하고 공개 키를 Zonos와 공유합니다. 이 작업은 온보딩 과정에서 한 번만 수행합니다.
키 페어 생성
# Generate private keyopenssl genrsa -out private_key.pem 4096# Extract public keyopenssl rsa -in private_key.pem -pubout -out public_key.pempublic_key.pem을 Zonos와 공유합니다.private_key.pem은 전용 시크릿 관리 서비스(AWS Secrets Manager, HashiCorp Vault 등)에 저장하고, 소스 코드나 환경 변수에는 절대 두지 마세요.Zonos가 키를 등록하고 조직 ID(Organization ID)를 반환하며, 이 값은 모든 JWT assertion의
iss클레임이 됩니다.Step 2 — JWT assertion 생성
개인 키로
RS256방식의 JWT에 서명합니다. assertion은 단 한 번의 토큰 교환에만 유효하므로, 유효 기간(60~300초)을 짧게 유지하세요.필수 클레임
iss"org_abc123")sub"checkout-service")aud"zonos-auth"여야 함expiat로부터 60~300초 후iatjtiJWT header에는
"alg": "RS256"과"typ": "JWT"를 지정해야 합니다.코드 예제
import jwt, uuid, timewith open("private_key.pem") as f:private_key = f.read()now = int(time.time())assertion = jwt.encode({"iss": "org_abc123","sub": "checkout-service","aud": "zonos-auth","iat": now,"exp": now + 300,"jti": str(uuid.uuid4()),},private_key,algorithm="RS256",)Step 3 — Assertion을 액세스 토큰으로 교환
서명된 JWT를 Zonos 토큰 엔드포인트로 전송해 단기 유효 Bearer 토큰을 받습니다.
엔드포인트
application/x-www-form-urlencoded형식도 허용됩니다.요청 필드
grant_type"urn:ietf:params:oauth:grant-type:jwt-bearer"assertion요청 및 응답
{"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer","assertion": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."}access_tokentoken_type"Bearer"expires_inscope전체 코드 예제
import requestsresponse = requests.post("https://auth.zonos.com/oauth/token",json={: ,: assertion,},)data = response.json()access_token = data[]expires_in = data[]Step 4 — 액세스 토큰으로 Zonos API 호출
모든 Zonos API 요청의
Authorizationheader에 액세스 토큰을Bearer토큰 형식으로 포함합니다.요청 예제
curl -X POST https://api.zonos.com/graphql \-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \-H "Content-Type: application/json" \-d '{ "query": "{ ... }" }'토큰 수명 및 캐싱
액세스 토큰은 기본적으로 5분 후 만료됩니다. 토큰을 캐시하고 선제적으로 갱신하세요 — API를 호출할 때마다 새 토큰을 요청하지 마세요. 갱신할 때마다 새로 서명된 JWT assertion이 필요합니다.
import time, requests_cache = {"access_token": None, "expires_at": 0}def get_access_token():if time.time() < _cache["expires_at"] - 30:return _cache["access_token"]assertion = build_jwt_assertion()data = requests.post("https://auth.zonos.com/oauth/token",json={"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer","assertion": assertion,},).json()_cache["access_token"] = data["access_token"]_cache["expires_at"] = time.time() + data["expires_in"]return _cache["access_token"]오류 참조
모든 오류는 OAuth 2.0 오류 응답 형식(RFC 6749 §5.2)을 따릅니다:
{"error": "invalid_grant","error_description": "JWT assertion has expired"}error↕400unsupported_grant_typegrant_type이urn:ietf:params:oauth:grant-type:jwt-bearer가 아님400invalid_request401invalid_grant500server_error일반적인
invalid_grant발생 원인:exp가 과거 시점임 — 시스템 시계가 NTP로 동기화되어 있는지 확인하세요aud가 정확히"zonos-auth"가 아님iss가 등록된 조직 ID와 일치하지 않음보안 모범 사례
jti를 포함하세요. assertion마다 고유한 값을 사용하면 서버 측에서 재사용 공격을 탐지할 수 있습니다.access_token이나assertion값을 로그에 남기지 마세요. 둘 다 자격 증명(credential)으로 취급하세요.이 페이지가 도움이 되었나요?