3.2 OAuth 2.0 与 OIDC:委托授权不是登录捷径
OAuth 2.0 让资源所有者把有限 API 权限委托给 client,而不把自己的密码交给 client。OpenID Connect(OIDC)在 OAuth 之上增加身份认证语义。把 access token 当登录证明,或把 ID token 当 API 通行证,都会跨越协议边界。
先认清四个角色
Resource Owner:通常是用户
Client:请求被委托权限的应用
Authorization Server:认证用户并签发 token
Resource Server:持有受保护 APIClient ID 是公开标识,不是密码。浏览器 SPA 和原生应用无法可靠保守静态 client secret,属于 public client;后端服务能在受控环境保护凭据,才可能作为 confidential client。
Authorization Code + PKCE 是默认交互流程
简化流程:
client generates code_verifier
└─ SHA-256 → code_challenge
browser → authorization endpoint
client_id, exact redirect_uri, state,
code_challenge, method=S256
authorization server → redirect with one-time code + state
client → token endpoint
code + original code_verifierPKCE 让截获 authorization code 的攻击者缺少 verifier,无法兑换 token。Verifier 必须为每次请求重新生成并保存在发起流程的客户端上下文,不能写死或降级到弱方法。
state 绑定发起请求与回调,防止 login CSRF 和响应混淆;OIDC 的 nonce 绑定 ID token 与认证请求,帮助防重放。两者用途不同,不能因为有 PKCE 就全部删除。
Redirect URI 应精确注册与匹配。通配符、开放重定向和基于字符串前缀的宽松比较,会把 code/token 送到攻击者控制的位置。
OAuth 2.1 仍不是已发布 RFC
截至本卷专业校验时,OAuth 2.1 仍是 IETF Internet-Draft。它汇总 Authorization Code + PKCE、移除 implicit 和 resource owner password credentials 等现代实践,但不能把草案版本写成最终标准。
生产安全基线应以已发布的 RFC 9700(OAuth 2.0 Security Best Current Practice)等规范为依据:不使用 implicit grant,不使用 Resource Owner Password Credentials grant,防止 authorization code 注入和 token replay,并限制 token 权限。
Token 类型决定验证位置
Access token 是给 Resource Server 的授权凭据,可以是不透明随机值,也可以是 JWT。Client 不应依赖它的内部字段作为用户登录状态。
ID token 是 OIDC Client 的认证结果,目标 audience 是 Client。验证至少包括:
- 从受信配置确定 issuer,而不是相信 token 自报地址;
- 只允许预期算法,按 issuer 的可信 key set 验签;
- 校验
iss、aud,必要时azp; - 校验
exp、iat与合理时钟偏差; - 对应请求中校验
nonce; - 根据业务检查
auth_time、acr等保证信息。
Resource Server 验证 access token 时则关注 issuer、自己的 audience、scope/authorization details、有效期、撤销策略和 sender constraint。它不应把“JWT 签名有效”直接等同于“允许访问任意对象”。
Refresh Token 是高价值长期凭据
浏览器和移动端的 refresh token 泄露后可能持续换取新 access token。RFC 9700 要求 public client 使用 refresh token rotation 或 sender-constrained token,并检测旧 token 重放。
Token 应遵循最小 audience、最小 scope 和短有效期。高价值系统可使用 mTLS 或 DPoP 将 access token 绑定到发送方密钥,降低 bearer token 被复制后的可用性,但这仍不能修复终端已完全失陷的情况。
前端应用应避免把长期 token 放入任意脚本可读的 localStorage。对传统 Web 产品,Backend-for-Frontend 可以把 OAuth token 留在服务端,用受保护 session Cookie 暴露最小浏览器会话面。选择取决于架构,不能把 BFF 当万能代理而忽略 CSRF、会话固定和后端横向权限。
OIDC Discovery 也需要信任起点
Discovery 与 JWKS 自动化 key 发现和轮换,但 URL 必须来自预配置 issuer 或经过严格验证的发现流程。不能从任意 token 的 iss 直接发网络请求,否则可能造成 SSRF、密钥混淆和恶意 issuer 接管。
Key rotation 要允许新旧签名 key 短暂重叠,并对未知 kid 做受限刷新;攻击者可构造大量随机 kid,不能让每次失败都无限触发外部 JWKS 请求。
流程检查
- 使用 Authorization Code + PKCE(S256),而非 implicit/ROPC?
- Redirect URI 是否精确注册,没有开放重定向?
state、OIDCnonce和 PKCE verifier 是否分别生成并验证?- Access token、ID token、refresh token 是否按受众与用途隔离?
- 是否固定可信 issuer、算法与 audience?
- Token 是否最小 scope、短期、可撤销或可检测重放?
- 浏览器脚本能否读取长期凭据?
- API 是否在 token 有效之外继续做资源级授权?
下一课专门处理最后一个问题:主体已经认证后,怎样决定它能对哪一个具体资源做什么。
参考资料
- RFC 9700, Best Current Practice for OAuth 2.0 Security
- RFC 7636, Proof Key for Code Exchange
- OpenID Foundation, OpenID Connect Core 1.0
- IETF Datatracker, OAuth 2.1 Draft