题记与说明
综合报告(最终稿)· 2026-10-31
本文由两份章节稿综合而成,已合并两稿并消除其间的重复与分歧(2.4 节与 3.4 节的重复推荐按"一处保留结论、一处交叉引用"处理):
- 纵轴(发展史):parts/2026-10-31-身份与权限架构-OAuth-OIDC-PKCE-vertical.md
- 横轴(2026-10-31 时间截面对比):parts/2026-10-31-身份与权限架构-OAuth2.1-OIDC-PKCE-APIkey-服务账号-horizontal.md
关键事实均附来源链接;OAuth 2.1 草案状态等时效性数据在本次综合时经 IETF Datatracker 实查复核,两稿的一处日期分歧已在 1.6 节裁定。两稿中无法进一步核实的存疑项集中列于第 4 节。
本最终稿按独立审读意见逐条修订(2026-10-31):修正 1.7 总表与 2.1 题眼的自相矛盾、统一免费档数字口径、降格并补证"三层混淆"论断、补 implicit / state / DPoP-vs-mTLS 机制说明、删除对原稿结构的悬空指涉、明示 3.4 节适用范围、补 MCP 2025-11-25 与 2026-07-28 两版授权规范的直接来源链接、存疑项独立成节并消除 2.4/3.4 重复推荐。
TL;DR
身份凭证分三层:认证(OIDC,你是谁)、授权(OAuth 2.1,用户允许你做什么)、机器身份(API key/服务账号),混淆三层是最常见的认证事故根源之一。纵轴:二十年演进由客户端形态驱动(浏览器→手机→受限设备→AI 代理),协议从密码反模式收敛到授权码+PKCE。横轴:OAuth 2.1 截至 2026-10-31 仍是草案,却已被 MCP 定为强制基线——草案即事实标准。边界持续收缩:implicit/ROPC 被判死,API key 退守公共数据标识,服务账号转向联邦短时凭证。个人应用:登录用 OIDC,代用户访问用授权码+PKCE,机器用服务账号,公共数据用 API key,只有自己用就什么都别建。
一、纵轴:发展脉络——五条支流汇成一条河
"身份与权限架构"这个课题实际由五条支流构成:
- 授权(Authorization):OAuth 1.0 → OAuth 2.0 → OAuth 2.1;
- 认证(Authentication):OpenID 2.0 → OpenID Connect(OIDC),ID Token / JWKS / Discovery;
- 令牌安全加固:Bearer 令牌攻防 → PKCE(RFC 7636)→ DPoP(RFC 9449)→ RFC 9700 安全最佳实践;
- 应用级凭证(API key / PAT):各平台自发的静态密钥 → 细粒度化、短期化(GitHub fine-grained PAT、GitHub App);
- 机器身份(服务账号 / workload identity):Google 服务账号(2012)→ JWT 断言交换 → 云厂商的短期角色凭证。
五条支流的合流点就是 2020 年启动的 OAuth 2.1:它不是一个新协议,而是把 OAuth 2.0 加上十几年的安全实践与被广泛采用的扩展"折叠"回一个规范。理解这段历史的关键问题只有一个:什么凭证、在什么主体、什么场景下使用,边界在哪里。
1.1 前史:密码反模式(2006–2007)
2006 年 11 月前后,Twitter 的 Blaine Cook 在为 Twitter 实现 OpenID 时意识到,当时第三方应用接入 API 的通行做法是让用户把账号密码直接交给第三方——这就是著名的"密码反模式"(password anti-pattern)。Cook 后来自述:"OAuth came out of my worry that if the Twitter API became popular, we'd be spreading passwords all around the web."(来源:oauth.net/about、Wikipedia: OAuth)
2007 年,Cook(Twitter)、Larry Halff(Ma.gnolia)、Chris Messina 等人组成社区起草规范;2007 年 12 月 OAuth Core 1.0 社区规范定稿——第一批"自下而上"的开放标准之一,作者们后来被称为 "OAuth founding fathers"(来源:oauth.net/about)。这一阶段没有 IETF、没有大公司标准化流程,驱动力是 API 生态爆发(Twitter、Ma.gnolia、Google Data APIs)导致的用户密码四处扩散,以及一个关键认识:OpenID 解决的是"登录",不是"授权",两者是不同的问题。
1.2 OAuth 1.0:请求签名时代(2008–2012)
- 2008 年 11 月:IETF 成立 OAuth 工作组,社区规范进入标准化轨道(来源:Wikipedia: OAuth)。
- 2009 年 6 月:OAuth 1.0a 发布,修复会话固定(session fixation)漏洞——攻击者可劫持授权回调把受害者会话绑到自己的令牌上(对应 CVE-2009-2904,表述来自维基百科转述,见第 4 节存疑项)。这是 OAuth 历史上第一次"安全事件驱动规范修订"。
- 2010 年 4 月:IETF 发布 RFC 5849(The OAuth 1.0 Protocol)。1.0 的核心是对每个请求做密码学签名(HMAC-SHA1 / RSA-SHA1 / PLAINTEXT),令牌在不依赖 TLS 的情况下也不可伪造(来源:RFC 5849)。
同期平台侧:2010 年 9 月 Google 宣布 Apps APIs 全面支持 OAuth 并启动旧凭证迁移——旧的 ClientLogin(直接把用户名密码发给 Google 换令牌)正是"密码反模式"的平台化样本,2012 年宣布弃用,2015 年 4 月 20 日彻底关停(来源:Google Developers Blog, 2010-09、mike42.me: Deprecated Google APIs)。
1.0 的教训:签名方案安全但实现极其痛苦——每个请求都要规范化参数再签名,库实现普遍有 bug,平台排障困难。行业开始想要一个更简单的协议。
1.3 OAuth 2.0:授权框架 + Bearer 时代(2010–2015)
- 2012 年 7 月:主编 Eran Hammer 辞职,发表著名的《OAuth 2.0 and the Road to Hell》,批评标准化过程被大公司利益绑架、规范退化成"不安全的框架"——删掉了签名、丢掉了互操作性(hueniverse.com 原文多方存档转述,见 scripting.com 当时的评论)。
- 2012 年 10 月:IETF 同时发布 RFC 6749(授权框架)与 RFC 6750(Bearer Token 使用方式)。OAuth 2.0 彻底重设计:不再要求签名,安全完全托付给 TLS;令牌变为不透明的 Bearer 令牌("拿着就用");定义四种授权许可:authorization code、implicit、resource owner password credentials、client credentials(来源:RFC 6749、RFC 6750)。
- 2012 年:Google 推出服务账号(Service Account),机器对机器场景第一次有了标准化的"应用自有身份":用私钥签出 JWT 断言换取访问令牌,不经过用户授权流程(原始公告 URL 已失联,现收录于 Google Cloud Blog,见第 4 节存疑项)。
- 2014 年 2 月 26 日:OpenID Connect Core 1.0 定稿(连同 Discovery、Dynamic Client Registration、Session Management)。OIDC 在 OAuth 2.0 之上加了一层"简单身份层":用 ID Token(JWT 格式) 向客户端证明"这个用户是谁",用 JWKS 公钥集让客户端可以本地验证签名(来源:OpenID Connect Core 1.0)。
- 2015 年 5 月:RFC 7519(JWT)发布,为 ID Token 与自包含访问令牌提供统一格式(来源:RFC 7519)。
- 2015 年 9 月:RFC 7636(PKCE)发布。PKCE 为 authorization code 流加了一道"代码验证器"证明:客户端先生成随机
code_verifier,把其派生值code_challenge放进授权请求,换令牌时出示原值——防止授权码被中间人截获后冒用。它最初为无法保密client_secret的移动端 public client 而生(来源:RFC 7636)。
这一阶段形成了延续至今的默认分工——OAuth 管"应用代表用户做什么"(授权),OIDC 管"这个用户是谁"(认证),而 API key / 服务账号代表"应用自己",与前两者是不同类型的凭证。但 2.0 的 implicit 与 password 两种许可,埋下了此后十年收紧的伏笔。
1.4 客户端形态补全:移动、原生、受限设备(2015–2019)
- 2017 年 10 月:RFC 8252(OAuth 2.0 for Native Apps,BCP 212)发布:确立原生/移动 App 的最佳实践——用系统浏览器而非内嵌 WebView 完成授权,用 loopback(
127.0.0.1端口回调)或自定义 scheme 接收授权码,并应使用 PKCE(来源:RFC 8252)。 - 2019 年 10 月:RFC 8628(设备授权许可 / Device Flow)发布:为智能电视、CLI、游戏机等无浏览器或输入受限设备设计——设备先拿 user code + 验证 URI,用户在另一台设备上完成授权,设备轮询取令牌(来源:RFC 8628)。
- 2017 年:GitHub 发布 GitHub Apps,把第三方集成的令牌从"长期、全仓库、全权限"改为按仓库安装、细粒度权限、短期自动过期——API 集成令牌从粗放到细粒度的转折点(来源:GitHub Docs: Apps;发布年份取自官方文档表述,未逐条核对 changelog,见第 4 节)。
驱动因素:授权码中间人攻击(App 切换被劫持)等真实攻击出现;各操作系统对内嵌 WebView 与第三方 Cookie 的限制收紧;IoT/CLI 场景"设备上无法输入密码"的现实;令牌泄露事件频发催生"最小权限 + 短期"的令牌形态。
1.5 安全收紧与 OAuth 2.1 合流(2020–2025)
- 2020 年 3 月 7 日:Aaron Parecki 提交个人草案 draft-parecki-oauth-v2-1-00——"OAuth 2.1"第一次出现在世界上;2020 年 7 月 30 日被 IETF OAuth 工作组采纳为 draft-ietf-oauth-v2-1-00(来源:datatracker 修订史)。
- 2022 年 10 月:GitHub 发布 fine-grained personal access tokens,把用户侧的 PAT 也推向"按仓库选择 + 细粒度权限 + 可设过期",与 2017 年 GitHub Apps 的模型对齐(来源:GitHub Docs: Managing personal access tokens)。
- 2023 年 9 月:RFC 9449(DPoP)发布:客户端用自有非对称密钥在每个请求上证明持有令牌(sender-constrained token),弥补 Bearer 令牌"丢了就能用"的缺陷(来源:RFC 9449)。
- 2025 年 1 月:RFC 9700(OAuth 2.0 Security Best Current Practice,BCP 240)发布:把十余年攻防经验固化——对 implicit 与 password 许可判死刑、PKCE 对所有客户端强烈建议(2.1 草案进一步升为全体强制且仅允许 S256)、redirect_uri 精确匹配、bearer token 禁止进 URL、公共客户端 refresh token 必须轮换或发送方约束;它更新并扩展了 RFC 6749/6750 的威胁模型,是 OAuth 2.1 的直接蓝本(来源:RFC 9700、oauth.net/2/、Aembit 迁移指南)。
驱动因素:implicit 流把 access token 直接放进重定向 URL 的 fragment 交给浏览器端 JS,而规范层面它就拿不到 refresh token(RFC 6749 §4.2.2 明确 implicit 响应中"授权服务器 MUST NOT 签发 refresh token");它赖以"静默拿令牌/续令牌"的机制——授权服务器的会话 Cookie 在第三方上下文(隐藏 iframe 静默认证)中生效——被 Safari ITP 率先、其余浏览器陆续跟进的第三方 Cookie 封锁整体拆除,无 refresh token 可续命的 implicit 就此无路可走;大规模令牌泄露事件推动"最短路径"(authorization code + PKCE)成为唯一推荐;行业意识到"框架 + 最佳实践"两件套对开发者过于晦涩,干脆把最佳实践折回主规范(来源:RFC 6749 §4.2.2)。
1.6 AI 代理时代:OAuth 2.1 成为基础设施默认(2025–2026)
- 2025 年 3 月 / 6 月:Model Context Protocol(MCP)规范 2025-03-26 与 2025-06-18 两个版本把 OAuth 2.1 定为 HTTP 传输的授权框架,要求 PKCE、动态客户端注册、基于 RFC 9728 的授权服务器元数据发现;其后的 2025-11-25 修订与 2026-07-28 新版继续细化资源服务器侧的要求——2026-07-28 版明确 MCP 服务器作为 OAuth 2.1 资源服务器的令牌校验与 audience 绑定义务,新增 Client ID Metadata Documents(动态客户端注册降为向后兼容)、RFC 9207
iss校验,并把 mix-up/confused deputy 防护写入安全考量,代理(agent)场景的授权则经由 OAuth Client Credentials、Enterprise Managed Authorization 等授权扩展推进(来源:MCP 授权规范 2025-11-25、MCP 授权规范 2026-07-28、MCP Authorization 官方文档、MCP 授权规范 2025-06-18、MCP 规范索引、AuthZed: MCP and OAuth 2.1)。 - 截至本报告写作日(2026-10-31),OAuth 2.1 仍不是 RFC:最新修订为 draft-ietf-oauth-v2-1-16,IESG 状态 "I-D Exists",工作组里程碑是 2026 年 12 月提交 IESG。换言之,OAuth 2.0(RFC 6749)发布已满 14 年,其"修订版"还在走流程——但事实上 RFC 9700 + PKCE + MCP 的组合已经让 2.1 的内容成为事实标准(来源:datatracker: draft-ietf-oauth-v2-1,本次综合时经 WebFetch 实查:最新修订 16,Datatracker 元数据"最近更新"为 2026-09-02,草案文档自带日期为 3 September 2026,到期 2027-03-07,无 telechat、未指派 AD)。
两稿日期分歧的裁定:纵轴稿写 draft-16"更新于 2026-09-02",横轴稿写"2026-09-03"。本次实查 Datatracker 页面确认两者都对:2026-09-02 是 Datatracker 元数据的"最近更新"时间戳,2026-09-03 是草案文档自身标注的日期——指同一个 draft-16 修订,不构成事实矛盾。本报告统一表述为"文档日期 2026-09-03(Datatracker 元数据 2026-09-02)"。
驱动力与 2006 年遥相呼应:AI 代理需要"代表用户"或"以服务身份"调用大量第三方系统,MCP 生态直接选择站在 OAuth 2.1 肩膀上而不是发明新协议——授权的问题域没变,变的只是客户端形态(浏览器→手机→CLI→代理)。
1.7 阶段总表与贯穿全程的三条边界逻辑
| 阶段 | 时间 | 版本节点 | 主要参与者 | 核心驱动 |
|---|---|---|---|---|
| 前史 | 2006–2007 | OAuth Core 1.0(社区) | Twitter、Ma.gnolia、Messina | 密码反模式 |
| OAuth 1.x | 2008–2012 | 1.0a(2009-06)、RFC 5849(2010-04) | IETF WG、Eran Hammer、Google | 请求签名;但实现痛苦 |
| OAuth 2.0 | 2010–2015 | RFC 6749/6750(2012-10)、Google 服务账号(2012)、OIDC(2014-02)、JWT(2015-05)、PKCE(2015-09) | IETF、OpenID Foundation、Google | 移动化、TLS 化;认证层(OIDC 2014)同期另行补齐,OAuth 本身仍只做授权 |
| 客户端补全 | 2015–2019 | RFC 8252(2017-10)、GitHub Apps(2017)、RFC 8628(2019-10) | IETF、Apple/Google、GitHub | 原生/设备/CLI 场景;最小权限萌芽 |
| 收紧合流 | 2020–2025 | OAuth 2.1 草案(2020)、fine-grained PAT(2022-10)、DPoP(2023-09)、RFC 9700(2025-01) | IETF、GitHub、身份云厂商 | 攻防实践、浏览器生态变化 |
| 代理时代 | 2025–2026 | MCP 采用 OAuth 2.1(2025)、draft-16(2026-09) | IETF、Anthropic/MCP 生态 | AI 代理成为新客户端形态 |
贯穿全程的三条边界逻辑(本课题"边界"的历史答案):
- 用户在场 vs 用户不在场:有用户交互 → OAuth 授权码流 + PKCE(浏览器/loopback/设备流);无用户交互 → 服务账号/角色/签名 JWT;
- 应用身份 vs 用户身份:API key、服务账号代表"应用自己"(只适合标识与机器对机器,不适合授权用户数据);OAuth 令牌与 PAT 携带用户委托;
- 长期凭证 vs 短期凭证:从 2007 年到 2026 年,方向始终是"更细粒度、更短期、可撤销"——GitHub 从 PAT 到 GitHub Apps 再到 fine-grained PAT,云厂商从长期密钥到 workload identity。
二、横轴:横向对比——2026-10-31 时间截面
2.1 先画坐标:三个问题、三层边界
身份与权限的方案,本质上是回答三个不同的问题,混淆这三者是最常见的一类"认证事故"根源:
- 认证(Authentication,你是谁):OIDC、passkey、密码、magic link——证明"人"的身份;
- 授权(Authorization,你被允许做什么):OAuth 2.0/2.1 的核心——用户把"自己资源的一部分权限"委托给第三方应用;
- 机器身份(Machine Identity,调用方是哪个程序):API key、服务账号、工作负载身份——证明"程序"的身份。
边界第一条(题眼):OAuth 本身不认证。 OAuth 2.0 是授权框架,"用微信/GitHub 登录"能成立,是因为上面叠了 OIDC(ID Token)。API key 更不认证用户——按 Google 官方口径,它"标识的是项目而非用户",只适合公共数据访问与配额/计费归集(来源:Google Cloud: Why and when to use API keys)。
"最常见的一类"有据可查:RFC 9700 记载的 mix-up 攻击(§4.4) 与授权码注入(§4.5),正是令牌/回调层混淆导致"受害者在不知情中登录成攻击者账号"的真实攻击类别——1.0a 修复的会话固定是它的前身;MCP 在 2025–2026 年把 "token passthrough / audience 混淆"列为禁令(见 2.3 节),则是同一类混淆在 AI 代理形态下的重演(来源:RFC 9700 §4.4/§4.5)。
2.2 凭证与协议速查表(边界总览)
| 方案 | 本质 | 证明的是谁 | 典型场景 | 凭证有效期 | 主要误用(边界外) |
|---|---|---|---|---|---|
| OAuth 2.1 授权码 + PKCE | 委托授权框架 | 用户对应用的授权 | 第三方应用代表用户访问其资源 | access token 短期(分钟~小时级),refresh token 配轮换 | 当登录系统用(须叠 OIDC) |
| OIDC | OAuth 之上的认证层 | 用户身份(ID Token 是 JWT) | "用 Google/微信登录" | ID Token 分钟级 | 把 access token 当身份用 |
| PKCE | 授权码防拦截机制 | ——(不是身份方案) | 一切授权码流程 | —— | 用 plain 模式(已被 2.1 删除) |
| API key | 项目/应用标识符 | 项目,不是人 | 公共数据、配额与计费归集 | 长期,手动轮换 | 当作用户身份或细粒度授权用 |
| 服务账号 | 机器身份 + 策略绑定 | 程序/工作负载 | 服务间调用、自动化任务 | 最佳实践趋短(或无静态密钥) | 密钥泄漏、权限过大 |
| Passkey/WebAuthn | 用户认证因子 | 人 | 登录第二因子或替代密码 | 绑定设备/同步链 | 恢复流程没设计好 |
| SAML | 企业 SSO 联合(XML) | 企业员工 | Workforce SSO、传统企业 | 断言分钟级 | 新互联网产品首选(过重) |
| mTLS / DPoP | 发送方约束(证明持有者) | 持有私钥的客户端 | 高安全 M2M、令牌防盗用 | 证书/token 周期 | 通用 Web 场景硬上(运维成本高) |
2.3 协议层横向对比
| 维度 | OAuth 2.0 (RFC 6749, 2012) | OAuth 2.1(草案) | OIDC Core 1.0 (2014) | SAML 2.0 (2005) | Passkeys/WebAuthn | API key |
|---|---|---|---|---|---|---|
| 解决的问题 | 授权(delegation) | 2.0 的安全收敛版 | 基于 OAuth 的认证 | 企业 SSO 联合 | 用户认证(替代密码) | 应用标识 |
| 标准状态 | RFC | 草案(-16,文档日期 2026-09-03;目标 2026-12 进 IESG) | 稳定 | 稳定(遗留) | W3C/FIDO 稳定 | 无标准,各平台自定义 |
| PKCE | 建议公共客户端 | 全体强制,仅 S256 | 继承 OAuth | —— | —— | —— |
| implicit / ROPC | 存在但已不建议 | 删除 | 继承 | —— | —— | —— |
| 生态现状 | 全网基座 | MCP 强制采用中 | 登录事实标准 | 企业 SSO 存量 | 消费端 ~50 亿枚 | 云厂商 API 默认 |
| 主要风险 | 老配置的坑(implicit 等) | 尚无(未定稿) | token 类型混淆 | XML 签名实现复杂 | 恢复流程 | 静态泄漏 |
来源:draft-ietf-oauth-v2-1(Datatracker,本次综合实查)、RFC 9700、OIDC Core、RFC 7636 (PKCE)。
逐项要点:
- OAuth 2.1:还没"出生",就已经被采纳。 它做的事可以概括为"把 RFC 6749 + RFC 6750 + RFC 9700 安全最佳实践收编成一个规范"(来源:Aembit、RFC 9700)。
- OIDC:认证层的既成事实。 所有托管/自托管 IdP 都以 OIDC 兼容为第一优先级;SAML 在企业存量市场仍有优势,但新产品接入基本默认 OIDC。边界提醒:ID Token(身份)和 access token(授权)不能互换使用——MCP 安全最佳实践专门把 "token passthrough / audience 混淆"列为禁令,这是 2025–2026 年新出现的一类典型事故(来源:MCP 授权规范)。
- PKCE:从可选到强制的十年。 RFC 7636(2015)最初只是公共客户端的补救;RFC 9700(2025-01)把它升级为对机密客户端也强烈建议;OAuth 2.1 草案直接全体强制且只允许 S256。对开发者的影响:写授权码流程时无脑带上 PKCE 就对了——本课题里"零成本、纯增益"的一项。
- API key:边界收缩但没退场。 正确生态位是"轻"(公共数据 + 配额/计费标识),错误生态位是"当身份系统用"(来源:Google Cloud 文档)。
- Passkeys:消费端越过临界点,企业端起飞。 FIDO Alliance 2026 年"World Passkey Day"数据:全球约 50 亿枚 passkey 在用,认知率 90%,75% 的人至少在一个账号启用过(来源:FIDO Alliance 报告);企业侧 FIDO2/WebAuthn 部署 2025 年增长 412%,89% 的新无密码企业部署选择 passkeys(来源:Descope: Passkey Trends)。主流 IdP(含自托管的 Authentik/Keycloak/Logto)都已内置 passkey,"要不要支持"已不用讨论,只剩"恢复流程怎么设计"。
2.4 实现层横向对比:真正的选型战场
协议是公共品,差异化竞争发生在"谁替你把协议做对"。三条路线:托管 IdP(省心、按月费)、自托管 IdP(可控、运维成本)、代码库级认证(嵌进应用、无 MAU 计费)。
托管 IdP(SaaS 认证)
| 服务 | 免费档(2026 口径) | 付费起点 | 协议/能力 | 适合 |
|---|---|---|---|---|
| Auth0 (Okta) | 25,000 MAU(从 7.5K 扩容) | Essentials 约 $150/月 | OIDC/SAML/企业级最全 | 需要企业特性且预算够 |
| Amazon Cognito | 10,000 MAU(Lite 档) | Lite/Essentials/Plus 分档 | OIDC/SAML,AWS 深度绑定 | 已在 AWS 且能接受复杂定价 |
| Clerk | 50,000 MRU(月留存用户) | 按留存用户阶梯 | OIDC,组件化 UI | 前端团队要"开箱即用的登录 UI" |
| Supabase Auth | ~50,000 MAU | Pro $25/月 | OIDC,与数据库一体 | 已用 Supabase 的全栈个人项目 |
| Firebase Auth | 基础登录免费 | 高级功能需 Identity Platform 付费档 | OIDC 为主 | 移动端/原型 |
价格为 2025–2026 年对比文章口径(来源:Zuplo 认证定价对比、DesignRevision 对比(2026-02)、DesignKey SaaS auth 对比),各家免费档近年频繁调整,决策前以官方 pricing 页为准。同一批文章的规模化测算:Auth0 在 10 万 MAU 量级约为 Supabase 的 10–35 倍,Cognito 的分档加价经常"越用越贵"(二手测算,见第 4 节)。
自托管 IdP
| 项目 | 语言/栈 | 定位 | 成熟度 | 适合 |
|---|---|---|---|---|
| Keycloak | Java | 企业 workforce SSO 的"重型坦克"(OIDC/SAML 全,内存 GB 级) | 最高 | 企业/协议全要求 |
| Authentik | Python | 最易上手的内部 SSO(OIDC/SAML/proxy) | 高(homelab 首选) | 个人 homelab、内部应用 |
| Logto | TypeScript | 轻量 CIAM,开发者友好 | 中高 | 中小产品的 C 端登录 |
| Zitadel | Go | 多租户 + 内建审计 | 中高 | 多租户 SaaS |
| Ory | Go | API-first 模块化套件(需组装) | 中 | 定制化 API 产品 |
来源:r/selfhosted 社区对比帖、Zitadel vs Keycloak(厂商博客,注意立场)。自托管 IdP 对个人应用是否"过度设计"、何时才值得上,本报告统一收在 3.4 节的场景决策表里给结论;若目的就是学习或 homelab 本身,它自然另当别论。
代码库级认证
| 库 | 形态 | 特点 | 2026 年状态 |
|---|---|---|---|
| Better Auth | TypeScript 库,框架无关 | 数据库 session、插件生态(2FA/passkey/组织)、自托管零 MAU 费 | 2024 年出现,上升极快,新项目社区默认推荐 |
| Auth.js (NextAuth v5) | TypeScript 库 | 老牌、OAuth provider 生态最大 | 稳定维护,v4→v5 迁移有摩擦 |
| Lucia | —— | 曾是最小化代表 | 已转型为教学资源("a pattern, not a package") |
说明:此表为原始检索的社区聚合观点(部分直接链接未留存,属二手聚合,见第 4 节),以 better-auth.com 与 authjs.dev 官方文档为准。三者的共同点:认证逻辑嵌在你的应用和数据库里,没有 MAU 计费、没有第三方停服风险。"个人应用最终该选哪条路线、哪个产品",本报告统一在 3.4 节给出结论与推荐栈,此处不重复。
2.5 机器身份层横向对比
| 凭证 | 形态 | 有效期 | 撤销 | 最佳实践边界 |
|---|---|---|---|---|
| API key | 静态随机串 | 长期 | 可撤销 | 只标识项目/公共数据;不进前端、不进 URL、不进 git |
| 服务账号 + JSON 密钥 | 静态私钥文件 | 长期 | IAM 撤销 | 尽量不下载;必须用时最小角色 + 定期轮换 |
| 工作负载身份联邦 | 短时令牌换发 | 短(分钟~小时) | 天然过期 | 云上默认:GCP/AWS/Azure OIDC 联邦替代静态密钥 |
| CI OIDC(GitHub Actions → 云) | 短时 JWT | 作业级 | 天然过期 | 云部署首选,secret 里不再存云密钥 |
GITHUB_TOKEN | 作业级自动 token | job 结束即失效 | 自动 | Actions 默认,最小权限 |
| GitHub App 安装 token | 短时 | ~1 小时 | 吊销安装 | 跨仓库/组织自动化,优先于 PAT |
| Fine-grained PAT | 静态长令牌 | 人工设过期 | 可撤销 | 最后手段:个人脚本、CI/CD 外的临时用途 |
| mTLS / DPoP | 发送方约束 | 证书/令牌周期 | 证书吊销 | 高安全 M2M;个人应用一般不必上 |
来源:GitHub Docs: Authentication in a workflow、GitHub Apps vs OAuth Apps、Google Cloud Blog: 服务账号认证、Google Cloud: 认证方式选择、Auth0: API keys → OAuth 迁移论述。
要点:Google 官方建议机器对机器优先用 IAM 绑定的服务账号、signed JWT,尽量避免下载长期 JSON 密钥,在云上用"附加服务账号"或 Workload Identity Federation 获取短时凭证;GitHub 侧同理——Actions 默认用最小权限的 GITHUB_TOKEN,部署上云用 OIDC 联邦换短时云凭证,GitHub App 短时安装 token 优先于 PAT,fine-grained PAT 只是最后手段。机器身份的趋势是"短时化、无钥化、身份内置"——能联邦就不发静态密钥,能 job 级生命周期就不跨作业复用。
补充机制说明(DPoP 与 mTLS 的差异与取舍):二者同属"发送方约束"——把令牌绑定到客户端持有的私钥,令牌被偷也无法在其他客户端使用——但作用层级与成本不同。DPoP(RFC 9449)在应用层:客户端对每个请求用自有非对称密钥签出一个短时效 JWT 证明,纯代码即可实现、不需要任何证书基础设施,适合互联网 API 与纯软件客户端,代价是每个请求多一次签名计算、且防护止步于应用层;mTLS(RFC 8705)在传输层:TLS 握手阶段即双向验证证书,保护覆盖整条连接、生态成熟,但需要证书签发、轮换、吊销的完整 PKI 运维。取舍:具备企业 PKI 与网关能力的高安全 M2M 选 mTLS;跨组织的互联网 API 场景选 DPoP;个人应用两者一般都不必上(来源:RFC 9449、RFC 8705)。
2.6 2026 时间截面的三个新变量
- MCP 把 OAuth 2.1 变成 AI 时代的强制基线。 MCP 授权规范要求:授权服务器 MUST 实现 OAuth 2.1;客户端 MUST 实现 PKCE(S256);MUST 使用
resource参数(RFC 8707)做 audience 绑定;MCP 服务器 MUST 实现 Protected Resource Metadata(RFC 9728)并禁止 token passthrough;动态客户端注册为 SHOULD(来源:MCP 授权规范 2025-06-18、RFC 8707、RFC 9728)。"给 AI Agent 做的 MCP 服务器要不要支持 OAuth",2026 年的标准答案已经是"HTTP 传输就必须"。 - 托管认证的免费档大跃进 → 个人应用认证成本 ≈ 0。 Auth0 免费档从 7,500 → 25,000 MAU,Clerk 扩到 50,000 月留存用户,Supabase ~50K MAU(来源:DesignRevision 2026-02、Zuplo)。叠加代码库级方案(Better Auth/Auth.js)零 MAU 费,"自己手写密码存储"在个人项目里已经没有经济上的借口。
- Passkeys 越过临界点。 消费端 50 亿枚在用、90% 认知率(FIDO Alliance 2026),企业端 +412%(Descope)。个人应用加 passkey 的边际成本接近零,值得默认开启——同时必须设计好恢复路径(passkey 体系公认的最弱一环)。
三、交叉洞察:两轴合起来说明了什么
3.1 边界是攻防打出来的,不是设计出来的
把纵轴铺开再看横轴的每一条 "MUST",会发现一个规律:现在边界上的每一道线,背后都是纵轴上的一次真实事故或一类新客户端的倒逼。会话固定攻击打出 1.0a;签名库实现质量参差打出"安全托付 TLS"的 2.0;Bearer 令牌"丢了就能用"打出 PKCE、DPoP 和 RFC 9700;浏览器封杀第三方 Cookie 打死了 implicit(它靠授权服务器 Cookie 在隐藏 iframe 里静默发令牌、续令牌,Cookie 一被封杀这条静默通道即告断绝,机制见 1.5 节);移动端无法保密 client_secret 打出 public client 概念与系统浏览器要求;IoT/CLI 打出 Device Flow;AI 代理打出 MCP 对 audience 绑定(RFC 8707)与 token passthrough 禁令的强制。反过来,横轴上那些"看起来过时"的东西(SAML、API key)没有被消灭,只是被压缩进历史给它们划定的生态位——企业存量 SSO 与"轻量公共数据标识"。
这给评估者的启示是一个可复用的问题:面对任何新凭证形态或新规范条款,先问"它防的是哪次事故、服务的是哪类客户端"。答不上来的条款大概率是过度设计,答得上来的条款才值得付出遵循成本。
3.2 三条边界逻辑在 2026 截面上的投影
纵轴总结的三条边界逻辑,在横轴的 2026-10-31 截面上全部找到了对应物:
- 用户在场 vs 不在场:授权码 + PKCE 从"移动端补救"长成"一切客户端的默认"——RFC 8252 把它带进原生 App,RFC 9700 把它推给机密客户端,OAuth 2.1 把它定为全体强制,MCP 把它带进 AI 代理。用户不在场的一侧则从 Google 2012 年的静态 JWT 服务账号演化到 2026 年的"联邦换短时凭证"。
- 应用身份 vs 用户身份:Google "API key 标识项目而非用户"的口径与 MCP "禁止 token passthrough" 的禁令,是同一条边界在新场景下的两次重申——一次划清"程序标识 ≠ 用户身份",一次划清"用户授权 ≠ 资源服务器身份"。
- 长期 vs 短期:GitHub PAT → GitHub Apps → fine-grained PAT 的三段演化,与 Google JSON 密钥 → Workload Identity Federation 的两段演化,是同一条"更细粒度、更短期、可撤销"曲线在两个平台的投影。
横轴数据还补上了纵轴看不到的一面:边界之内已经 commodity 化(认证免费档普遍扩容至 2.5 万–5 万 MAU:Auth0 2.5 万、Clerk/Supabase 5 万;passkey 开箱即用、OIDC 处处兼容),因此 2026 年个人开发者的剩余风险几乎全部集中在边界之外的误用上——API key 当身份用、静态密钥进 CI、ID Token 与 access token 混用、refresh token 放前端。这就是为什么本课题的最终交付物是一张"边界速查表"(3.5 节)而不是一份"产品推荐榜"。
3.3 草案即事实标准:纸面状态 ≠ 事实状态
OAuth 2.1 从 2020 年个人草案到 2026 年仍是 "I-D Exists",但 MCP 已经把它定为强制基线,RFC 9700 已经把它的核心条款固化为 BCP——生态采纳走在了 IETF 流程前面。判断一个标准的成熟度,要看"谁在强制采用它",而不是只看"IETF 状态栏"。这是横轴对比对纵轴叙事的修正:如果只读规范流程,会得出"2.1 还没定稿、暂不采用"的误判;如果只看生态,又会忽略它毕竟不是 RFC、个别条款(如 S256-only)仍可能在定稿中微调。
实操含义:今天实现授权码流,就按 2.1 草案的方式写(PKCE S256 + 精确 redirect_uri + 无 implicit/ROPC)——这同时满足 RFC 9700 的合规要求,2.1 转正时零迁移成本。但在对外声称合规基线时,引用的应是已发布的 RFC 6749/6750/9700,而非草案。
3.4 对个人开发者的判断与建议(两稿建议的合并版)
适用范围:本节与 3.5 节的建议面向个人开发者与小团队——包括一人团队同样会遇到的个人脚本、CI/CD、个人 MCP 服务器等场景;企业 workforce SSO、Keycloak 级自托管选型只在 2.2–2.4 节给出对比坐标,本节不展开落地建议,企业读者请从 2.4 节的托管 IdP 企业版与自托管 IdP 对比出发另行评估。
纵轴稿与横轴稿各给了一套建议,二者高度重叠;本节将两稿建议合并为一张按场景展开的决策表,并统一共同原则。
共同原则(两稿合并):不自建 IdP;不碰已被 RFC 9700 / OAuth 2.1 禁用的 implicit 与 password 流;授权码流程一律 PKCE(S256) + state + 精确 redirect_uri;密钥一律环境变量或系统 keyring,绝不进代码仓库;云上能联邦就不发静态密钥;上线前先想好密钥泄漏的撤销路径;不自研 OAuth provider、密码哈希以外的加密、JWT 签名算法选择——用库,别造轮子。
三件套各防一环:PKCE 防授权码在返回途中被中间人截获冒用——换令牌时必须出示当初随机生成的 verifier,截获者拿到的只是无用码;state 防 CSRF 与回调劫持/授权码注入——客户端发起授权前生成随机值存入本地会话,回调必须原样带回并比对,攻击者伪造或替换的回调对不上这个值,也就无法把受害者会话绑到攻击者的授权上(RFC 9700 §4.7 要求用 state 携带一次性 CSRF token,或以 PKCE/nonce 等效防护;其 §4.5 记载的授权码注入同样靠这一比对拦截);精确 redirect_uri 防授权码经宽松的重定向匹配漏给第三方应用(来源:RFC 9700)。
| 场景 | 正确工具 | 要点 |
|---|---|---|
| 只有你自己用(个人仪表盘/工具) | 不建登录系统 | Tailscale/WireGuard 内网直达;或 HTTP Basic + TLS;要像样一点就单一管理员密码 + magic link |
| 少量真实用户(几十到几千) | Better Auth(新项目默认)或 Auth.js v5;不想碰认证代码则 Clerk/Supabase Auth 免费档 | 零 MAU 费或免费档顶到产品验证期;开 passkey 插件与邮箱恢复 |
| 应用代用户访问第三方(GitHub/Google/Notion…) | 授权码流程 + PKCE(S256) + state + 精确 redirect URI | 本地用系统浏览器 + loopback 回调(RFC 8252);纯后端 cron 用 Device Flow(RFC 8628);token 只存服务端,能 httpOnly cookie 就 httpOnly |
| 个人脚本/CLI 调第三方 API | API key 或 fine-grained PAT 一个 + HTTPS | 只勾需要的仓库与权限;存放环境变量或 OS keyring;不要为脚本实现 OAuth 服务器 |
| 自己的多个后端服务互调 | VPS:每服务独立 API key + 服务端哈希存储 + 可单独吊销;云上:服务账号/工作负载身份联邦拿短时凭证 | 不下载 JSON 密钥文件;确需静态凭证时用"签名 JWT 换短期访问令牌"模式 |
| CI/CD | GitHub Actions 默认 GITHUB_TOKEN 最小权限;部署云资源用 OIDC 联邦 | 不在 Secrets 存云密钥;任何 PAT 都应 fine-grained + 设过期且仅作最后手段 |
| 给自己的应用加"登录"(统一账号) | 托管认证服务(Auth0/Clerk/Supabase Auth/Logto)或现成 IdP(Keycloak/Authentik),走 OIDC 授权码 + PKCE | 个人应用只填 client_id 与回调 URL;单个用户/家人用的内部工具连 OIDC 都可省(反代基础认证 + HTTPS) |
| 做了 MCP 服务器(HTTP 传输) | OAuth 2.1 + PKCE + RFC 8707 resource + RFC 9728 | 禁止 token passthrough,校验 audience |
两个具体推荐栈(2026 时间截面):
- 栈 A"零月费全栈"(TS 全栈应用,自有 VPS/Serverless):Better Auth + 邮箱 magic link + passkey(可选)+ 应用数据库存 session;对外脚本/自动化用可撤销的 API key(前缀标识 + 哈希入库);CI 用
GITHUB_TOKEN+ OIDC。月成本 0 元,代价是半天学习时间。 - 栈 B"零认证代码"(更想写业务):Clerk 免费档(或 Supabase Auth,若数据库也用 Supabase);业务 API 校验其签发的 JWT(验签名 +
aud);自己的脚本调自己的 API 用 API key。月成本 0 元(免费档内),代价是平台锁定与免费档条款变动风险。
3.5 边界速查表(题眼的最终压缩版)
| 你想做的事 | 正确工具 | 越界行为 |
|---|---|---|
| 用户登录我的应用 | OIDC(ID Token)/ magic link / passkey | 用 access token 认身份;自写密码哈希以外的认证 |
| 用户授权我访问他在别处的资源 | OAuth 授权码 + PKCE(S256) | implicit;密码模式;把 refresh token 放前端 |
| 标识"哪个项目在调我的公共 API" | API key(配额/计费) | 把 API key 当用户身份或细粒度授权 |
| 我的服务调你的服务 | 服务账号 / client_credentials / 联邦短时令牌 | 共享一个"万能"API key;下载长期 JSON 密钥 |
| CI 部署云资源 | OIDC 联邦 | Secrets 里存云 AK/SK;用经典 PAT |
| MCP 服务器(HTTP) | OAuth 2.1 + PKCE + resource + PRM | token passthrough;不校验 audience |
| 高价值令牌防盗用 | DPoP / mTLS(发送方约束) | ——(个人应用可暂缓) |
一句话版本:用户委托用 OAuth+PKCE,用户登录用 OIDC+passkey,机器用服务账号/联邦,公共数据用 API key,只有自己用就什么都别建。
四、存疑项:剩余不确定性与两稿存疑项(合并保留)
- GitHub Apps "2017 年发布"取自搜索结果与官方文档表述,未逐条核对 changelog 原文;
- Google 服务账号 2012 年原始公告 URL 已失联,现仅能给出 Google Cloud Blog 站点级链接;
- OAuth 1.0a 对应 CVE-2009-2904 的表述来自维基百科转述;
- Eran Hammer《Road to Hell》原文站已不可直达,依据多方存档转述;
- 托管 IdP 价格与免费档变动频繁,本文数字为 2025–2026 年对比文章口径,决策前以官方 pricing 页为准;"Auth0 ≈ 10–35 × Supabase"为二手测算;
- Better Auth / Auth.js / Lucia 的对比结论部分为社区二手聚合(原始检索未留存全部直接链接),以官方文档为准;
- passkey 消费端与企业端数据来自 FIDO Alliance 与 Descope(后者为利益相关方厂商),绝对数值宜作量级参考。
五、参考来源
协议与规范
- OAuth 起源与社区史:https://oauth.net/about/ ;https://en.wikipedia.org/wiki/OAuth
- RFC 5849(OAuth 1.0):https://www.rfc-editor.org/info/rfc5849
- RFC 6749 / 6750(OAuth 2.0 / Bearer):https://www.rfc-editor.org/info/rfc6749 ;https://www.rfc-editor.org/info/rfc6750
- OpenID Connect Core 1.0(2014-02-26):https://openid.net/specs/openid-connect-core-1_0.html
- RFC 7519(JWT):https://www.rfc-editor.org/info/rfc7519
- RFC 7636(PKCE):https://www.rfc-editor.org/info/rfc7636 ;https://datatracker.ietf.org/doc/html/rfc7636
- RFC 8252(Native Apps BCP):https://www.rfc-editor.org/info/rfc8252
- RFC 8628(Device Flow):https://www.rfc-editor.org/info/rfc8628
- RFC 9449(DPoP):https://www.rfc-editor.org/info/rfc9449
- RFC 8705(OAuth 2.0 mTLS):https://www.rfc-editor.org/info/rfc8705
- RFC 9700(Security BCP,2025-01):https://www.rfc-editor.org/info/rfc9700 ;https://oauth.net/2/oauth-best-practice
- RFC 8707(Resource Indicators):https://datatracker.ietf.org/doc/html/rfc8707
- RFC 9728(Protected Resource Metadata):https://datatracker.ietf.org/doc/html/rfc9728
- OAuth 2.1 草案状态与修订史(2026-10-31 实查):https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/ ;https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/history/
- Eran Hammer《OAuth 2.0 and the Road to Hell》(2012-07,hueniverse.com 原文多方存档转述; contemporaneous comment: http://scripting.com/2012/07/26/oauth1IsFine.html )
MCP 与 AI 代理
- MCP 授权规范(2025-06-18 修订):https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization
- MCP 授权规范(2025-11-25 修订):https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization
- MCP 授权规范(2026-07-28 新版,资源服务器要求细化,含安全考量与授权扩展):https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization/ ;https://modelcontextprotocol.io/specification/2026-07-28/basic/authorization/security-considerations
- MCP 授权总览与规范索引:https://modelcontextprotocol.io/extensions/auth/overview ;https://modelcontextprotocol.io/specification
- AuthZed: MCP and OAuth 2.1:https://authzed.com/learn/mcp-oauth-2-1-authentication
平台官方文档
- GitHub 令牌与工作流认证:https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens ;https://docs.github.com/actions/reference/authentication-in-a-workflow ;https://docs.github.com/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps ;https://docs.github.com/en/apps
- Google 凭证演进与机器身份:https://developers.googleblog.com/new-oauth-support-for-google-apps-apis ;https://mike42.me/blog/2015-04-deprecated-google-apis-what-you-need-to-know ;https://cloud.google.com/blog/products/identity-security/how-to-authenticate-service-accounts-to-help-keep-applications-secure ;https://docs.cloud.google.com/api-gateway/docs/authentication-method
- Google API key 定位:https://docs.cloud.google.com/endpoints/docs/openapi/when-why-api-key
选型对比与市场数据
- 认证服务定价:https://zuplo.com/learning-center/api-authentication-pricing ;https://designrevision.com/blog/auth-providers-compared ;https://www.designkey.studio/post/saas-auth-comparison-clerk-vs-auth0-vs-supabase
- 自托管 IdP 对比:https://www.reddit.com/r/selfhosted/comments/1szaigb/selfhosting_authentication_server_choice ;https://zitadel.com/blog/zitadel-vs-keycloak(厂商博客,注意立场)
- 代码库级认证官方文档:https://www.better-auth.com/ ;https://authjs.dev/
- Passkey 数据:https://fidoalliance.org/fido-alliance-reports-accelerating-global-passkey-adoption-on-world-passkey-day-2026 ;https://www.descope.com/blog/post/passkey-trends
- OAuth 2.1 迁移与 API key→OAuth 论述:https://aembit.io/blog/oauth-2-1-guide-migration-security ;https://auth0.com/blog/why-migrate-from-api-keys-to-oauth2-access-tokens