令牌端点
POST /oauth/token
用授权码换取 Access Token,或用 Refresh Token 刷新。
响应 Content-Type: application/json; charset=utf-8
频率限制
- 30 次/分钟(基于请求方 IP,非服务器全局限制)
- 超限返回 HTTP 429,响应头包含
Retry-After
Authorization Code Grant
用授权码换取 Token。
请求
POST https://id.caellab.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=YOUR_CALLBACK_URL
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
也支持 HTTP Basic Auth 传递凭证:
POST https://id.caellab.com/oauth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic BASE64(client_id:client_secret)
grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=YOUR_CALLBACK_URL
请求参数
| 参数 | 必需 | 说明 |
|---|---|---|
grant_type | 是 | 固定值 authorization_code |
code | 是 | 授权码 |
redirect_uri | 是 | 必须与授权请求中一致 |
client_id | 是 | Client ID(Basic Auth 时可省略) |
client_secret | 是 | Client Secret(Basic Auth 时可省略) |
处理逻辑
- 验证
client_id+client_secret(失败返回 401invalid_client) - 查找授权码(验证 client_id 匹配、未过期)
- 验证
redirect_uri与授权时一致 - 删除已使用的授权码(一次性使用)
- 生成 Access Token(
bin2hex(random_bytes(32))) - 生成 Refresh Token
- 存储 SHA-256 哈希到数据库
- 返回 JSON 响应
成功响应
{
"access_token": "a1b2c3d4e5f6...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "g7h8i9j0k1l2...",
"scope": "openid profile email"
}
错误响应
| error | 状态码 | 说明 |
|---|---|---|
invalid_client | 401 | client_id 或 client_secret 错误 |
invalid_grant | 400 | 授权码无效、已过期、或已使用 |
invalid_request | 400 | 缺少 code 或 redirect_uri |
unsupported_grant_type | 400 | grant_type 不是 authorization_code 或 refresh_token |
rate_limited | 429 | 请求太频繁 |
Refresh Token Grant
刷新 Access Token。
请求
POST https://id.caellab.com/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token=YOUR_REFRESH_TOKEN
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
请求参数
| 参数 | 必需 | 说明 |
|---|---|---|
grant_type | 是 | 固定值 refresh_token |
refresh_token | 是 | Refresh Token |
client_id | 是 | Client ID |
client_secret | 是 | Client Secret |