OAuth 2.0 与 OpenID Connect
OAuth 解决客户端获得资源访问授权的问题;OpenID Connect(OIDC)在其上定义用户认证结果。把“拿到了某个平台的 Access Token”直接当作本站用户身份,会遗漏令牌受众、主体来源和账号关联等边界。
OIDC 定义的身份层和 ID Token 见 OpenID Connect Core。仅提供 OAuth 的平台,需要使用该平台明确规定的用户信息接口与主体标识规则,不能自行伪造一套 ID Token 解释。
四个角色
| 角色 | 在第三方登录中的对应 |
|---|---|
| 资源所有者 | 授予访问权限的用户 |
| 客户端 | 希望接入登录或 API 的业务应用 |
| 授权服务器 | 验证用户、处理授权并签发令牌的一方 |
| 资源服务器 | 接受 Access Token 的 API,如 UserInfo |
OIDC 中提供认证的一方称为 OP,依赖认证结果的应用称为 RP。一个服务可以同时承担多种角色,但它们的验证职责仍应分开。
授权码流程
以有服务端的 Web 应用为例,浏览器传递授权码,应用后端向提供方兑换令牌。登录事务保存预期提供方、state、nonce、PKCE verifier、回跳目标与过期时间,并绑定发起浏览器。
授权请求使用精确注册的 redirect_uri 与 PKCE challenge。图中自调用表示必须成功的本地处理,任一关联、令牌或账号校验失败都停止登录,不创建正式会话;state、nonce 与 PKCE 的分工见下一节。
客户端只在后端保存上游令牌时,浏览器最终持有本地 Session Cookie 即可,没必要把上游 Refresh Token 再交给页面脚本。
state、nonce 与 PKCE
| 机制 | 关联对象 | 验证位置 |
|---|---|---|
state | 本次登录发起与回调、浏览器上下文 | 回调入口 |
nonce | 本次 OIDC 请求与 ID Token | ID Token 校验 |
| PKCE | 授权请求与授权码兑换方 | Token Endpoint |
应用统一使用授权码与 PKCE S256,同时保留一次性 state 和 OIDC nonce,是便于审计的设计选择。标准对不同条件允许使用的防护组合有更细规定,见 RFC 9700 §2.1;不能把 PKCE 解释成无条件替代所有请求关联检查。
code_challenge = BASE64URL(SHA256(ASCII(code_verifier)))verifier 使用密码学安全随机数,每次授权事务独立生成,长度与编码按 RFC 7636;不把固定字符串、用户 ID 或普通 UUID 拼接当作替代。服务端保存原值用于兑换,授权请求只带 challenge。
ID Token 验证
采用成熟 OIDC 库,按 Core §3.1.3.7与提供方元数据进行验证。应用至少需要明确以下配置与检查:
- 信任的
issuer、签名算法与该 issuer 的密钥来源。 aud是否包含本客户端,多个受众时按规范处理azp。- 时间声明是否有效,
nonce是否匹配此次请求。 - 要求近期认证或特定强度时,检查约定的
auth_time、acr等结果。 - 如再读取 UserInfo,其
sub必须与 ID Token 对应主体一致。
不能根据客户端请求参数随意信任一个新 issuer,也不能根据未验证令牌中的地址去发现密钥。多身份提供方要绑定发起时的配置,并防范 issuer 混淆;发现元数据与重定向地址同样需要受信配置。
回调是一次性业务事务
登录事务只保存随机 state,却不绑定发起浏览器,也可能接受攻击者从另一个浏览器搬来的回调。可通过安全关联 Cookie 或服务端预登录会话绑定,并设置短有效期与一次性消费。
并发标签页不能共享唯一一条“当前 state”记录。使用以事务 ID 为键的记录集合,在控制数量的前提下允许多个独立登录流程。
授权码重复兑换通常应失败,应用回调也要防止重复创建账号或重复绑定。可将事务标记为处理中并原子消费;外部兑换超时出现未知结果时,选择安全失败并重新登录,不无限重放回调。
回跳地址保存在服务端或使用严格允许列表。return_to 不能直接允许任意绝对 URL,也要处理 //evil.example、编码绕过和协议差异,避免本站成为开放重定向器。
外部账号映射
首次登录先按 (issuer, sub) 查找绑定。未绑定时,可以进入注册流程或让用户先登录现有账号再显式绑定;不要仅凭邮箱相同自动接管已有账号。
账号被冻结、企业成员被移除或外部身份被解绑后,应用必须拒绝建立对应登录态。外部认证成功只证明提供方的认证判断,本地业务状态仍由本站控制。详细流程见账号模型与生命周期。
客户端差异与旧模式
原生应用优先通过系统浏览器完成授权,回调与安全存储遵循平台要求;SPA 不携带可保密的固定 secret。后台能保密的 Web 客户端也应使用 PKCE,客户端认证与 PKCE 保护的是不同边界。
新设计不采用隐式流程返回 Access Token,也不收集用户在提供方的密码来执行密码授权模式。RFC 9700 对隐式授权给出限制,对资源所有者密码凭据授权规定不得使用;旧系统迁移需要核对兼容性,不能只替换参数名称。
参考资料
- OpenID Foundation:OpenID Connect Core 1.0
- IETF:RFC 7636 — PKCE
- IETF:RFC 9700 — OAuth 2.0 Security Best Current Practice