摘要

  • draft-li-oauth-delegated-authorization-03 提议由授权服务器签发根令牌,再允许客户端给另一个客户端签发权限更窄的子令牌。它是拟走 Standards Track 的个人 Internet-Draft,不是 RFC、工作组共识或部署证明。
  • 子令牌签名只证明父令牌绑定的密钥签了这一跳。它不单独证明根可信、累计权限没有扩张、资源所有者同意转授权,或资源服务器此刻仍允许该操作。
  • 叶端客户端提交精确、有序的完整链和 DPoP 证明;资源服务器仍需验证所有祖先、相邻密钥、语义包含、撤销现状与本地政策,然后才可能产生应用效果。

事故现场往往只剩下最后一个令牌。它的签名正确、期限未过、受众看似匹配,持有者也能用绑定密钥生成 DPoP。若审计到这里就停止,系统会把“这件对象自洽”误写成“这项行动有权发生”。

个人草案 draft-li-oauth-delegated-authorization-03 把授权设计成一条有方向的链。授权服务器生成根 Delegated Authorization Token,用 cnf.jkt 把权限绑定给客户端 A 的密钥。A 若仍有转授权深度,可以签一个绑定到 B 密钥的子令牌。B 只能在整条路径已经留下的范围内继续使用或再收窄。

这种设计适合代理系统:本地每一跳不必重新联络授权服务器。但服务器缺席并不等于权限真空。根令牌预先给出的,是一项有上限的委任能力;后续每一跳只能消耗它。资源服务器在请求到来时从可信根重新计算,不能只相信链尾。

修订版 03 上传于 2026 年 7 月 24 日,2027 年 1 月 25 日到期。页首写着 Standards Track 意向,而 Datatracker API 目前没有填入 stream 或 intended level。文中的媒体类型、认证方案、claim 和元数据注册仍是请求,不是已经生效的 IANA 记录。本文不推断采纳、互操作或产品实现。

第一条信任边不在令牌自己里面

根令牌必须用来自可信配置或经认证元数据的授权服务器密钥验证。软件不能只读到 iss,再接受同一个对象提供的任意公钥,让令牌自己决定谁有权验证它。根信任来自链外。

客户端签发的子令牌在受保护头中携带签发者的公开 JWK。验证器计算 RFC 7638 指纹,与直接父令牌的 cnf.jkt 对比,再用该 JWK 验证签名。成功意味着:父令牌所绑定密钥的控制者签了这一个孩子。

这不意味着该密钥属于预期的人、公司、租户或工作负载。A 如何取得 B 的公钥、又如何确认它就是目标 B,草案明确留给本地 runtime、配置或应用协议。上游若把攻击者的密钥贴成 B,后续链可以在密码学上完全正确。

因此需要两类收据。一类记录相邻指纹、签名和位置;另一类记录实例、租户、操作者、交换渠道与关联依据。“key continuity passed”不能替代“选对了委任对象”。

方向也不能翻转。第一个元素必须是可信授权服务器签发的根。验证器不能跳过坏前缀,在后面寻找一个可用起点。中间的有效签名无法补造缺失的根授权。

“更窄”必须能按语义计算

草案的安全核心是单调性:子令牌不能扩大父链的有效授权。对 scope,可以做集合包含。对 authorization_details,仅看 JSON 形状不够,必须理解各类型对资源、动作、通配符、默认值、数组和省略成员的定义。

一个字段更少的对象可能更宽,因为缺省可能表示“任意”。两个对象的 type 相同,也可能指向不同账户。把字符串相等或字段数量当作 subset,会把扩权包装成简化。

若验证器不能证明包含关系,修订版 03 要求拒绝。这一点与 docs/heng-lu-note.md 的原则一致:共同层只承载可确定、可本地复算的最小规则。未知语义不应交给常设解释者猜测,更不应默认为兼容。

省略本身也会改变授权。子令牌省略 scope 或 authorization_details,该权限组件即消失,后代不得重新引入。省略 aud 则不增加限制,继续沿用父层有效受众;省略 nbf 也不会新增更晚的生效点。任何扩展 claim 都应明确定义能否引入、继承、收窄、删除和重现。

这使路径成为计算的一部分。逐个验证 JWT 再读取叶端字段不够;系统必须从根向下维护有效权限、受众、最早过期、最晚生效和剩余深度。真正的收据是整次遍历和结果。

深度限制路径长度,不等于限制爆炸半径

根必须带有限的 max_delegation_depth。父层有效值为 m 时,孩子显式值只能位于零到 m-1;省略就得到 m-1。零深度可以访问资源,但不能再签孩子。

这只限制一条路径有多少层,不限制兄弟数量。深度二的客户端仍可签大量孩子;每个孩子又可在寿命内做大量请求。一个名字看似很窄的权限,也可能控制不可逆的高价值动作。

