DOCS

OAuth 2.0 身份验证

OAuth 2.0 身份验证

使用非对称密钥加密对您的后端服务进行身份验证 — 无需共享密钥。

Zonos 支持通过 OAuth 2.0 JWT Bearer Token Grant (RFC 7523) 进行机器到机器的身份验证。您的服务使用 RSA 私钥对短期 JWT 进行签名;Zonos 使用您注册的公钥验证它,并返回作用于您的组织的 Bearer 令牌。

流程概述:

  1. 生成 RSA 密钥对并向 Zonos 注册您的 公钥
  2. 在运行时,使用您的 私钥 对 JWT 断言进行签名,然后将其 POST 到令牌端点。
  3. Zonos 返回一个短期 访问令牌
  4. 在每个 API 请求中包括访问令牌作为 Authorization: Bearer <token>

生成 4096 位 RSA 密钥对,并与 Zonos 共享公钥。这在入职期间进行一次。

生成密钥对

1# Generate private key
2openssl genrsa -out private_key.pem 4096
3 
4# Extract public key
5openssl rsa -in private_key.pem -pubout -out public_key.pem

public_key.pem 分享给 Zonos。将 private_key.pem 存储在专用的密钥管理器中(如 AWS Secrets Manager、HashiCorp Vault 等)— 从不在源代码控制或环境变量中。

Zonos 将注册您的密钥并返回您的 Organization ID,这将成为所有 JWT 断言中的 iss 声明。

使用您的私钥用 RS256 对 JWT 进行签名。该断言对单次令牌交换有效 — 使过期时间窗口保持较短(60–300 秒)。

必需的声明

声明
iss您的 Zonos Organization ID(例如 "org_abc123"
sub标识调用服务(例如 "checkout-service"
aud必须完全为 "zonos-auth"
expUnix 时间戳;从 iat 起 60–300 秒
iat发行的 Unix 时间戳
jti每个断言的唯一 UUID(启用重放检测)

JWT 头必须指定 "alg": "RS256""typ": "JWT"

代码示例

1import jwt, uuid, time
2 
3with open("private_key.pem") as f:
4 private_key = f.read()
5 
6now = int(time.time())
7assertion = jwt.encode(
8 {
9 "iss": "org_abc123",
10 "sub": "checkout-service",
11 "aud": "zonos-auth",
12 "iat": now,
13 "exp": now + 300,
14 "jti": str(uuid.uuid4()),
15 },
16 private_key,
17 algorithm="RS256",
18)

将签名的 JWT 发送到 Zonos 令牌端点以接收短期 Bearer 令牌。

端点

POST https://auth.zonos.com/oauth/token
Content-Type: application/json

也接受 application/x-www-form-urlencoded

请求字段

字段必需
grant_type"urn:ietf:params:oauth:grant-type:jwt-bearer"
assertion您的签名 JWT(紧凑序列化)

请求和响应

1{
2 "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
3 "assertion": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
4}
响应字段描述
access_token用于所有后续 API 请求的 Bearer 令牌
token_type始终为 "Bearer"
expires_in到期前的秒数(默认值:300)
scope授予此令牌的以空格分隔的权限

完整代码示例

1import requests
2 
3response = requests.post(
4 "https://auth.zonos.com/oauth/token",
5 json={
6 "grant_type": ,
7 : assertion,
8 },
9)
10data = response.json()
11access_token = data[]
12expires_in = data[]

Authorization 头中的 Bearer 令牌形式包含访问令牌,适用于每个 Zonos API 请求。

示例请求

1curl -X POST https://api.zonos.com/graphql \
2 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
3 -H "Content-Type: application/json" \
4 -d '{ "query": "{ ... }" }'

令牌生命周期和缓存 

访问令牌默认在 5 分钟后过期。缓存令牌并主动刷新 — 不要在每个 API 调用上请求新令牌。每次刷新都需要一个新签名的 JWT 断言。

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"]

错误参考 

所有错误遵循 OAuth 2.0 错误响应格式 (RFC 6749 §5.2):

1{
2 "error": "invalid_grant",
3 "error_description": "JWT assertion has expired"
4}
HTTP 状态error原因
400unsupported_grant_typegrant_type 不是 urn:ietf:params:oauth:grant-type:jwt-bearer
400invalid_request缺少或格式不正确的字段
401invalid_grant无效签名、过期的断言、未知组织或未注册的密钥
500server_error内部错误 — 如果持续存在,请联系 Zonos 支持

常见 invalid_grant 原因:

  • exp 在过去 — 确保您的系统时钟已 NTP 同步
  • aud 不完全是 "zonos-auth"
  • iss 与您的注册 Organization ID 不匹配
  • 公钥已轮换,但尚未与 Zonos 更新

安全最佳实践 

  • 保护您的私钥。 将其存储在专用的密钥管理器中 — 从不在源代码控制、环境变量或日志中。
  • 保持断言短期。 60–300 秒是标准的;没有理由发行更长期的断言。
  • 包含 jti 每个断言的唯一值支持服务器端重放检测。
  • 定期轮换密钥对。 在撤销旧密钥对之前向 Zonos 注册新公钥以避免停机。
  • 从不记录 access_tokenassertion 值。 将两者都视为凭证。
预约演示

这个页面有帮助吗?