DOCS

OAuth 2.0 authentication

Authenticate your backend services with Zonos using asymmetric key cryptography — no shared secrets.

Zonos supports machine-to-machine authentication via OAuth 2.0 JWT Bearer Token Grant (RFC 7523). Your service signs a short-lived JWT with your RSA private key; Zonos verifies it using your registered public key and returns a Bearer token scoped to your organization.

Flow summary:

  1. Generate an RSA key pair and register your public key with Zonos.
  2. At runtime, sign a JWT assertion with your private key and POST it to the token endpoint.
  3. Zonos returns a short-lived access token.
  4. Include the access token as Authorization: Bearer <token> on every API request.

Token lifecycle and caching 

Access tokens expire in 5 minutes by default. Cache the token and refresh proactively — do not request a new token on every API call. Each refresh requires a newly signed JWT assertion.

1import time, requests
2 
3_cache = {"access_token": None, "expires_at": 0}
4 
5def get_access_token():
6 if time.time() < _cache["expires_at"] - 30:
7 return _cache["access_token"]
8 
9 assertion = build_jwt_assertion()
10 data = requests.post(
11 "https://auth.zonos.com/oauth/token",
12 json={
13 "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
14 "assertion": assertion,
15 },
16 ).json()
17 
18 _cache["access_token"] = data["access_token"]
19 _cache["expires_at"] = time.time() + data["expires_in"]
20 return _cache["access_token"]

Error reference 

All errors follow the OAuth 2.0 error response format (RFC 6749 §5.2):

1{
2 "error": "invalid_grant",
3 "error_description": "JWT assertion has expired"
4}
HTTP StatuserrorCause
400unsupported_grant_typegrant_type was not urn:ietf:params:oauth:grant-type:jwt-bearer
400invalid_requestMissing or malformed field
401invalid_grantInvalid signature, expired assertion, unknown org, or unregistered key
500server_errorInternal error — contact Zonos support if persistent

Common invalid_grant causes:

  • exp is in the past — ensure your system clock is NTP-synchronized
  • aud is not exactly "zonos-auth"
  • iss does not match your registered Organization ID
  • Public key was rotated but not yet updated with Zonos

Security best practices 

  • Protect your private key. Store it in a dedicated secrets manager — never in source control, environment variables, or logs.
  • Keep assertions short-lived. 60–300 seconds is standard; there is no reason to issue longer ones.
  • Include jti. A unique value per assertion enables server-side replay detection.
  • Rotate key pairs periodically. Register a new public key with Zonos before revoking the old one to avoid downtime.
  • Never log access_token or assertion values. Treat both as credentials.
Book a demo

Was this page helpful?


Get support·Legal docs·© 2026 Zonos