草案因此还要求限制令牌数、序列化大小和验证成本。运营方还要限制 fan-out、跨租户密钥复用、任务寿命,并决定如何盘点后代。由于子令牌在本地签发,授权服务器天然看不到完整图谱。

减少中央往返换来了自治,也拿掉了天然审计点。若事故响应需要找出受损父密钥下的每个后代,就必须额外设计发行日志或收据。这些日志提升可观测性,却不会把一条本来扩权的链变成有效链。

同意访问,不等于同意它再任命别人

对涉及资源所有者的 grant,草案要求授权服务器区分“允许访问”与“允许客户端自行转授权”。用户允许 A 读报表,并不自然等于允许 A 再挑选 B 和 C。

同意界面应说明权限、受众、寿命和最大深度,并明确服务器不会逐个观察或批准后续孩子。风险的性质已经改变:委任者获得选择下游相对方的能力。

根令牌可同时带来使用权和有限的任命权。相同私钥既能给当前请求做 DPoP,也能在仍有深度时签孩子。密钥泄露会同时打开两面。签名 API 应区分两种输入,避免原本只用于 HTTP proof 的签名 oracle 被拿来发子令牌。

持有根令牌不能被宣传成资源所有者亲自批准了某个具体 B。所有者批准的是边界与深度;A 选择了 B,这个本地选择需要自己的证据和责任。授权服务器设上限,委任者选相对方并继续收窄,资源服务器在效果点保留最终否决。

精确顺序一直绑定到叶端证明

拟议 DA 凭证用 ~ 把根到叶的 compact JWT 串起来。叶端 DPoP 的 ath 对精确序列化字节求哈希。不能先解码、规范化或重排再计算。

这样可以阻止旁观者在已捕获请求中删、加、换序,而不取得新的叶密钥证明。但它不会自动验证祖先。资源服务器仍要检查根签名、每个孩子签名、每一条 cnf.jkt 连续性、深度、包含、受众与时间。

DPoP 说明叶端控制者用这条确切链授权了本次方法和 URI;它没有替全链授予合法性。既有 BTW RFC 9449 文章保留 sender constraint 机制,本篇拥有的是 DPoP 之前那段祖先授权计算。

客户端签发的孩子没有父标识。当另一个父令牌绑定同一签名密钥,且累计限制足以覆盖孩子时,该孩子可以出现在另一条兼容链中。这是格式的刻意属性。每令牌独立密钥可缩小范围;若业务要求唯一父链,还需额外绑定。

撤销必须传播到真正作决定的地方

草案扩展 RFC 7009:撤销请求提交精确完整链,目标是最后一个令牌。目标绑定密钥的控制者,或孩子的直接签发者,可用适用私钥证明撤销权。普通 OAuth 客户端认证本身不够。

撤销根会使所有分支无效;撤销孩子会使包含它的链及后代无效,但不会自动撤销父、兄弟或绑定同一密钥的其他令牌。

撤销端点的 HTTP 200 不是全球完成证明。它可以对成功和无效输入返回相同状态;更重要的是,成功只把状态记在授权服务器。草案没有定义向离线资源服务器分发撤销状态的机制。

因此要分开“撤销已登记”和“某执行点已看到撤销”。短寿命、introspection 或快速状态分发需要另行选择。离线验证器在更新前仍可能接受祖先。销毁密钥也不证明派生会话已结束或已发生的效果已回滚。

链有效以后,本地政策才开始作最后判断

密码学与单调性都通过后,资源服务器仍要结合资源所有者政策、租户规则、资源现状、撤销与应用条件判断操作。有效链可以被拒绝。这不是异常,而是保持本地未来决定的必要边界。

根是最高允许范围,不是远程命令。每个孩子只能降低上限;资源服务器可以基于此刻掌握的事实进一步拒绝。账户冻结、法律保全、功能关闭或风险升级,无需回写所有祖先才具有权威。

应用效果又是另一张收据。授权通过后,数据库可能冲突、队列可能超时、外部动作可能只完成一半。DPoP replay 控制不是 exactly-once 事务。系统仍需幂等键、commit 证明和事后对账。

收据 最小证据 不能证明
根同意 访问、独立转授权同意、深度 对具体未来孩子的批准
根签发 issuer、哈希、权限、受众、时间、密钥 当前访问
委任对象关联 实例、租户、指纹、渠道 孩子未扩权
孩子签发 父边、孩子哈希、限制、剩余深度 根可信或状态新鲜
全链验证 签名、连续性、包含、时间 叶密钥持有
叶端 DPoP 方法、URI、ath、nonce、replay 祖先有效
状态 各祖先撤销观察与时效 已送达所有执行点
本地授权 政策版本、操作、决定 应用 commit
执行效果 事务键与持久状态 外部结果符合预期
对账 指定观察与异常 下一次行动权限

子令牌可以是真的。只有整条可信、单调、当前且被本地接受的路径,才可能让它拥有行动权。