OAuth 与 OpenID Connect:最重要的视角转换,是 Web/App 才是 Client
理解 OAuth/OIDC 最容易卡住的地方,是把“正在点登录按钮的人”误认为 OAuth client。其实不是:浏览器里的 Web portal、移动 App、CLI,才是 Client;人是 Resource Owner / End-User。
一旦角色放对,很多看似绕的跳转就自然了:授权服务器不是把 token 发给用户,而是在用户同意后,给注册过的 Client 一份可受限、可撤销的授权凭据。
End User(人)
使用
Client(Web portal / App / CLI)
向 Authorization Server 请求授权
再携带 access token 调用
Resource Server(API)

图的上半部分是 OAuth 2.0:Client 先把用户带到 Authorization Server;用户在 IdP / 授权服务器完成登录与同意;Client 收到短期 authorization code,再用它兑换 access token,最后带着 token 调用 Resource Server。下半部分是在同一授权码主线中加入 OIDC:当请求 scope 包含 openid,Client 还能得到 ID Token,用来验证“本次登录的用户是谁”。
它也说明了 OAuth 要避免的事:第三方应用不该收集或保存用户在 IdP 的密码,而应把认证交给 IdP,再取得范围受限的授权结果。图中的 Client 是 Web portal、移动 App 或 CLI;浏览器只是帮助重定向的 User Agent,不是 OAuth Client。
| 图中的直观名称 | OAuth / OIDC 术语 | 负责什么 |
|---|---|---|
| User | Resource Owner / End-User | 登录、同意授权,拥有资源或身份 |
| Web portal / App / CLI | Client | 发起授权请求、接收 code、调用 API;不是正在操作的人 |
| IdP | Authorization Server;OIDC 场景也提供身份能力 | 认证用户、征求同意、签发 code 与 token |
/my-data 的后端 API | Resource Server | 验证 access token 与 scope,决定是否返回资源 |
IdP、Authorization Server 和 Resource Server 在小型系统中可以由同一个产品承担,但概念上不应混为一谈:Authorization Server 签发凭据,Resource Server 消费 access token 并保护数据。正因为如此,OAuth 的关键不是“把登录页嵌进应用”,而是让不同系统能在不共享用户密码的前提下委托访问权限。
OAuth 解决授权,不负责定义“用户是谁”
OAuth 2.0 的目标是 delegated authorization:用户不必把密码交给第三方应用,而是让授权服务器向该应用签发范围受限的 access token。token 面向 resource server;它回答的是“这个 client 能否代表某个授权上下文访问这个 API”。
OpenID Connect(OIDC)在 OAuth 之上增加身份层:当请求含 scope=openid,Client 可以获得 ID Token,并据此验证 End-User 的身份。OIDC Core 将其定义为建立在 OAuth 2.0 之上的身份层。
因此不要拿 access token 当本地登录凭据,也不要让前端仅凭“能 decode JWT”就相信用户身份。token 的用途和接收方决定其验证方式。
Authorization Code + PKCE:把前信道与后信道分开
现代 Web、SPA 和移动应用的默认选择是 Authorization Code Flow;public client 还应使用 PKCE。流程如下:
1. Client 生成 state、nonce、code_verifier
2. Client → Authorization Endpoint
client_id, redirect_uri, scope, state, code_challenge
3. User Agent 跳转,Authorization Server 认证用户并征求同意
4. Authorization Server → registered redirect_uri?code=...&state=...
5. Client → Token Endpoint
code + code_verifier(confidential client 还需 client auth)
6. Token Endpoint → access_token (+ id_token / refresh_token)
这里的两个边界特别重要:浏览器跳转属于 front channel,容易暴露给用户代理和历史记录,所以只返回短寿命、一次性 code;token exchange 在 back channel 或受 PKCE 保护的通道完成。PKCE 让截获 code 的攻击者无法在没有 code_verifier 的情况下兑换 token。
state 用来关联发起请求的浏览器会话并抵御 CSRF/authorization response mix-up;OIDC 的 nonce 用来将 ID Token 绑定到本次认证请求并降低重放风险。redirect URI 必须预注册并严格匹配,不能靠“startsWith”或通配符糊弄。
OIDC 的 ID Token 必须被验证,而不是被“读取”
ID Token 通常是签名 JWT。Client 至少应验证:签名(使用 issuer 的 JWKS)、iss 是否是期望的 issuer、aud 是否包含自己的 client_id、exp 是否未过期,以及请求中使用了 nonce 时 nonce 是否匹配。OIDC 对 Authorization Code Flow 的 token response 与验证步骤有明确规定。
sub 才是 issuer 范围内稳定的用户标识;email、name 是可变 profile claim。以 (iss, sub) 建立本地账户关联,比拿 email 当主键稳得多。
Web portal、SPA、mobile 的 Client 身份不同
- 传统 Web portal(confidential client):后端保存 client secret,回调后由后端换 token,再建立自己的 session cookie。
- SPA / mobile(public client):不能安全保存 client secret,使用 Authorization Code + PKCE;token 存放与刷新策略需格外谨慎。
- 服务到服务:没有人在浏览器中同意,可用 client credentials;此时 token 表示 client 本身,不代表某个用户。
图中列出的 Implicit Flow 与 Resource Owner Password Credentials Flow 是 OAuth 2.0 早期常见的分类,但不应作为新系统的默认方案:前者容易使 token 暴露在前信道,后者让 client 接触用户密码,均不符合现代安全最佳实践。浏览器、SPA、移动端通常统一采用 Authorization Code + PKCE;没有用户参与的服务间调用再考虑 client credentials。
OAuth 2.0 的最新安全最佳实践明确强调重定向 URI、PKCE、token 泄露与客户端认证等问题。
最后用一句话收束:登录页上的人完成的是认证与同意;Web/App 作为 Client 获得的是可验证、受范围约束的结果;API 只接受发给自己的 access token。把这三者混在一起,认证系统就会变得既不安全也难以解释。
References
Notion 相关零散笔记
- Auth
- OAuth
- Https
- JWT