跳到内容

3.2 OAuth 2.0 与 OIDC:委托授权不是登录捷径

OAuth 2.0 让资源所有者把有限 API 权限委托给 client,而不把自己的密码交给 client。OpenID Connect(OIDC)在 OAuth 之上增加身份认证语义。把 access token 当登录证明,或把 ID token 当 API 通行证,都会跨越协议边界。

先认清四个角色

text
Resource Owner:通常是用户
Client:请求被委托权限的应用
Authorization Server:认证用户并签发 token
Resource Server:持有受保护 API

Client ID 是公开标识,不是密码。浏览器 SPA 和原生应用无法可靠保守静态 client secret,属于 public client;后端服务能在受控环境保护凭据,才可能作为 confidential client。

Authorization Code + PKCE 是默认交互流程

简化流程:

text
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_verifier

PKCE 让截获 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 验签;
  • 校验 issaud,必要时 azp
  • 校验 expiat 与合理时钟偏差;
  • 对应请求中校验 nonce
  • 根据业务检查 auth_timeacr 等保证信息。

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、OIDC nonce 和 PKCE verifier 是否分别生成并验证?
  • Access token、ID token、refresh token 是否按受众与用途隔离?
  • 是否固定可信 issuer、算法与 audience?
  • Token 是否最小 scope、短期、可撤销或可检测重放?
  • 浏览器脚本能否读取长期凭据?
  • API 是否在 token 有效之外继续做资源级授权?

下一课专门处理最后一个问题:主体已经认证后,怎样决定它能对哪一个具体资源做什么。

参考资料

Built with VitePress | Software Systems Atlas