第三方 OAuth 接入参考
错误码
两种错误响应格式、全部错误码与排查顺序。
MuoCeon 的 OAuth 接口有两套错误格式,取决于出错位置。写客户端时两种都要能解析,不要把「统一信封」当成全站约定。
格式一:协议端点(RFC 6749 §5.2)
适用于 token、revoke、introspect。这类端点对标准客户端必须说标准语言,否则库无法识别:
{
"error": "invalid_grant",
"error_description": "The authorization code has already been used"
}error | HTTP | 含义 | 排查方向 |
|---|---|---|---|
invalid_request | 400 | 缺参数、参数重复、grant_type 不受支持 | 检查表单字段与 Content-Type |
invalid_client | 401 | 客户端认证失败 | 密钥错、Basic 编码错、公开客户端误带密钥 |
invalid_grant | 400 | 授权码/刷新令牌无效、已用、过期;PKCE 或 redirect_uri 不匹配 | 授权码只能换一次;redirect_uri 必须逐字一致 |
unauthorized_client | 400 | 该客户端无权使用此授权类型 | 例如第三方用 client_credentials |
invalid_scope | 400 | scope 非法,或刷新时试图扩大 | 只能申请登记过的可委托 scope |
server_error | 500 | 服务端异常 | 带上请求时间与 client_id 反馈 |
格式二:统一信封
适用于 preauthorize、authorize、grants 等内部与管理端点,以及所有端点的限流响应:
{
"success": false,
"code": "OAUTH_INVALID_REDIRECT_URI",
"message": "Redirect URI is not registered for this client: https://...",
"details": ["redirect_uri must match a registered value"],
"timestamp": "2026-01-01T00:00:00.000Z"
}code | HTTP | 含义 |
|---|---|---|
OAUTH_CLIENT_NOT_FOUND | 400 | client_id 未登记 |
OAUTH_INVALID_REDIRECT_URI | 400 | 回调地址未登记或形状不合法 |
OAUTH_INVALID_REQUEST | 400 | 授权请求参数不合规 |
OAUTH_INVALID_CONSENT_TICKET | 400 | 同意票据缺失、过期、已消费,或与本次请求的客户端/账号/会话不匹配 |
OAUTH_INVALID_AUTHORIZATION_DETAILS | 400 | authorization_details 的 type/action 不在白名单内 |
OAUTH_INVALID_ASSERTION | 401 | 身份断言无效或过期 |
OAUTH_ASSERTION_REPLAY | 400 | 身份断言已被使用过 |
INSUFFICIENT_USER_AUTHENTICATION | 401 | 认证强度不足,需要提权(票据不会被消费) |
OAUTH_TOO_MANY_REQUESTS | 429 | 触发限流,响应带 Retry-After |
UNAUTHENTICATED | 401 | 资源端点:访问令牌无效或过期 |
FORBIDDEN | 403 | 资源端点:受众或 scope 不满足 |
票据错误不细分
OAUTH_INVALID_CONSENT_TICKET 刻意把「不存在」「已消费」「绑定不匹配」合并成同一个错误:
分开报等于告诉攻击者「票据是真的,只是客户端填错了」。对正常客户端而言,这三个原因的处理方式也一样——重新走一次授权。
格式三:重定向错误
用户拒绝或授权请求被拒时,错误出现在回调地址上,而不是 API 响应里:
https://your-app.example.com/callback?error=access_denied&state=k3Jd9x&iss=https%3A%2F%2Fid.example.com%2Fv1%2Foauth必须校验 state 与 iss,再处理 error。access_denied 表示用户明确拒绝——不要自动重试,那只会让用户再拒一次。
限流
- 每个端点独立计数,同一 IP 在
token上超限不影响jwks。 - 响应:
429+Retry-After(秒)。 - 客户端应遵守
Retry-After,不要立即重试;批量刷新时做随机退避。
排查顺序
遇到失败,按这个顺序定位,能覆盖绝大多数情况:
- 参数:
redirect_uri是否与登记值逐字一致(含末尾斜杠、端口)。 - 客户端:机密客户端是否带密钥、公开客户端是否误带密钥、
client_id是否正确。 - 授权码/刷新令牌:是否已被使用(授权码一次性、刷新即轮换)。
- PKCE:
code_verifier与code_challenge是否配对,方法是否为S256。 - 受众与 scope:调 UserInfo 用的是不是
auth受众令牌;scope 是否被裁剪。 - 认证强度:
acr_values/max_age是否要求过高。