OAuth 委托授权生命周期与即时撤销
解释 access/refresh token 如何绑定独立授权生命周期,scope 如何收缩,以及撤销后重新授权为何不能让旧 Token 复活。
[!info] related notes
- 所属 MOC:
- 相关概念: mcp-http-authorization、oauth-authorization-code-pkce
- 易混淆概念:
- 相关资源:
OAuth 委托授权生命周期与即时撤销
范围
本笔记讨论某个用户授权某个客户端访问某个资源的一次授权生命周期(grant / connection),以及 Token 刷新、缩权、撤销、重新授权之间的关系。它是设计严谨资源服务器时的长期不变量,不等于 OAuth 规定所有实现必须创建名为 Connection 的业务数据库表。
为什么要为一次授权建立稳定关联
用户 A 授权 Claude Code(grant-1)
├─ access-1
└─ refresh-1 → refresh-2
↓
撤销
× 旧凭据不可用
用户 A 重新授权 Claude Code(grant-2)
└─ 新凭据
即便 userId 与 clientId 完全相同,grant-2 也不能使 grant-1 的旧 JWT 复活。 授权生命周期必须可区分;既可以分配新 connection/grant ID,也可采用具备相同防复活效果的授权版本。Token 刷新应持续关联其来源生命周期。
服务端有效权限模型
对于需要即时缩权和撤销的系统,可以定义:
effectiveScopes =
tokenScopes
∩ currentGrantScopes
∩ serverAllowedScopes
还应检查 token 的签名或 introspection、issuer、audience/resource、有效期、具体 grant 是否仍有效、账户状态和业务资源的 owner policy。
- 新 consent 增加 scope,不得隐式给旧 Token自动扩权。
- 原 grant 被缩权,应让随后发起的请求立刻受限。
- 撤销成功后开始的新 MCP 调用必须拒绝;已提交的业务命令不靠撤销自动回滚。
- 这种每请求在线核验 grant是强撤销要求对应的工程设计;不是“只要 JWT 签名正确”就自然拥有的能力。
Refresh Token、offline_access 与连接寿命
offline_access 是一些 OAuth Authorization Server 用来判断是否允许离线访问/签发 Refresh Token 的 scope。客户端可以请求,授权服务器也可以拒绝签发;不能认为 Access Token 一定会配 Refresh Token。
协议上的注意点:
offline_access通常是授权服务器的委托/续期授权,不是受保护 MCP resource 所需的业务读取 scope。- 需要分别约束 Access Token TTL、Refresh Token TTL/空闲超时、Grant 最长生命周期、rotation/replay detection。
- Refresh Token 轮换不应该自动解释为用户同意无限期授权;是否延长总寿命必须是明确产品策略。
- 撤销与账户关闭必须覆盖 token endpoint 的 refresh 流程,而不仅是 MCP Gateway。
- 当 auth/grant store 不可用且产品承诺即时撤销时,应 fail closed,不能默认放行。
并发与重放
旧 Refresh Token 在并发刷新中可能被重复提交。安全实现需要原子 single-winner 语义,并可根据厂商实现定义短重试窗口,避免正常网络重试误判为攻击。若支持 overlap,则必须确认同一组轮换结果返回、跨实例和进程重启的稳定性;超窗重放的处理遵循具体 Provider 策略。
“30 秒 refresh overlap”不是 OAuth 的通用强制规定,只是部分实现(如某些 Better Auth MCP 版本)的配置选择;安全验收必须核对锁定版本源码与真实数据库行为。
需要能够复现的反例
- 撤销 grant-1,立刻重新授权同一 client 为 grant-2;用 grant-1 未过期 JWT 访问应拒绝。
- 减少
tasks:read权限;旧 Token 的该权限不再有效。 - 在 revoke 前刚发生 refresh,revoke 完成后新 Token 不能越权执行。
- 在 short refresh retry window 内,旧凭据重试仍必须遵守当前撤销和账户状态。
- 授权数据库不可用,不能只靠 JWT 本地验签绕过即时撤销承诺。
学习检查
- 为什么不能只用
userId + clientId判定某个 JWT 仍属于活跃授权? tokenScopes ∩ currentGrantScopes分别防止哪两类权限漂移?- Refresh Token Rotation 是否天然意味着用户永久授权?
- MCP Gateway 每请求校验 grant 已实现后,为什么 token endpoint 仍要独立检查 revoke?
- “30 秒可重试窗口”与“无限容忍旧 Refresh Token”有什么根本差别?