OAuth 授权码模式与 PKCE
解释 OAuth Authorization Code + PKCE 如何将浏览器中的授权请求与后续换取 Token 的客户端实例绑定,以及它不能解决的信任问题。
#concept
#growing
#security
#auth
#http
[!info] related notes
- 所属 MOC:
- 前置概念: access-token
- 并列概念: refresh-token
- 易混淆概念:
- 关系笔记:
OAuth 授权码模式与 PKCE
一句话定义
OAuth 授权码模式让用户在授权服务器处登录并同意权限,然后由客户端以一次性 authorization code 换取 Token;PKCE(Proof Key for Code Exchange)再证明“换 Token 的一方掌握发起这次授权时生成的随机秘密”,降低授权码被截获和冒用的风险。
为什么需要
浏览器会经由重定向将 code 交还客户端。桌面程序、CLI 和 SPA 通常不能安全保管一个所有安装实例共享的 client_secret,因此不能靠静态密钥保护授权码。PKCE 使每次授权都拥有独立的临时秘密。
核心机制
- 客户端为这一次授权请求生成高熵随机
code_verifier,只在客户端保留。 - 计算
code_challenge = BASE64URL(SHA256(code_verifier)),发起授权时发送code_challenge_method=S256和 challenge。 - 用户在授权服务器登录并同意 scope;服务器重定向回已验证的
redirect_uri,携带短期一次性code。 - 客户端向 token endpoint 提交
code和原始code_verifier。 - 服务器重算 challenge 并比对,且检查 code、redirect、client、resource 等已绑定条件;成功后按授权策略签发 Access Token,可能附带 Refresh Token。
最小场景
Claude Code 要查询 MemoFlow 目标:本地 CLI 生成 verifier → 浏览器打开 MemoFlow 授权页 → 用户确认 goals:read → 浏览器回调把 code 交给 CLI → CLI 用 verifier 换 Token → 请求 /mcp 时携带 Authorization: Bearer <access_token>。
此时 code 不是 Access Token;它不能拿来直接调用业务接口。
安全边界
- PKCE 不是客户端厂商身份认证:它证明此次 code exchange 与先前的授权请求匹配,并不证明某个程序必然由 Claude 或 Codex 官方发布。
client_id也不是秘密;公开客户端(public client)与可安全持有凭据的 confidential client 要分开讨论。- PKCE 不替代用户登录、明确 consent、精确回调 URI 校验、
state关联、issuer 校验或 resource audience 限制。 - 使用 S256,不主动降级成 plain;Token 是否可刷新仍取决于授权服务器与请求的 scope/策略。
- 一次授权码只能按规定使用,不能用作长期凭据;已被用户撤销的 grant 不能靠旧 code 或旧 token 复活。
学习检查
- 为什么公开客户端不应依赖写在二进制里的固定
client_secret? - challenge 在授权时发送、verifier 在换 Token 时提交,分别起到什么作用?
- 为什么抓到浏览器回调里的 code 仍不等于能成功换取 Token?
- PKCE 能不能证明“这一定是 Claude 官方客户端”?为什么?
- Access Token 过期后,是否一定能够得到 Refresh Token?