互联网草案 OAuth 2.1 授权框架 2026 年 3 月
Hardt 等 有效期至 2026 年 9 月 3 日 [页]
工作组:
OAuth 工作组
互联网草案:
draft-ietf-oauth-v2-1-15
发布:
预期状态:
标准轨道
有效期至:
作者:
D. Hardt
Hellō
A. Parecki
Okta
T. Lodderstedt
SPRIND

OAuth 2.1 授权框架

摘要

OAuth 2.1 授权框架使 应用程序能够获得对受保护资源的有限访问权限,这可以通过 代表资源所有者来编排资源所有者与授权服务之间的批准交互 来实现,也可以通过允许该应用程序以其自身名义获得访问权限来实现。本 规范取代并废止了 RFC 6749 中描述的 OAuth 2.0 授权 框架以及 RFC 6750 中的承载令牌用法。

讨论场所

本说明将在发布为 RFC 前移除。

本文档的讨论在 OAuth 工作组邮件列表(oauth@ietf.org)上进行, 该列表的归档位于 https://mailarchive.ietf.org/arch/browse/oauth/

本草案的源代码和议题跟踪器可在 https://github.com/oauth-wg/oauth-v2-1 找到。

本备忘录状态

本互联网草案完全遵循 BCP 78 和 BCP 79 的 规定提交。

互联网草案是互联网工程任务组(IETF)的工作文档。 请注意,其他组织也可以将工作文档作为互联网草案分发。 当前互联网草案的列表位于 https://datatracker.ietf.org/drafts/current/

互联网草案是有效期最长为六个月的草案文档, 可在任何时候由其他文档更新、取代或废止。 将互联网草案用作参考资料,或以“进行中的工作”之外的方式 引用它们,都是不合适的。

本互联网草案将于 2026 年 9 月 3 日到期。

目录

1. 介绍

OAuth 通过将客户端的角色与资源所有者的角色分离, 向客户端-服务器认证模型引入了一个授权层。在 OAuth 中, 客户端请求访问由资源所有者控制并托管在资源服务器上的资源。 客户端不是使用资源所有者的凭据来访问受保护资源, 而是获得一个访问令牌——一种表示特定访问属性集合 (例如范围和生命周期)的凭据。访问令牌由授权服务器 在资源所有者批准后颁发给客户端。客户端使用访问令牌 访问由资源服务器托管的受保护资源。

在较旧且限制更多的客户端-服务器认证模型中, 客户端通过使用资源所有者的凭据向服务器认证, 来请求服务器上的访问受限资源(受保护资源)。 为了向应用程序提供对受限资源的访问,资源所有者会与 应用程序共享其凭据。这会产生若干问题和限制:

OAuth 的一个使用示例是,最终用户(资源所有者)授予财务 管理服务(客户端)访问其存储在银行服务(资源服务器)中的 敏感交易历史记录,而无需与该财务管理服务共享用户名和 密码。相反,用户直接向其金融机构的服务器(授权服务器)进行认证, 该服务器向财务管理服务颁发特定于委派的凭据 (访问令牌)。

这种关注点分离还提供了使用更高级用户认证方法的能力, 例如多因素认证,甚至无密码认证,而无需对应用程序进行任何修改。 由于所有用户认证逻辑都由授权服务器处理,应用程序无需关心 实现任何特定认证机制的具体细节。这使授权服务器能够管理 用户认证策略,并且即使将来更改这些策略,也无需与应用程序 协调变更。

授权层还可以简化资源服务器判断请求是否已获授权的方式。 传统上,在认证客户端之后,每个资源服务器都会在每次 API 调用时 评估策略,以计算客户端是否被授权。在分布式系统中, 策略需要同步到所有资源服务器,或者资源服务器必须调用中央策略 服务器来处理每个请求。在 OAuth 中,策略评估只在授权服务器 创建新的访问令牌时执行。如果已授权访问体现在访问令牌中, 资源服务器就不再需要评估策略,而只需验证访问令牌。 当应用程序代表资源所有者行事,或代表自身行事时, 这种简化都适用。

OAuth 是一种授权协议,而不是认证协议,因为 OAuth 并未定义 实现用户认证所必需的组件。如果目标是认证用户, 则需要一种认证协议。一个示例是 OpenID Connect [OpenID.Connect],它构建在 OAuth 之上,以提供认证协议所需的安全特性和必要组件。

访问令牌表示授予客户端的授权。客户端向专有 API 提交访问令牌, 该 API 返回资源所有者的用户标识符,然后将该 API 的结果作为 认证用户的代理,这是一种常见做法。此做法不属于 OAuth 标准或安全考虑的一部分,也可能并未被资源所有者所考虑。 实现者在采用此做法之前,应仔细查阅资源服务器的文档。

本规范设计用于 HTTP [RFC9110]。在 HTTP 之外的任何协议上使用 OAuth 均不在范围内。

自 OAuth 2.0 授权框架 [RFC6749] 于 2012 年 10 月发布以来,它已经由 OAuth 2.0 for Native Apps [RFC8252]、 OAuth Security Best Current Practice [RFC9700] 以及 OAuth 2.0 for Browser-Based Apps [I-D.ietf-oauth-browser-based-apps] 更新。 OAuth 2.0 Authorization Framework: Bearer Token Usage [RFC6750] 也已通过 [RFC9700] 更新。本 标准轨道规范整合了所有这些文档中的信息,并移除了 [RFC9700] 中发现的不安全功能。

1.1. 角色

OAuth 定义了四个角色:

“资源所有者”:

能够授予对受保护资源访问权限的实体。 当资源所有者是人时,称为最终用户。有时缩写为 “RO”。

“资源服务器”:

托管受保护资源的服务器,能够使用访问令牌接受并响应 受保护资源请求。资源服务器通常可通过 API 访问。 有时缩写为 “RS”。

“客户端”:

代表资源所有者并在其授权下发出受保护资源请求的应用程序。 “客户端”一词并不暗示任何特定的实现特征(例如, 应用程序是在服务器、桌面还是其他设备上执行)。

“授权服务器”:

在成功认证资源所有者并获得授权后,向客户端颁发访问令牌的服务器。 有时缩写为 “AS”。

本规范的大部分内容定义了客户端与授权服务器之间、 以及客户端与资源服务器之间的交互。

授权服务器与资源服务器之间的交互超出本规范范围, 但已经定义了一些扩展,以提供资源服务器与授权服务器之间 互操作性的选项。授权服务器可以与资源服务器是同一台服务器, 也可以是单独的实体。单个授权服务器可以颁发被多个资源服务器 接受的访问令牌。

资源所有者与授权服务器之间的交互 (例如最终用户如何在授权服务器上认证自己) 也超出本规范范围,但有一些例外,例如围绕提示最终用户同意的 安全考虑。

当资源所有者是最终用户时,用户将与客户端交互。 当客户端是基于 Web 的应用程序时,用户将通过用户代理与客户端交互 (如 [RFC9110] 的 第 3.5 节 所述)。 当客户端是原生应用程序时,用户将直接通过操作系统与客户端交互。 有关更多详细信息,请参见 第 2.1 节

1.2. 协议流程

     +--------+                               +---------------+
     |        |--(1)- Authorization Request ->|   Resource    |
     |        |                               |     Owner     |
     |        |<-(2)-- Authorization Grant ---|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(3)-- Authorization Grant -->| Authorization |
     | Client |                               |     Server    |
     |        |<-(4)----- Access Token -------|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(5)----- Access Token ------>|    Resource   |
     |        |                               |     Server    |
     |        |<-(6)--- Protected Resource ---|               |
     +--------+                               +---------------+
图 1抽象协议流程

图 1 中所示的抽象 OAuth 2.1 流程描述了 四个角色之间的交互,并包括以下步骤:

  1. 客户端向资源所有者请求授权。 授权请求可以直接向资源所有者发出(如图所示), 或者最好通过授权服务器作为中介间接发出。

  2. 客户端接收授权授予,这是一种表示资源所有者授权的凭据, 使用本规范中定义的一种授权授予类型来表示, 或使用扩展授予类型来表示。授权授予类型取决于客户端 请求授权所使用的方法,以及授权服务器支持的类型。

  3. 客户端通过向授权服务器认证并提交授权授予来请求访问令牌。

  4. 授权服务器认证客户端并验证授权授予; 如果有效,则颁发访问令牌。

  5. 客户端从资源服务器请求受保护资源, 并通过提交访问令牌进行认证。

  6. 资源服务器验证访问令牌; 如果有效,则为该请求提供服务。

客户端从资源所有者获得授权授予的首选方法 (图中步骤 (1) 和 (2) 所示)是使用授权服务器作为中介, 这在 第 4.1 节图 3 中说明。

1.3. 授权授予

授权授予表示资源所有者的授权 (用于访问其受保护资源),客户端使用该授权来获得访问令牌。 本规范定义了三种授予类型——授权码、刷新令牌和客户端凭据—— 以及一种用于定义其他类型的扩展机制。

1.3.1. 授权码

授权码是一种用于获得访问令牌的临时凭据。 客户端不是直接向资源所有者请求授权,而是将资源所有者 引导到授权服务器(通过其用户代理),授权服务器随后将 资源所有者连同授权码一起引导回客户端。然后客户端可以 用授权码交换访问令牌。

在通过授权码将资源所有者引导回客户端之前, 授权服务器会认证资源所有者,并可能请求资源所有者同意, 或以其他方式告知其客户端的请求。由于资源所有者只向授权服务器 进行认证,资源所有者的凭据永远不会与客户端共享, 并且客户端无需了解任何额外的认证步骤,例如多因素认证或 委派账户。

授权码提供了一些重要的安全优势, 例如认证客户端的能力,以及将访问令牌直接传输给客户端, 而无需让它经过资源所有者的用户代理并可能暴露给其他方, 包括资源所有者。

1.3.2. 刷新令牌

刷新令牌是用于获得访问令牌的凭据。刷新令牌可以由 授权服务器颁发给客户端,并用于在当前访问令牌变为无效或过期时 获得新的访问令牌,或者用于获得具有相同或更窄范围的 其他访问令牌(访问令牌可以比资源所有者授权的生命周期更短、 权限更少)。是否颁发刷新令牌由授权服务器自行决定, 并且可以基于客户端属性、请求属性、授权服务器内的策略或 任何其他条件来颁发。如果授权服务器颁发刷新令牌, 则在颁发访问令牌时包含它(即 图 2 中的步骤 (2))。 刷新令牌的生命周期也由授权服务器自行决定。

刷新令牌是一个字符串,表示资源所有者授予客户端的授权。 该字符串对客户端而言是不透明的。刷新令牌可以是用于检索 授权信息的标识符,也可以将此信息编码到字符串本身中。 与访问令牌不同,刷新令牌仅供授权服务器使用, 永远不会发送给资源服务器。

+--------+                                           +---------------+
|        |--(1)------- Authorization Grant --------->|               |
|        |                                           |               |
|        |<-(2)----------- Access Token -------------|               |
|        |               & Refresh Token             |               |
|        |                                           |               |
|        |                            +----------+   |               |
|        |--(3)---- Access Token ---->|          |   |               |
|        |                            |          |   |               |
|        |<-(4)- Protected Resource --| Resource |   | Authorization |
| Client |                            |  Server  |   |     Server    |
|        |--(5)---- Access Token ---->|          |   |               |
|        |                            |          |   |               |
|        |<-(6)- Invalid Token Error -|          |   |               |
|        |                            +----------+   |               |
|        |                                           |               |
|        |--(7)----------- Refresh Token ----------->|               |
|        |                                           |               |
|        |<-(8)----------- Access Token -------------|               |
+--------+           & Optional Refresh Token        +---------------+
图 2刷新过期的 访问令牌

图 2 中所示流程包括以下步骤:

  1. 客户端通过向授权服务器认证并提交授权授予 来请求访问令牌。

  2. 授权服务器认证客户端并验证授权授予; 如果有效,则颁发访问令牌,并可选地颁发刷新令牌。

  3. 客户端通过提交访问令牌向资源服务器发出 受保护资源请求。

  4. 资源服务器验证访问令牌; 如果有效,则为该请求提供服务。

  5. 步骤 (3) 和 (4) 重复进行,直到访问令牌过期。 如果客户端知道访问令牌已过期,则跳到步骤 (7); 否则,它会发出另一个受保护资源请求。

  6. 由于访问令牌无效,资源服务器返回 无效令牌错误。

  7. 客户端通过提交刷新令牌并在已颁发凭据时 提供客户端认证来请求新的访问令牌。客户端认证要求基于 客户端类型以及授权服务器策略。

  8. 授权服务器认证客户端并验证刷新令牌; 如果有效,则颁发新的访问令牌(并且可选地颁发新的刷新令牌)。

请注意,无需将刷新令牌的生命周期传达给客户端, 因为客户端知道该生命周期后也无法采取任何不同操作。此外, 授权服务器可能选择使用动态生命周期(例如,只要刷新令牌 至少每 7 天使用一次,就延长其过期时间),或者授权服务器可能 因任何原因在计划过期日期之前撤销刷新令牌,例如用户撤销 应用程序访问。因此,客户端本来就必须处理刷新令牌在任意时间 过期的情况。

无论刷新令牌为何或何时过期,客户端都只有一种路径 来获得新令牌,即从头开始新的 OAuth 流程。因此, 没有定义用于向客户端传达刷新令牌过期时间的属性。

1.3.3. 客户端凭据

当授权范围限于客户端控制下的受保护资源, 或限于先前与授权服务器安排好的受保护资源时, 客户端凭据或其他形式的客户端认证 (例如,如 [RFC7523] 及其更新 [I-D.ietf-oauth-rfc7523bis] 中所述, 用于签署 JWT 的私钥) 可以用作授权授予。客户端凭据用于客户端基于先前与授权服务器 安排好的授权请求访问受保护资源的情况。

1.4. 访问令牌

访问令牌是用于访问受保护资源的凭据。访问令牌是一个 表示向客户端颁发的授权的字符串。

该字符串对客户端而言被认为是不透明的,即使它具有结构。 客户端不得期望能够解析访问令牌值。除资源服务器预期的内容外, 授权服务器不需要使用一致的访问令牌编码或格式。

资源所有者授予客户端的访问由授权服务器创建的访问令牌表示。 访问令牌生命周期较短,以降低访问令牌泄露的影响范围。 访问令牌的过期时间由授权服务器设置。

根据授权服务器实现,令牌字符串可以由资源服务器用于检索 授权信息,或者令牌可以以可验证的方式自行包含授权信息 (即由已签名数据载荷组成的令牌字符串)。令牌检索机制的一个示例是 Token Introspection [RFC7662],其中 RS 调用 AS 上的端点来验证客户端提交的令牌。 结构化令牌格式的一个示例是 JWT Profile for Access Tokens [RFC9068], 这是一种将访问令牌数据编码并签名为 JSON Web Token [RFC7519] 的方法。

为了让客户端使用访问令牌,可能需要其他认证凭据, 这些内容超出本规范范围。这通常称为发送方约束的访问令牌, 例如 DPoP [RFC9449] 和 Mutual TLS Certificate-Bound Access Tokens [RFC8705]

访问令牌提供了一个抽象层,将不同的授权构造 (例如用户名和密码)替换为资源服务器理解的单个令牌。 这种抽象支持颁发比用于获得访问令牌的授权授予更具限制性的访问令牌, 同时也消除了资源服务器理解大量认证方法的需要。

访问令牌可以根据资源服务器的安全要求具有不同的格式、 结构和使用方法(例如加密属性)。访问令牌属性以及用于访问 受保护资源的方法可以扩展到本规范描述之外。

访问令牌(以及任何机密访问令牌属性)在传输和存储中必须 保持机密,并且只能在授权服务器、该访问令牌对其有效的资源服务器, 以及被颁发该访问令牌的客户端之间共享。

授权服务器必须确保未经授权方无法生成、修改或猜测出 有效的访问令牌。

1.4.1. 访问令牌范围

访问令牌旨在以低于授予访问权限的用户所拥有权限的 权限颁发给客户端。这称为有限“范围”的访问令牌。 授权服务器和资源服务器可以使用此范围机制来限制特定客户端 可以拥有的资源类型或访问级别。

例如,客户端可能只需要对用户资源的“读取”访问权限, 而不需要更新资源,因此客户端可以请求授权服务器定义的只读范围, 并获得不能用于更新资源的访问令牌。这需要授权服务器、 资源服务器和客户端之间进行协调。授权服务器向客户端提供 请求特定范围的能力,并将这些范围与颁发给客户端的访问令牌 相关联。随后资源服务器负责在收到有限范围访问令牌时执行范围。

OAuth 不定义任何范围值;范围由授权服务器, 或 OAuth 的扩展或配置文件定义。定义范围的一个此类扩展是 [OpenID.Connect],它定义了一组范围, 用于提供对用户个人资料信息的细粒度访问。建议避免定义 与已知扩展中的范围冲突的自定义范围。

为了请求有限范围的访问令牌,客户端根据所使用的 授予类型,在授权端点或令牌端点使用 scope 请求参数。相应地,授权服务器使用 scope 响应参数告知客户端所颁发访问令牌的范围。

scope 参数的值表示为以空格分隔的区分大小写字符串列表。 这些字符串由授权服务器定义。如果该值包含多个以空格分隔的字符串, 它们的顺序无关紧要,并且每个字符串都会向所请求范围添加 一个额外的访问范围。

    scope       = scope-token *( SP scope-token )
    scope-token = 1*( %x21 / %x23-5B / %x5D-7E )

授权服务器可以根据授权服务器策略或资源所有者的指示, 完全或部分忽略客户端请求的范围。如果已颁发访问令牌的范围 与客户端请求的范围不同,授权服务器必须在令牌响应 (第 3.2.3 节) 中包含 scope 响应参数,以告知客户端实际授予的范围。

如果客户端在请求授权时省略 scope 参数, 授权服务器必须要么使用预定义的默认值处理该请求, 要么使请求失败并指示无效范围。授权服务器应记录其范围要求 和默认值(如果已定义)。

1.4.2. 承载令牌

承载令牌是一种安全令牌,其属性是任何持有该令牌的主体 (“承载者”)都可以以任何其他持有该令牌的主体所能使用的方式 使用该令牌。使用承载令牌不要求承载者证明其持有加密密钥材料 (持有证明)。

承载令牌可以通过 DPoP [RFC9449] 和 mTLS [RFC8705] 等持有证明规范进行增强,以提供持有证明特性。

为了防止访问令牌泄露,客户端与资源服务器之间的通信交互 必须按照 第 1.5 节 所述使用机密性和完整性保护。

对承载令牌的特定结构或格式没有要求。如果承载令牌是 对授权信息的引用,则攻击者必须不可行地猜测此类引用, 例如使用足够长的加密随机字符串。如果承载令牌使用编码机制 将授权信息包含在令牌本身中,则访问令牌必须使用足够的完整性保护 来防止令牌被修改。JSON Web Token Profile for Access Tokens [RFC9068] 中描述了访问令牌的一种 编码和签名机制示例。

1.4.3. 发送方约束的访问令牌

发送方约束的访问令牌将访问令牌的使用绑定到特定发送方。 该发送方有义务证明其知道某个秘密,作为接收方 (例如资源服务器)接受该访问令牌的前提条件。

授权服务器和资源服务器应使用用于发送方约束访问令牌的机制, 例如 OAuth Demonstration of Proof of Possession (DPoP) [RFC9449] 或 Mutual TLS for OAuth 2.0 [RFC8705]。 请参见 [RFC9700] 的 第 4.10.1 节,以防止被窃取和泄露的 访问令牌被滥用。

建议在客户端和资源服务器之间使用端到端 TLS。 如果 TLS 流量需要在中介处终止,请参阅 [RFC9700] 的 第 4.13 节 以获得进一步安全建议。

1.5. 通信安全

实现必须使用一种机制来提供通信认证、完整性和机密性, 例如 Transport-Layer Security [RFC8446], 以保护内容或标头字段中的明文凭据和令牌交换,防止窃听造成重放 (例如,参见 第 2.4.1 节第 7.5.1 节第 3.2 节第 1.4.2 节)。

所有 OAuth 协议 URL(由 AS、RS 和客户端暴露的 URL) 都必须使用 https 方案,回环接口重定向 URI 除外, 后者可以使用 http 方案。 使用 https 时,必须按照 [RFC9110] 的 第 4.3.4 节 检查 TLS 证书。 在撰写本文时,TLS 版本 1.3 [RFC8446] 是最新版本。

实现也可以支持满足其安全要求的其他传输层安全机制。

TLS 版本和算法的确定超出本规范范围。 有关传输层安全的最新建议,请参阅 [BCP195], 有关证书验证和其他安全考虑,请参阅相关规范。

1.6. HTTP 重定向

本规范广泛使用 HTTP 重定向,其中客户端或授权服务器将 资源所有者的用户代理引导到另一个目的地。虽然本规范中的示例 展示了 HTTP 302 状态码的使用,但除了 HTTP 307 之外, 允许使用通过用户代理可用的任何其他方法来完成此重定向, 并且这被认为是实现细节。有关详细信息,请参见 第 7.5.3 节

1.7. 互操作性

OAuth 2.1 提供了一个具有明确定义安全属性的丰富授权框架。

本规范将若干必需组件部分或完全未定义 (例如客户端注册、授权服务器能力、端点发现)。 其中一些行为在可选扩展中定义,实现可以选择使用,例如:

  • [RFC8414]: Authorization Server Metadata,定义客户端可用于查找与特定 OAuth 服务器 交互所需信息的端点

  • [RFC7591]: Dynamic Client Registration,提供一种以编程方式向授权服务器 注册客户端的机制

  • [RFC7592]: Dynamic Client Management,提供一种更新动态注册客户端信息的机制

  • [RFC7662]: Token Introspection,定义一种机制,供资源服务器获取有关访问令牌的信息

请参阅 附录 D,其中列出了截至本发布时已知的当前扩展。

1.8. 与 OAuth 2.0 的兼容性

OAuth 2.1 与 OAuth 2.0 兼容,同时应用了来自已知最佳当前实践的 扩展和限制。具体而言,OAuth 2.0 核心中未指定的功能 (例如 PKCE)在 OAuth 2.1 中是必需的。此外,OAuth 2.0 中可用的一些功能 (例如隐式授予或资源所有者凭据授予类型)未在 OAuth 2.1 中指定。 另外,OAuth 2.0 中允许的一些行为在 OAuth 2.1 中受到限制, 例如 OAuth 2.1 要求的重定向 URI 严格字符串匹配。

有关与 OAuth 2.0 差异的更多详细信息,请参见 第 10 节

1.9. 符号约定

本文档中的关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“NOT RECOMMENDED”、 “MAY” 和 “OPTIONAL” 按照 BCP 14 [RFC2119] [RFC8174] 中的描述解释, 但仅当它们以全大写形式出现时才如此,如此处所示。

本规范使用 [RFC5234] 中的扩展巴科斯-诺尔范式(ABNF)表示法。此外,规则 URI-reference 引自 “Uniform Resource Identifier (URI): Generic Syntax” [RFC3986]

某些与安全相关的术语应按 [RFC4949] 中定义的含义理解。 这些术语包括但不限于:“attack”、“authentication”、“authorization”、“certificate”、 “confidentiality”、“credential”、“encryption”、“identity”、“sign”、 “signature”、“trust”、“validate” 和 “verify”。

术语 “content” 应按 [RFC9110] 的 第 6.4 节 中的描述解释。

术语 “user agent” 应按 [RFC9110] 的 第 3.5 节 中的描述解释。

除非另有说明,所有协议参数名称和值均区分大小写。

2. 客户端注册

在启动协议之前,客户端必须已在授权服务器处建立一个标识符 (第 2.2 节)。 客户端标识符与授权服务器建立的方式超出本规范范围, 但通常涉及客户端开发者在授权服务器的网站上手动注册客户端 (在创建账户并同意该服务的服务条款之后),或者使用 Dynamic Client Registration [RFC7591]。 扩展也可以定义其他用于建立客户端注册的编程方法。

客户端注册不要求客户端与授权服务器直接交互。 在授权服务器支持时,注册可以依赖其他方式来建立信任并获得所需的 客户端属性(例如重定向 URI、客户端类型)。例如,可以使用自颁发或 第三方颁发的断言完成注册,或者由授权服务器通过可信通道执行客户端发现。

客户端注册必须包括:

Dynamic Client Registration [RFC7591] 定义了一个通用客户端数据模型, 即使在手动客户端注册中也可以使用。

2.1. 客户端类型

OAuth 2.1 根据客户端是否能够安全地向授权服务器认证, 定义了两种客户端类型。

“机密”:

具有 AS 凭据的客户端被指定为 “机密客户端”

“公开”:

没有凭据的客户端称为“公开客户端”

任何具有凭据的客户端都必须采取预防措施,防止其凭据泄露和滥用。

客户端认证允许授权服务器确保其在 OAuth 流程中 与某个特定客户端(由其 client_id 标识)进行交互。 授权服务器可能会基于其对确实正在与合法客户端通信的信心, 对诸如是在每次授权时提示用户同意还是仅第一次提示等事项作出策略决策。

授权服务器是否以及如何验证客户端身份或提供/运营此客户端的 参与方身份,超出本规范范围。授权服务器在决定是否允许客户端 访问更敏感的资源和操作(例如客户端凭据授予类型)以及多频繁地提示用户同意时, 应考虑其对客户端身份的信心水平。

不要求授权服务器支持特定客户端类型。

单个 client_id 不应被视为超过一种客户端类型。

本规范围绕以下客户端配置文件设计:

“Web 应用程序”:

Web 应用程序是在 Web 服务器上运行的客户端。 资源所有者通过在其设备上的用户代理中渲染的 HTML 用户界面 访问该客户端。客户端凭据以及颁发给客户端的任何访问令牌 都存储在 Web 服务器上,不会暴露给资源所有者,也无法由其访问。

“基于浏览器的应用程序”:

基于浏览器的应用程序是一种客户端,其客户端代码 从 Web 服务器下载,并在资源所有者所用设备上的用户代理 (例如 Web 浏览器)内执行。协议数据和凭据很容易被资源所有者访问 (并且通常可见)。如果此类应用程序希望使用客户端凭据, 建议使用 backend for frontend 模式。由于此类应用程序驻留在用户代理中, 因此它们在请求授权时可以无缝利用用户代理能力。

“原生应用程序”:

原生应用程序是安装并执行在资源所有者所用设备上的客户端。 协议数据和凭据可被资源所有者访问。假定应用程序中包含的任何 客户端认证凭据都可以被提取。动态颁发的访问令牌和刷新令牌 可以获得可接受级别的保护。在某些平台上,这些凭据受到保护, 不会被同一设备上的其他应用程序访问。如果此类应用程序希望使用 客户端凭据,建议使用 backend for frontend 模式,或者在运行时使用 Dynamic Client Registration [RFC7591] 颁发凭据。

2.2. 客户端标识符

每个客户端都在授权服务器的上下文中由客户端标识符标识—— 这是一个表示客户端提供的注册信息的唯一字符串。虽然授权服务器通常 自行颁发客户端标识符,但它也可以服务由授权服务器之外的一方 创建客户端标识符的客户端。客户端标识符不是秘密;它会暴露给 资源所有者,并且不得单独用于客户端认证。客户端标识符在 授权服务器的上下文中是唯一的。

客户端标识符是一个不透明字符串,其大小由本规范保持未定义。 客户端应避免对标识符大小作出假设。授权服务器应记录其颁发的 任何标识符的大小。

如果授权服务器支持由授权服务器之外的参与方颁发客户端标识符的 客户端,则授权服务器应采取预防措施,以避免客户端冒充资源所有者, 如 第 7.4 节 所述。

2.3. 客户端重定向 端点

客户端重定向端点(也称为“重定向端点”) 是客户端的 URI,授权服务器在完成与资源所有者的交互后, 将用户代理重定向回该 URI。

授权服务器将用户代理重定向到客户端在客户端注册过程中 先前与授权服务器建立的一个重定向端点。

重定向 URI 必须是 [RFC3986] 的 第 4.3 节 所定义的绝对 URI。重定向 URI 可以包含查询字符串组件 (附录 C.1),在添加额外查询参数时必须保留该组件。 重定向 URI 不得包含片段组件。

2.3.1. 注册 要求

授权服务器必须要求客户端注册其完整的重定向 URI (包括路径组件)。授权服务器必须拒绝指定了与已注册 URI 不完全匹配的重定向 URI 的授权请求;回环重定向除外, 其中除端口 URI 组件外仍要求精确匹配,详见 第 4.1.1 节

授权服务器可以允许客户端注册多个重定向 URI。

注册可以带外发生,例如在授权服务器配置客户端信息的手动步骤; 也可以在运行时发生,例如在 Pushed Authorization Requests [RFC9126] 中的初始 POST。

对于基于私用 URI 方案的重定向 URI, 授权服务器应强制执行 第 8.4.3 节 中的要求,即客户端使用 基于反向域名的方案。至少,应拒绝任何不包含句点字符 (.)的私用 URI 方案。

除了抗冲突属性之外,这还有助于在两个应用声称相同私用 URI 方案(一方恶意行事)的争议中证明所有权。 例如,如果两个应用都声称 com.example.appexample.com 的所有者可以请求应用商店运营者移除 假冒应用。如果使用的是通用 URI 方案,则此类请求更难证明。

客户端不得暴露会将用户浏览器转发到从查询参数获得的任意 URI 的 URL(“开放重定向器”),如 第 7.12 节 所述。 开放重定向器可能导致授权码和访问令牌被外泄。

如果需要,客户端可以使用 state 请求参数实现 每个请求的自定义,而不是逐请求改变重定向 URI。

如果不要求注册重定向 URI,攻击者可以将授权端点用作 开放重定向器,如 第 7.12 节 所述。

2.3.2. 多个重定向 URI

如果客户端已注册多个重定向 URI, 则客户端必须使用 redirect_uri 请求参数 (第 4.1.1 节) 在授权请求中包含一个重定向 URI。 如果客户端只注册了单个重定向 URI,则 redirect_uri 请求参数是可选的。

2.3.3. 防止 CSRF 攻击

客户端必须防止跨站请求伪造(CSRF)攻击。 在此上下文中,CSRF 指的是并非源自授权服务器, 而是源自恶意第三方的对重定向端点的请求 (有关详细信息,请参见 [RFC6819] 的 第 4.4.1.8 节)。 已确认授权服务器支持 code_challenge 参数的客户端 可以依赖该机制提供的 CSRF 保护。在 OpenID Connect 流程中, 验证 nonce 参数会提供 CSRF 保护。否则, 必须使用 state 参数中携带并安全绑定到用户代理的一次性 CSRF 令牌进行 CSRF 保护(参见 第 7.9 节)。

2.3.4. 防止 混淆攻击

当 OAuth 客户端只能与一个授权服务器交互时,不需要混淆防御。 然而,在 OAuth 客户端与两个或更多授权服务器交互的场景中, 客户端必须防止混淆攻击。为了防止混淆攻击,客户端必须只处理 来自其向之发送相应请求的颁发者的重定向响应,并且该响应必须来自 发起该授权请求时使用的同一用户代理。

有关两种不同混淆攻击防御的详细说明,请参见 第 7.14 节

2.3.5. 无效端点

如果授权请求因缺失、无效或不匹配的重定向 URI 而验证失败, 授权服务器应告知资源所有者该错误,并且不得自动将用户代理重定向到 无效的重定向 URI。

2.3.6. 端点内容

到客户端端点的重定向请求通常会产生一个由用户代理处理的 HTML 文档响应。如果 HTML 响应直接作为重定向请求的结果提供, 则 HTML 文档中包含的任何脚本都将以对重定向 URI 及其包含的 工件(例如授权码)的完全访问权限执行。此外,包含授权码的请求 URL 可能会在 HTTP Referer 标头中发送给页面中加载的任何嵌入图像、 样式表和其他元素。

客户端不应在重定向 URI 端点响应中包含任何第三方脚本 (例如第三方分析、社交插件、广告网络)。相反,它应从 URI 中提取工件,并再次将用户代理重定向到另一个端点, 且不暴露这些工件(无论是在 URI 中还是其他位置)。 如果包含第三方脚本,客户端必须确保自己的脚本 (用于从 URI 提取并移除凭据)首先执行。

2.4. 客户端认证

只有在底层凭据的签发/注册和分发过程确保其机密性的情况下, 授权服务器才必须依赖客户端认证。

对于机密客户端,授权服务器可以接受任何满足其安全要求的 客户端认证形式(例如客户端密钥、公钥/私钥对)。

建议使用非对称(基于公钥)方法进行客户端认证, 例如 mTLS [RFC8705],或按照 [RFC7521][RFC7523] 及其更新 [I-D.ietf-oauth-rfc7523bis] 使用已签名 JWT(“Private Key JWT”) (在 [OpenID.Connect] 中定义为 private_key_jwt 客户端认证方法)。 使用此类客户端认证方法时,授权服务器无需存储敏感的对称密钥, 这使这些方法对多种攻击更为稳健,并使客户端能够管理自己的密钥和 密钥轮换。

使用基于 JWT 的客户端认证时,客户端和授权服务器必须遵循 [I-D.ietf-oauth-rfc7523bis] 中关于 aud 值的更新指导。

当无法进行客户端认证时,授权服务器应采用其他方式来验证 客户端身份——例如要求注册客户端重定向 URI,或让资源所有者确认身份。 在请求资源所有者授权时,有效的重定向 URI 不足以验证客户端身份, 但可用于在获得资源所有者授权后,防止将凭据交付给假冒客户端。

客户端在每个请求中不得使用超过一种认证方法, 以防止关于哪个认证机制对请求具有权威性的冲突。

授权服务器必须考虑与未经认证客户端交互的安全影响, 并采取措施限制颁发给此类客户端的令牌的潜在暴露 (例如限制刷新令牌的生命周期)。

授权服务器与特定客户端身份关联的权限,必须取决于其对 客户端标识和客户端凭据生命周期管理整体过程的评估。 有关其他详细信息,请参见 第 7.2 节

2.4.1. 客户端密钥

为了支持持有客户端密钥的机密客户端, 授权服务器必须支持客户端使用以下参数在请求正文内容中包含 客户端凭据:

"client_id":

必需。注册过程中颁发给客户端的客户端标识符, 如 第 2.2 节 所述。

"client_secret":

必需。客户端密钥。

这些参数只能在请求内容中传输,并且不得包含在请求 URI 中。

这也称为 client_secret_post, 如 [RFC7591] 的 第 2 节 中所定义。

例如,使用内容参数刷新访问令牌 (第 4.3 节)的请求 (额外换行仅用于显示目的):

POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
&client_id=s6BhdRkqt3&client_secret=7Fjfp0ZBr1KtDRbnfVdmIw

授权服务器可以支持 HTTP Basic 认证方案, 用于认证已颁发客户端密钥的客户端。

当使用 [RFC9110] 的 第 11 节 所定义的 HTTP Basic 认证方案向授权服务器认证时, 客户端标识符使用 附录 B 中的 application/x-www-form-urlencoded 编码算法进行编码, 并将编码后的值用作用户名;客户端密钥使用相同算法编码并用作密码。

这也称为 client_secret_basic, 如 [RFC7591] 的 第 2 节 中所定义。

例如(额外换行仅用于显示目的):

Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3

注意:这种先对客户端标识符和密钥进行表单编码, 然后将编码后的值用作 HTTP Basic 认证用户名和密码的方法, 过去导致了许多互操作性问题。一些实现遗漏了编码步骤, 或决定只编码某些字符,或在验证凭据时忽略编码要求, 导致客户端不得不为其向各个授权服务器提交凭据的方式进行特殊处理。 在请求正文内容中包含凭据可以避免编码问题,并产生更具互操作性的实现。

由于客户端密钥认证方法涉及密码, 授权服务器必须保护任何使用该方法的端点免受暴力破解攻击。

2.4.2. 其他 认证方法

授权服务器可以支持任何符合其安全要求的合适认证方案。 使用其他认证方法时,授权服务器必须定义客户端标识符 (注册记录)与认证方案之间的映射。

一些额外认证方法,例如 mTLS [RFC8705] 和 Private Key JWT([RFC7523][I-D.ietf-oauth-rfc7523bis]) 定义在 “OAuth Token Endpoint Authentication Methods” 注册表中, 并且作为通用客户端认证方法可能很有用,而不仅限于保护令牌端点的特定用途。

2.5. 未注册客户端

本规范不要求客户端必须向授权服务器注册。 但是,未注册客户端的使用超出本规范范围,并且需要额外的安全分析以及 对其互操作性影响的审查。

3. 协议端点

授权过程使用两个授权服务器端点(HTTP 资源):

以及一个客户端端点:

并非每种授权授予类型都会使用两个端点。 扩展授予类型可以根据需要定义其他端点。

3.1. 授权端点

授权端点用于与资源所有者交互并获得授权授予。 授权服务器必须首先认证资源所有者。授权服务器认证资源所有者的方式 (例如用户名和密码登录、passkey、联合登录,或使用已建立的会话) 超出本规范范围。

客户端获得授权端点 URL 的方式超出本规范范围, 但该 URL 通常在服务文档中提供,或在授权服务器的元数据文档 [RFC8414] 中提供。

授权端点 URL 不得包含片段组件,并且可以包含查询字符串组件 附录 C.1, 在添加额外查询参数时必须保留该组件。

授权服务器必须支持对授权端点使用 HTTP GET 方法 [RFC9110] 的 第 9.3.1 节,并且也可以支持 POST 方法([RFC9110] 的 第 9.3.3 节)。

授权服务器必须忽略发送到授权端点的未识别请求参数。

本规范定义的请求和响应参数不得包含超过一次。 此要求也适用于扩展定义的参数,除非该扩展对特定参数明确另有定义。 发送时没有值的参数必须被视为已从请求中省略。

重定向可能包含用户凭据的请求的授权服务器, 必须避免意外转发这些用户凭据(详见 第 7.5.3 节)。

Authorization Endpoint 不得支持 Cross-Origin Resource Sharing [WHATWG.CORS],因为客户端不会直接访问此端点, 而是将用户代理重定向到该端点。

3.2. 令牌端点

令牌端点由客户端用于使用授予获得访问令牌, 例如 第 4 节第 4.3 节 中描述的授予。

客户端获得令牌端点 URL 的方式超出本规范范围, 但该 URL 通常在服务文档中提供并在客户端开发期间配置, 或在授权服务器的元数据文档 [RFC8414] 中提供并在运行时以编程方式获取。

令牌端点 URL 不得包含片段组件, 并且可以包含查询字符串组件 附录 C.1

客户端向令牌端点发出请求时必须使用 HTTP POST 方法。

授权服务器必须忽略发送到令牌端点的未识别请求参数。

发送时没有值的参数必须被视为已从请求中省略。本规范定义的 请求和响应参数不得包含超过一次。此要求也适用于扩展定义的参数, 除非该扩展对特定参数明确另有定义。

希望支持基于浏览器应用程序的授权服务器 (例如完全在客户端 JavaScript 中运行且无法访问支持性后端服务器的应用程序) 需要确保令牌端点支持必要的 CORS [WHATWG.CORS] 标头,以允许应用程序看到响应。 如果授权服务器向应用程序提供其他端点,例如元数据 URL、 动态客户端注册、撤销、自省、发现或用户信息端点, 这些端点也可能被基于浏览器的应用程序访问,并且也需要定义 CORS 标头以允许访问。 有关更多详细信息,请参见 [I-D.ietf-oauth-browser-based-apps]

3.2.1. 客户端 认证

机密客户端在向令牌端点发出请求时, 必须按照 第 2.4 节 所述向授权服务器认证。

客户端认证用于:

  • 强制刷新令牌和授权码与其被颁发给的客户端绑定。 当授权码通过不安全通道传输到重定向端点时, 客户端认证会添加一层额外安全性。

  • 通过禁用客户端或更改其凭据, 从客户端被攻破中恢复,从而防止攻击者滥用被窃取的刷新令牌。 更改单组客户端凭据明显快于撤销整组刷新令牌。

  • 实施认证管理最佳实践, 这些实践要求定期轮换凭据。轮换整组刷新令牌可能很有挑战, 而轮换单组客户端凭据则明显更容易。

3.2.2. 令牌端点 请求

客户端通过在 HTTP 请求内容中使用 UTF-8 字符编码, 按 附录 C.2 的表单编码序列化格式发送以下参数,向令牌端点发出请求:

"grant_type":

必需。客户端在特定令牌请求中使用的授予类型标识符。 本规范定义了值 authorization_coderefresh_tokenclient_credentials。 授予类型决定令牌请求所需或支持的其他参数。 这些授予类型的详细信息在下文定义。

"client_id":

可选。当使用依赖该参数的客户端认证形式, 或 grant_type 要求标识公开客户端时,需要客户端标识符。

机密客户端必须按照 第 3.2.1 节 所述向授权服务器认证。

例如,客户端发出以下 HTTPS 请求 (额外换行仅用于显示目的):

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
&code_verifier=3641a2d12d66101249cdf7a79c000c1f8c05d2aafcf14bf146497bed

授权服务器必须:

  • 要求机密客户端 (或具有其他认证要求的客户端)进行客户端认证,

  • 如果包含客户端认证,则认证客户端

进一步的特定授予类型处理规则适用,并在相应授予类型中指定。

3.2.3. 令牌端点 响应

如果访问令牌请求有效并已获授权, 授权服务器会颁发访问令牌和可选的刷新令牌。

如果客户端认证失败或无效, 授权服务器返回如 第 3.2.4 节 所述的错误响应。

授权服务器通过按照 附录 C.3 创建 HTTP 响应来颁发访问令牌 和可选刷新令牌,使用 [RFC8259] 定义的 application/json 媒体类型,并带有以下参数和 HTTP 200 (OK) 状态码:

"access_token":

必需。授权服务器颁发的访问令牌。

"token_type":

必需。所颁发访问令牌的类型,如 第 1.4 节 所述。值不区分大小写。

"expires_in":

建议。一个 JSON 数字,表示访问令牌的生命周期 (以秒为单位)。例如,值 3600 表示访问令牌将在 生成响应后一小时过期。如果省略,授权服务器应通过其他方式提供 生命周期,或记录默认值。请注意,授权服务器可以提前使访问令牌过期, 客户端不得期望访问令牌在所提供的生命周期内一直有效。

"scope":

如果与客户端请求的范围相同,则建议; 否则为必需。访问令牌的范围,如 第 1.4.1 节 所述。

"refresh_token":

可选。刷新令牌,可用于基于相应令牌请求中 传入的授予来获得新的访问令牌。

授权服务器应根据风险评估和自身策略决定是否向特定客户端 颁发刷新令牌。如果授权服务器决定不颁发刷新令牌, 客户端可以通过重新开始 OAuth 流程来获得新的访问令牌, 例如发起新的授权码请求。在这种情况下,授权服务器可以使用 cookie 和持久授权来优化用户体验。

如果颁发刷新令牌,这些刷新令牌必须绑定到资源所有者同意的 范围和资源服务器。这样做是为了防止合法客户端提升权限, 并降低刷新令牌泄露的影响。

这些参数按照 附录 C.3 的描述序列化为 JavaScript Object Notation (JSON) 结构。

授权服务器必须在任何包含令牌、凭据或其他敏感信息的响应中 包含 HTTP Cache-Control 响应标头字段 (参见 [RFC9111] 的 第 5.2 节),其值为 no-store

例如:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{
  "access_token": "2YotnFZFEjr1zCsicMWpAA",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter": "example_value"
}

客户端必须忽略响应中未识别的值名称。 从授权服务器接收的令牌和其他值的大小保持未定义。 客户端应避免对值大小作出假设。授权服务器应记录其颁发的任何值的大小。

3.2.4. 令牌端点 错误响应

授权服务器以 HTTP 400 (Bad Request) 状态码响应 (除非另有指定),并在响应中包含以下参数:

"error":

必需。来自以下内容的单个 ASCII [USASCII] 错误代码:

"invalid_request":

请求缺少必需参数, 包含不受支持的参数值(授予类型除外), 重复参数,包含多个凭据,使用超过一种机制认证客户端, 在授权请求中未发送 code_challenge 的情况下包含 code_verifier,或在其他方面格式错误。

"invalid_client":

客户端认证失败(例如未知客户端、 未包含客户端认证,或不受支持的认证方法)。 授权服务器可以返回 HTTP 401 (Unauthorized) 状态码, 以指示支持哪些 HTTP 认证方案。如果客户端尝试通过 Authorization 请求标头字段进行认证, 授权服务器必须以 HTTP 401 (Unauthorized) 状态码响应, 并包含与客户端所用认证方案匹配的 WWW-Authenticate 响应标头字段。

"invalid_grant":

所提供的授权授予 (例如授权码、资源所有者凭据)或刷新令牌无效、 已过期、已撤销,与授权请求中使用的重定向 URI 不匹配, 或已颁发给另一个客户端。

"unauthorized_client":

经认证的客户端未被授权使用此授权授予类型。

"unsupported_grant_type":

授权服务器不支持该授权授予类型。

"invalid_scope":

所请求范围无效、未知、格式错误, 或超出资源所有者授予的范围。

error 参数的值不得包含 集合 %x20-21 / %x23-5B / %x5D-7E 之外的字符。

"error_description":

可选。人类可读的 ASCII [USASCII] 文本, 提供额外信息,用于帮助客户端开发者理解发生的错误。 error_description 参数的值不得包含 集合 %x20-21 / %x23-5B / %x5D-7E 之外的字符。

"error_uri":

可选。标识一个包含错误相关信息的人类可读网页的 URI, 用于向客户端开发者提供关于该错误的额外信息。 error_uri 参数的值必须符合 URI-reference 语法, 因此不得包含集合 %x21 / %x23-5B / %x5D-7E 之外的字符。

这些参数使用 application/json 媒体类型包含在 HTTP 响应内容中,如 附录 C.3 所定义。

例如:

HTTP/1.1 400 Bad Request
Content-Type: application/json
Cache-Control: no-store

{
 "error": "invalid_request"
}

4. 授予类型

为了请求访问令牌,客户端从资源所有者获得授权。 本规范定义了以下授权授予类型:

它还提供了一种用于定义其他授予类型的扩展机制。

4.1. 授权码 授予

授权码授予类型用于同时获得访问令牌 和刷新令牌。

该授予类型使用额外的授权端点,让 授权服务器 与资源所有者交互,以获得资源访问的同意。

由于这是一个基于重定向的流程,客户端必须能够 通过资源所有者的用户代理(通常是 Web 浏览器)启动该流程,并且能够从授权服务器被重定向回来。

 +----------+
 | Resource |
 |   Owner  |
 +----------+
       ^
       |
       |
 +-----|----+          Client Identifier      +---------------+
 | .---+---------(1)-- & Redirect URI ------->|               |
 | |   |    |                                 |               |
 | |   '---------(2)-- User authenticates --->|               |
 | | User-  |                                 | Authorization |
 | | Agent  |                                 |     Server    |
 | |        |                                 |               |
 | |    .--------(3)-- Authorization Code ---<|               |
 +-|----|---+                                 +---------------+
   |    |                                         ^      v
   |    |                                         |      |
   ^    v                                         |      |
 +---------+                                      |      |
 |         |>---(4)-- Authorization Code ---------'      |
 |  Client |          & Redirect URI                     |
 |         |                                             |
 |         |<---(5)----- Access Token -------------------'
 +---------+       (w/ Optional Refresh Token)
图 3授权码流程

图 3 中所示的流程包括以下步骤:

(1) 客户端通过将资源所有者的 用户代理引导到授权端点来启动流程。客户端包含 其客户端标识符、代码挑战(由生成的代码验证器派生)、 可选的请求范围、可选的本地状态,以及一个 重定向 URI;一旦访问被授予(或拒绝),授权服务器会将 用户代理发送回该 URI。

(2) 授权服务器(通过 用户代理)认证资源所有者,并确定资源所有者 是授予还是拒绝客户端的访问请求。

(3) 假设资源所有者授予访问权限, 授权服务器使用之前提供的重定向 URI(在请求中或 客户端注册期间提供)将用户代理重定向回客户端。 该重定向 URI 包含一个授权码以及客户端 先前提供的任何本地状态。

(4) 客户端通过包含上一步接收的授权码, 并包含其代码验证器,从授权服务器的令牌端点请求 访问令牌。发出请求时,如果能够认证, 客户端会向授权服务器进行认证。客户端包含用于获得授权码的 重定向 URI,以便进行验证。

(5) 授权服务器在可能时认证客户端, 验证 授权码,验证代码验证器,并确保收到的重定向 URI 与步骤 (3) 中用于将用户代理重定向到客户端的 URI 匹配。 如果有效,授权服务器会响应访问令牌,并可选地响应刷新令牌。

4.1.1. 授权 请求

为了开始授权请求,客户端通过向授权服务器的 授权端点 URI 添加参数来构建 授权请求 URI。客户端最终会将用户代理重定向 到此 URI 以启动请求。

客户端在每个授权请求中使用一个唯一秘密, 称为“代码验证器”,以防止授权码注入和 CSRF 攻击。 客户端首先生成代码验证器,然后派生“代码挑战”并将其包含在 授权请求中。客户端在令牌端点交换授权码时使用代码验证器, 以证明使用该授权码的客户端与请求该授权码的客户端相同。

客户端按照 附录 C.1 的描述, 将以下参数添加到授权端点 URI 的查询组件中, 从而构造请求 URI:

"response_type":

必需。授权端点支持不同 请求和响应参数集合。客户端通过使用某个 response_type 值来确定流程类型。本规范定义了值 code, 该值必须用于表示 客户端希望使用授权码流程。

扩展响应类型可以包含以空格分隔(%x20)的 值列表,其中值的顺序无关紧要(例如,响应 类型 a bb a 相同)。此类组合 响应类型的含义由其各自的规范定义。

一些扩展响应类型由 [OpenID.Connect] 定义。

如果授权请求缺少 response_type 参数, 或者响应类型无法理解,则授权服务器 必须按照 第 4.1.2.1 节 的描述返回错误响应。

"client_id":

必需。如 第 2.2 节 所述的客户端标识符。

"code_challenge":

除非满足 第 7.5.1 节 的特定要求, 否则为必需。由代码验证器派生的代码挑战。

"code_challenge_method":

可选,如果请求中不存在则默认为 plain。 代码验证器转换方法为 S256plain

"redirect_uri":

如果此客户端仅注册了一个重定向 URI,则可选。 如果此客户端注册了多个重定向 URI,则必需。 请参见 第 2.3.2 节

"scope":

可选。如 第 1.4.1 节 所述的访问请求范围。

"state":

可选。客户端用于在请求和回调之间 维护状态的不透明值。授权服务器在将用户代理 重定向回客户端时包含此值。

code_verifier 是针对每个 授权请求生成的唯一高熵加密随机字符串, 使用未保留字符 [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~", 最小长度为 43 个字符,最大长度为 128 个字符。

客户端临时存储 code_verifier, 并计算其在授权请求中使用的 code_challenge

code_verifier 的 ABNF 如下。

code-verifier = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39

客户端应使用不会在授权请求中 暴露 code_verifier 的代码挑战方法。 否则,能够读取授权请求的攻击者(参见 [RFC9700] 中的攻击者 A4) 可以破坏该机制提供的安全性。目前,S256 是唯一这样的 方法。

注意:代码验证器应具有足够的熵,使其值 不切实际地难以猜测。建议使用合适随机数生成器的输出 来创建一个 32 八位字节序列。然后对该八位字节序列进行 base64url 编码,以生成一个 43 八位字节的 URL 安全字符串 作为代码验证器。

然后客户端使用以下转换之一,根据代码 验证器创建派生自该代码验证器的 code_challenge

S256
  code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

plain
  code_challenge = code_verifier

如果客户端能够使用 S256,则它必须使用 S256,因为 S256 是服务器必须实现(MTI)的。仅当客户端因某些 技术原因无法支持 S256,例如没有可用哈希函数的受限环境, 并且通过带外配置或通过 Authorization Server Metadata [RFC8414] 知道服务器支持 plain 时,才允许客户端使用 plain

code_challenge 的 ABNF 如下。

code-challenge = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39

属性 code_challengecode_verifier 采用自 OAuth 2.0 扩展 “Proof-Key for Code Exchange”,即 PKCE [RFC7636],该技术最初在其中开发。

授权服务器必须支持 code_challengecode_verifier 参数。

客户端必须使用 code_challengecode_verifier,并且 授权服务器必须强制使用它们,除非满足 第 7.5.1 节 中描述的条件。即使在这种情况下,仍然建议 按上述方式使用并强制执行 code_challengecode_verifier

statescope 参数不应 以明文形式包含敏感的 客户端或资源所有者信息,因为它们可能通过不安全通道传输或 被不安全地存储。

客户端使用 HTTP 重定向或通过用户代理 可用的其他方式,将资源所有者引导到构造的 URI。

例如,客户端引导用户代理发出以下 HTTPS 请求(额外换行仅用于显示目的):

GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
    &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
    &code_challenge=6fdkQaPm51l13DSukcAH3Mdx7_ntecHYd1vi3n0hMZY
    &code_challenge_method=S256 HTTP/1.1
Host: server.example.com

授权服务器验证该请求,以确保所有 必需参数都存在且有效。

特别是,如果请求中存在 redirect_uri,授权服务器必须验证它, 确保其与客户端注册期间先前建立的已注册 重定向 URI 之一匹配(第 2 节)。 在比较这两个 URI 时,授权服务器必须确保 两个 URI 相等;有关详细信息,请参见 [RFC3986] 的 第 6.2.1 节,简单字符串比较。 唯一例外是使用 localhost URI 的原生应用:在这种情况下, 授权服务器必须允许可变端口号,如 [RFC8252] 的 第 7.3 节 所述。

如果请求有效, 授权服务器会认证资源所有者,并获得 授权决定(通过询问资源所有者或通过 其他方式建立批准)。

当决定建立后,授权服务器使用 HTTP 重定向响应,或通过用户代理可用的其他方式, 将用户代理引导到提供的客户端重定向 URI。

4.1.2. 授权 响应

如果资源所有者授予访问请求,授权服务器 会颁发授权码,并通过使用 附录 C.1 中描述的查询字符串序列化,将以下参数添加到 重定向 URI 的查询组件中,将其交付给客户端, 除非扩展另有指定:

"code":

必需。授权码由 授权服务器生成,并且对客户端不透明。授权码必须在 颁发后很快过期,以降低泄露风险。建议 授权码的最长生命周期为 10 分钟。授权码绑定到 客户端标识符、代码挑战和重定向 URI。

"state":

如果客户端 授权请求中存在 state 参数,则必需。 从客户端接收到的确切值。

"iss":

可选。授权服务器的标识符; 如果客户端与多个授权服务器交互, 客户端可使用它来防止混淆攻击。有关此参数何时必要, 以及客户端如何使用它来防止混淆攻击的更多详细信息, 请参见 第 7.14 节[RFC9207]

例如,授权服务器通过发送以下 HTTP 响应 来重定向用户代理:

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
          &state=xyz&iss=https%3A%2F%2Fauthorization-server.example.com

客户端必须忽略未识别的响应参数。 授权码字符串大小由本规范保持未定义。 客户端应避免对 code 值大小作出假设。 授权服务器应记录其颁发的任何值的大小。

授权服务器必须将 code_challengecode_challenge_method 值与已颁发的授权码相关联, 以便稍后可以验证代码挑战。

服务器用于将 code_challenge 与已颁发 code 相关联的确切方法超出本规范范围。 代码挑战可以存储在服务器上,并在那里与 code 相关联。code_challengecode_challenge_method 值可以 以加密形式存储在 code 本身中,但服务器 不得在响应参数中以 AS 之外的实体可提取的形式 包含 code_challenge 值。

客户端必须防止攻击者将授权码注入(重放)到 授权响应中。使用 code_challengecode_verifier 可以防止授权码注入,因为 授权服务器会拒绝 code_verifier 不匹配的令牌请求。 有关更多详细信息,请参见 第 7.5.1 节

4.1.2.1. 授权错误响应

如果请求因缺失、无效或不匹配的 重定向 URI 而失败,或者客户端标识符缺失或无效, 授权服务器不得将用户代理重定向到 无效的重定向 URI,并应告知资源所有者该 错误,例如通过在用户浏览器中显示消息。

授权服务器必须拒绝来自公开客户端且没有 code_challenge 的请求, 并且必须拒绝来自其他客户端的此类请求,除非有 合理保证该客户端以其他方式缓解授权码注入。 详见 第 7.5.1 节

如果服务器不支持请求的 code_challenge_method 转换, 授权端点必须返回 error 值设置为 invalid_request 的授权错误响应。 error_descriptionerror_uri 的响应应说明错误的性质,例如转换 算法不受支持。

如果资源所有者拒绝访问请求,或者 请求因缺失或无效重定向 URI 之外的原因失败, 授权服务器通过将用户代理重定向到重定向 URI, 并按照 附录 C.1 的描述将以下 参数添加到重定向 URI 的查询组件中来通知客户端:

"error":

必需。来自以下内容的单个 ASCII [USASCII] 错误代码:

"invalid_request":

请求缺少必需 参数,包含无效参数值,包含某个参数超过 一次,或在其他方面格式错误。

"unauthorized_client":

客户端未被授权使用此 方法请求授权码。

"access_denied":

资源所有者或 授权服务器拒绝了 该请求。

"unsupported_response_type":

授权服务器不支持使用此 方法获得授权码。

"invalid_scope":

所请求范围无效、 未知或格式错误。

"server_error":

授权服务器遇到意外 状况,导致其无法完成请求。 (需要此错误代码,是因为无法通过 HTTP 重定向 将 500 Internal Server Error HTTP 状态码返回给客户端。)

"temporarily_unavailable":

由于服务器临时过载或维护, 授权服务器当前无法处理该请求。 (需要此错误代码,是因为无法通过 HTTP 重定向 将 503 Service Unavailable HTTP 状态码返回 给客户端。)

error 参数的值 不得包含集合 %x20-21 / %x23-5B / %x5D-7E 之外的字符。

"error_description":

可选。人类可读的 ASCII [USASCII] 文本, 提供额外信息,用于帮助客户端开发者 理解发生的错误。 error_description 参数的值不得包含 集合 %x20-21 / %x23-5B / %x5D-7E 之外的字符。

"error_uri":

可选。标识一个包含错误信息的 人类可读网页的 URI,用于向客户端 开发者提供有关错误的额外信息。 error_uri 参数的值必须符合 URI-reference 语法,因此不得包含 集合 %x21 / %x23-5B / %x5D-7E 之外的字符。

"state":

如果客户端 授权请求中存在 state 参数,则必需。 从客户端接收到的确切值。

"iss":

可选。授权服务器的标识符。 有关详细信息,请参见上文 第 4.1.2 节

例如,授权服务器通过以下 HTTP 响应 重定向用户代理,以指示请求被拒绝:

HTTP/1.1 302 Found
Location: https://client.example.com/cb?error=access_denied
          &state=xyz&iss=https%3A%2F%2Fauthorization-server.example.com

4.1.3. 令牌端点 扩展

授权授予类型在令牌端点通过 grant_typeauthorization_code 来标识。

如果设置为此值,则支持以下超出 第 3.2.2 节 的额外令牌请求 参数:

"code":

必需。从授权服务器接收到的 授权码。

"code_verifier":

如果授权请求中包含 code_challenge 参数,则必需。 否则不得使用。原始代码验证器字符串。

"client_id":

如果客户端未按照 第 3.2.1 节 所述向授权服务器 进行认证,则必需。

授权服务器对于给定授权码只能返回一次访问令牌。

如果使用与先前成功令牌请求相同的 授权码发出第二个有效令牌请求, 授权服务器必须拒绝该请求,并且应 撤销(在可能时)先前基于该授权码颁发的所有 访问令牌和刷新令牌。 有关更多详细信息,请参见 第 7.5.2 节

例如,客户端发出以下 HTTPS 请求 (额外换行仅用于显示目的):

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&code_verifier=3641a2d12d66101249cdf7a79c000c1f8c05d2aafcf14bf146497bed

除了 第 3.2.2 节 中的处理规则外, 授权服务器还必须:

  • 确保授权码颁发给了已认证的 机密客户端,或者如果客户端是公开客户端, 则确保 code 颁发给了请求中的 client_id

  • 验证授权码有效,

  • 验证当且仅当授权请求中存在 code_challenge 参数时, code_verifier 参数才存在,

  • 如果存在 code_verifier, 则通过从接收到的 code_verifier 计算代码挑战, 并在先按照客户端指定的 code_challenge_method 方法 对其进行转换后,将其与先前关联的 code_challenge 进行比较, 来验证 code_verifier,并且

  • 如果在与令牌请求中的授权码关联的 授权请求中没有 code_challenge, 授权服务器必须拒绝该令牌请求。

有关令牌请求中 redirect_uri 参数与 OAuth 2.0 客户端的向后兼容性详情,请参见 第 10.2 节

4.2. 客户端凭据 授予

当客户端请求访问其控制下的受保护资源, 或访问另一个资源所有者的、先前已与授权服务器安排好的 受保护资源(其方法超出本规范范围)时, 客户端可以仅使用其客户端凭据 (或其他受支持的认证方式)请求访问令牌。

客户端凭据授予类型必须仅由机密客户端使用。

     +---------+                                  +---------------+
     |         |                                  |               |
     |         |>--(1)- Client Authentication --->| Authorization |
     | Client  |                                  |     Server    |
     |         |<--(2)---- Access Token ---------<|               |
     |         |                                  |               |
     +---------+                                  +---------------+
图 4客户端凭据授予

图 4 中所示的 客户端凭据授予用法包括以下步骤:

(1) 客户端向授权服务器认证,并 从令牌端点请求访问令牌。

(2) 授权服务器认证客户端;如果有效, 则颁发访问令牌。

4.2.1. 令牌端点 扩展

客户端凭据授予类型在令牌端点通过 grant_typeclient_credentials 来标识。

如果设置为此值,则支持以下超出 第 3.2.2 节 的额外令牌请求 参数:

"scope":

可选。如 第 1.4.1 节 所述的访问请求范围。

例如,客户端使用传输层安全发出以下 HTTP 请求 (额外换行仅用于显示目的):

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials

授权服务器必须认证客户端。

4.3. 刷新令牌授予

刷新令牌是由授权服务器颁发给客户端的凭据, 可用于基于现有授予获得新的(新鲜的)访问令牌。 客户端使用此选项,要么是因为先前的访问令牌已过期, 要么是因为客户端先前获得的访问令牌范围比相应授予批准的范围更窄, 而后来需要在同一授予下获得具有不同范围的访问令牌。

刷新令牌在传输和存储中必须保持机密, 并且只能在授权服务器和被颁发刷新令牌的客户端之间共享。 授权服务器必须维护刷新令牌与其被颁发给的客户端之间的绑定。

只要客户端身份可以被认证,授权服务器就必须验证刷新 令牌与客户端身份之间的绑定。当无法进行客户端认证时, 授权服务器应颁发发送方约束的刷新令牌, 或使用 第 4.3.1 节 中描述的刷新令牌轮换。

授权服务器必须确保未经授权方无法生成、修改或猜测出 有效的刷新令牌。

4.3.1. 令牌端点 扩展

刷新令牌授予类型在令牌端点通过 grant_typerefresh_token 来标识。

如果设置为此值,则支持以下超出 第 3.2.2 节 的额外参数:

"refresh_token":

必需。颁发给客户端的刷新令牌。

"scope":

可选。如 第 1.4.1 节 所述的访问请求范围。请求的范围不得包含资源所有者 最初未授予的任何范围;如果省略,则视为等于 资源所有者最初授予的范围。

由于刷新令牌通常是用于 请求额外访问令牌的长期凭据,因此刷新令牌绑定到 其被颁发给的客户端。机密客户端 必须按照 第 3.2.1 节 所述向授权服务器认证。

例如,客户端使用传输层安全发出以下 HTTP 请求 (额外换行仅用于显示目的):

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA

除了 第 3.2.2 节 中的处理规则外, 授权服务器还必须:

  • 如果请求中包含客户端认证, 确保刷新令牌颁发给了已认证的客户端;或者如果请求中包含 client_id,则确保刷新令牌颁发给了匹配的客户端

  • 验证与此刷新令牌对应的授予仍处于活动状态

  • 验证刷新令牌

对于公开客户端,授权服务器必须利用以下方法之一 来检测恶意行为者的刷新令牌重放:

  • 发送方约束的刷新令牌:授权服务器 将刷新令牌以加密方式绑定到某个客户端 实例,例如通过利用 DPoP [RFC9449] 或 mTLS [RFC8705]

  • 刷新令牌轮换:授权服务器 在每次访问令牌刷新响应中颁发新的 刷新令牌。先前的刷新令牌被作废,但授权服务器 保留关于其关系的信息。如果刷新令牌被攻破, 并随后被攻击者和合法客户端同时使用,其中一方将提交 已作废的刷新令牌,这会通知授权服务器发生泄露。 授权服务器无法确定是哪一方提交了无效刷新令牌, 但它会撤销活动刷新令牌以及与其关联的访问授权 授予。这会阻止攻击继续进行, 代价是迫使合法客户端获得新的 授权授予。

实现说明:刷新令牌所属的授予 可以编码到刷新令牌本身中。这可以使 授权服务器高效地确定刷新令牌所属的授予,并由此确定 需要撤销的所有刷新令牌。在这种情况下,授权服务器必须确保 刷新令牌值的完整性,例如使用签名。

4.3.2. 刷新令牌 响应

如果有效并已获授权,授权服务器会按照 第 3.2.3 节 所述颁发访问令牌。

授权服务器可以颁发新的刷新令牌,在这种 情况下,客户端必须丢弃旧刷新令牌并将其替换为 新刷新令牌。

4.3.3. 刷新令牌 建议

授权服务器可以在向客户端颁发新的 刷新令牌后撤销旧的刷新令牌。如果颁发新的 刷新令牌,则刷新令牌范围必须与客户端在 请求中包含的刷新令牌范围相同。

授权服务器可以在发生安全事件时 自动撤销刷新令牌,例如:

  • 密码更改

  • 在授权服务器注销

如果客户端已有一段时间不活动, 即刷新令牌已有一段时间未用于获得新的 访问令牌,则刷新令牌应过期。过期时间由 授权服务器自行决定。它可以是全局值, 也可以基于客户端策略或与刷新令牌关联的授予 (及其敏感性)确定。

4.4. 扩展授予

客户端通过将 grant 类型指定为绝对 URI (由授权服务器定义),并将其作为令牌端点 grant_type 参数的值,同时添加任何必要的额外参数, 来使用扩展授予类型。

例如,在用户已在单独设备上授权客户端后, 若要使用 [RFC8628] 定义的 Device Authorization Grant 请求访问令牌, 客户端会发出以下 HTTPS 请求 (额外换行仅用于显示目的):

  POST /token HTTP/1.1
  Host: server.example.com
  Content-Type: application/x-www-form-urlencoded

  grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code
  &device_code=GmRhmhcxhwEzkoEqiMEg_DnyEysNkuNhszIySk9eS
  &client_id=C409020731

如果访问令牌请求有效并已获授权, 授权服务器按照 第 3.2.3 节 所述颁发访问令牌和可选刷新令牌。如果请求的客户端 认证失败或请求无效,则授权服务器按照 第 3.2.4 节 所述返回错误响应。

5. 资源请求

客户端通过向资源服务器提交访问 令牌来访问受保护资源。资源服务器必须验证 访问令牌,并确保它尚未过期,且其范围 覆盖所请求的资源。资源服务器用于验证访问令牌的 方法超出本规范范围,但通常涉及资源服务器与 授权服务器之间的交互或协调。例如,当资源服务器和 授权服务器位于同处或属于同一系统时, 它们可以共享数据库或其他存储;当两个组件 独立运营时,它们可以使用 Token Introspection [RFC7662] 或结构化访问令牌格式,例如 JWT [RFC9068]

5.1. 承载令牌请求

本节定义了在资源请求中向资源服务器发送承载令牌的 两种方法。客户端必须使用以下定义的两种方法之一, 并且在每个请求中不得使用超过一种方法来传输令牌。

特别是,客户端不得在 URI 查询参数中发送访问令牌, 资源服务器必须忽略 URI 查询参数中的访问令牌。

5.1.1. Authorization 请求标头字段

当在 HTTP/1.1 [RFC7235] 定义的 Authorization 请求标头字段中发送访问令牌时, 客户端使用 Bearer 方案来传输访问令牌。

例如:

 GET /resource HTTP/1.1
 Host: server.example.com
 Authorization: Bearer mF_9.B5f-4.1JqM

此方案的 Authorization 标头字段语法 遵循 [RFC2617] 的 第 2 节 中定义的 Basic 方案用法。 请注意,与 Basic 一样,它不符合 [RFC2617] 的 第 1.2 节 中定义的通用语法, 但它与 HTTP 1.1 Authentication [RFC7235] 中的通用认证框架兼容, 尽管为了反映现有部署,它未遵循其中概述的首选 实践。Bearer 凭据的语法如下:

token68    = 1*( ALPHA / DIGIT /
                 "-" / "." / "_" / "~" / "+" / "/" ) *"="
credentials = "bearer" 1*SP token68

客户端应使用带有 Bearer HTTP 授权方案的 Authorization 请求标头字段, 用承载令牌发出已认证请求。资源服务器必须支持此方法。

[RFC9110] 的 第 11.1 节 所述, 字符串 bearer 不区分大小写。这意味着以下所有用法都是 Authorization 标头的有效用法:

  • Authorization: Bearer mF_9.B5f-4.1JqM

  • Authorization: bearer mF_9.B5f-4.1JqM

  • Authorization: BEARER mF_9.B5f-4.1JqM

  • Authorization: bEaReR mF_9.B5f-4.1JqM

5.1.2. 表单编码 内容参数

当在 HTTP 请求内容中发送访问令牌时, 客户端使用 access_token 参数将访问令牌添加到请求内容中。 除非满足以下所有条件,否则客户端不得使用此方法:

  • HTTP 请求包含设置为 application/x-www-form-urlencodedContent-Type 标头字段。

  • 内容遵循 URL 现行标准 [WHATWG.URL] 定义的 application/x-www-form-urlencoded 内容类型编码要求。

  • HTTP 请求内容是单部分的。

  • 要在请求中编码的内容必须完全由 ASCII [USASCII] 字符组成。

  • HTTP 请求方法必须是内容具有已定义语义的方法。 特别是,这意味着不得使用 GET 方法。

内容可以包含其他特定于请求的参数,在这种情况下, access_token 参数必须使用 & 字符 (ASCII 代码 38)与特定于请求的参数正确分隔。

例如,客户端使用传输层安全发出以下 HTTP 请求:

POST /resource HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

access_token=mF_9.B5f-4.1JqM

除非在参与客户端无法访问 Authorization 请求标头字段的应用上下文中, 否则不应使用 application/x-www-form-urlencoded 方法。 资源服务器可以支持此方法。

5.2. 访问令牌 验证

在收到访问令牌后,资源服务器必须检查 访问令牌尚未过期,已被授权访问所请求的资源, 以适当范围颁发,并满足资源服务器访问受保护资源的 其他策略要求。

访问令牌通常分为两类:引用令牌或 自编码令牌。 引用令牌可以通过查询授权服务器或在令牌数据库中 查找令牌来验证,而自编码令牌 将授权信息包含在可由资源服务器提取的加密和/或签名字符串中。

Token Introspection [RFC7662] 定义了一种查询授权服务器以检查访问令牌有效性的 标准化方法。

JWT Profile for Access Tokens [RFC9068] 定义了一种在令牌字符串中编码信息的 标准化方法。

有关创建和验证访问令牌的其他考虑事项,请参见 第 7.1 节

5.3. 错误响应

如果资源访问请求失败,资源服务器应告知 客户端该错误。错误响应的细节由特定令牌类型确定, 例如 第 5.3.2 节 中对承载令牌的 描述。

5.3.1. WWW-Authenticate 响应标头字段

如果受保护资源请求不包含认证 凭据,或不包含能够访问 受保护资源的访问令牌,则资源服务器必须包含 HTTP WWW-Authenticate 响应标头字段;它也可以在 响应其他条件时包含该字段。WWW-Authenticate 标头 字段使用 HTTP/1.1 [RFC7235] 定义的框架。

此令牌类型的所有质询都必须使用 auth-scheme 值 Bearer。该方案后必须跟随一个或多个 auth-param 值。本规范为此令牌类型使用或定义的 auth-param 属性如下。也可以使用其他 auth-param 属性。

"realm":

可以包含 realm 属性, 以 HTTP/1.1 [RFC7235] 中描述的方式指示 保护范围。realm 属性不得出现超过一次。

"scope":

scope 属性定义于 第 1.4.1 节scope 属性是以空格分隔的区分大小写范围值列表, 指示访问所请求资源所需的访问令牌范围。 scope 值由实现定义;没有集中注册表; 允许值由授权服务器定义。scope 值的顺序 不重要。在某些情况下,请求具有足够访问范围的新访问令牌 以利用受保护资源时,会使用 scope 值。 scope 属性的使用是可选的。 scope 属性不得出现超过一次。 scope 值旨在供程序使用,并不意味着显示给最终用户。

下面给出两个示例 scope 值;它们分别取自 OpenID Connect [OpenID.Messages] 和 Open Authentication Technology Committee (OATC) Online Multimedia Authorization Protocol [OMAP] OAuth 2.0 用例:

scope="openid profile email"
scope="urn:example:channel=HBO&urn:example:rating=G,PG-13"
"error":

如果受保护资源请求包含访问 令牌并且认证失败,资源服务器应包含 error 属性,以向客户端提供访问 请求被拒绝的原因。参数值在 第 5.3.2 节 中描述。

"error_description":

资源服务器可以包含 error_description 属性,以向开发者提供人类可读的 说明,该说明不应显示给最终用户。

"error_uri":

资源服务器可以包含 error_uri 属性,其值为一个绝对 URI, 用于标识解释错误的人类可读网页。

errorerror_descriptionerror_uri 属性不得 出现超过一次。

scope 属性的值(在 附录 A.4 中指定) 不得包含用于表示 scope 值的集合 %x21 / %x23-5B / %x5D-7E 之外的字符,以及 scope 值之间分隔符使用的 %x20 之外的字符。errorerror_description 属性的值(在 附录 A.7附录 A.8 中指定)不得 包含集合 %x20-21 / %x23-5B / %x5D-7E 之外的字符。 error_uri 属性的值(在 附录 A.9 中指定) 必须符合 URI-reference 语法,因此不得 包含集合 %x21 / %x23-5B / %x5D-7E 之外的字符。

5.3.2. 错误代码

当请求失败时,资源服务器使用适当的 HTTP 状态码(通常为 400、401、403 或 405)进行响应, 并在响应中包含以下错误代码之一:

"invalid_request":

请求缺少必需参数,包含不受支持的参数或 参数值,重复相同参数,使用超过一种方法包含访问 令牌,或在其他方面格式错误。资源服务器应 以 HTTP 400 (Bad Request) 状态码响应。

"invalid_token":

提供的访问令牌已过期、已撤销、 格式错误,或因其他原因无效。资源服务器应以 HTTP 401 (Unauthorized) 状态码响应。客户端可以 请求新的访问令牌并重试受保护资源 请求。

"insufficient_scope":

该请求需要比授予客户端并由访问令牌表示的 范围所提供的权限(范围)更高的权限。 资源服务器应以 HTTP 403 (Forbidden) 状态码响应,并可以包含 scope 属性,其值为访问受保护 资源所需的范围。

扩展可以定义额外错误代码,或指定返回上述错误代码的 其他情形。

如果请求缺少任何认证信息(例如, 客户端不知道认证是必要的,或尝试使用不受支持的认证方法), 资源服务器不应包含错误代码或其他错误信息。

例如:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example"

以及在响应使用已过期访问令牌进行认证尝试的 受保护资源请求时:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example",
                  error="invalid_token",
                  error_description="The access token expired"

6. 可扩展性

6.1. 定义访问令牌 类型

访问令牌类型可以通过以下两种方式之一来定义: 注册到 Access Token Types 注册表中(遵循 [RFC6749] 的 第 11.1 节 中的程序),或使用唯一的绝对 URI 作为其名称。

6.1.1. 已注册的 访问令牌类型

[RFC6750][RFC6749] 的 第 11.4 节 中建立了一个通用注册表,供 OAuth 令牌认证方案共享错误值。

主要为 OAuth 令牌认证设计的新认证方案应定义 一种向客户端提供错误状态码的机制,其中允许的错误值 注册在本规范建立的错误注册表中。

此类方案可以将有效错误代码集合限制为 已注册值的子集。如果错误代码使用命名参数返回, 则参数名称应为 error

其他能够用于 OAuth 令牌认证, 但并非主要为此目的设计的方案,也可以以相同方式 将其错误值绑定到该注册表。

新的认证方案也可以选择指定使用 error_descriptionerror_uri 参数, 以与本规范中的用法相平行的方式返回错误信息。

类型名称必须符合 type-name ABNF。如果类型定义包含新的 HTTP 认证方案,则类型名称应与 HTTP 认证方案名称相同 (如 [RFC2617] 所定义)。令牌类型 example 保留用于示例。

type-name  = 1*name-char
name-char  = "-" / "." / "_" / DIGIT / ALPHA

6.1.2. 供应商特定的访问令牌类型

使用 URI 名称的类型应限于供应商特定的 实现,这些实现并非普遍适用,并且特定于其被使用的 资源服务器实现细节。

所有其他类型都必须注册。

6.2. 定义新的端点 参数

用于授权端点或令牌端点的新请求或响应参数, 按照 [RFC6749] 的 第 11.2 节 中的程序,在 OAuth Parameters 注册表中定义并注册。

参数名称必须符合 param-name ABNF, 并且参数值语法必须被明确定义(例如,使用 ABNF, 或引用现有参数的语法)。

param-name  = 1*name-char
name-char   = "-" / "." / "_" / DIGIT / ALPHA

未注册的供应商特定参数扩展,如果并非普遍适用, 且特定于其被使用的授权服务器实现细节,则应 使用不太可能与其他已注册值冲突的供应商特定前缀 (例如,以 'companyname_' 开头)。

6.3. 定义新的 授权授予类型

新的授权授予类型可以通过为其分配一个唯一的绝对 URI 来定义,该 URI 用作 grant_type 参数的值。 如果扩展授予类型需要额外的令牌端点参数, 这些参数必须按照 [RFC6749] 的 第 11.2 节 所述注册到 OAuth Parameters 注册表中。

6.4. 定义新的 授权端点响应类型

用于授权端点的新响应类型按照 [RFC6749] 的 第 11.3 节 中的程序,在 Authorization Endpoint Response Types 注册表中定义并注册。响应类型 名称必须符合 response-type ABNF。

response-type  = response-name *( SP response-name )
response-name  = 1*response-char
response-char  = "_" / DIGIT / ALPHA

如果响应类型包含一个或多个空格字符(%x20), 则它会作为以空格分隔的值列表进行比较,其中 值的顺序无关紧要。只能注册一种值顺序, 该顺序涵盖同一组值的所有其他排列。

例如,一个扩展可以定义并注册 code other_token 响应类型。一旦注册,同一组合不能注册为 other_token code,但两个值都可以用于 表示同一响应类型。

6.5. 定义其他 错误代码

在协议扩展(即访问令牌类型、 扩展参数或扩展授予类型)需要与授权码授予错误 响应(第 4.1.2.1 节)、令牌错误响应(第 3.2.4 节)或 资源访问错误响应(第 5.3 节)一起使用额外错误代码的情况下,可以 定义此类错误代码。

如果扩展错误代码与之结合使用的扩展是 已注册访问令牌类型、已注册端点参数或 扩展授予类型,则必须注册这些错误代码(遵循 [RFC6749] 的 第 11.4 节 中的程序)。与未注册扩展一起使用的错误代码 可以注册。

错误代码必须符合 error ABNF,并且在可能时应以 标识性名称作为前缀。例如,标识扩展参数 example 设置为无效值的错误应命名为 example_invalid

error      = 1*error-char
error-char = %x20-21 / %x23-5B / %x5D-7E

7. 安全考虑

作为一个灵活且可扩展的框架,OAuth 的安全 考虑取决于许多因素。以下各节 为实现者提供安全指南,重点关注 第 2.1 节 中描述的三个客户端配置文件:Web 应用程序、 基于浏览器的应用程序和原生应用程序。

全面的 OAuth 安全模型和分析,以及 协议设计背景,由 [RFC6819][RFC9700] 提供。

7.1. 访问令牌安全 考虑

7.1.1. 安全威胁

以下列表列出了针对使用某种形式令牌的协议的若干常见威胁。 该威胁列表基于 NIST Special Publication 800-63 [NIST800-63]

7.1.1.1. 访问 令牌制造/修改

攻击者可能生成伪造的 访问令牌,或修改现有令牌的内容(例如认证或 属性声明),导致资源 服务器向客户端授予不适当的访问权限。例如, 攻击者可能修改令牌以延长有效期; 恶意客户端可能修改断言,以获得其不应查看的信息访问权限。

7.1.1.2. 访问 令牌信息泄露

访问令牌可能包含包括敏感信息在内的认证和属性 声明。

如果应防止客户端观察 访问令牌的内容, 则必须应用内容加密。

由于 cookie 默认以明文传输, 其中包含的任何信息都有泄露风险: 承载令牌不得存储在可能以明文发送的 cookie 中。 有关 cookie 的安全考虑,请参见 [RFC6265] 的第 7 节和第 8 节。

7.1.1.3. 访问令牌 重定向

攻击者使用为一个资源服务器使用而生成的访问令牌, 来访问另一个错误地认为该令牌是为其准备的资源 服务器。

7.1.1.4. 访问令牌 重放

攻击者尝试使用过去已经 与该资源服务器一起使用过的访问令牌。

7.1.2. 威胁缓解

通过使用数字签名保护访问令牌的内容, 并遵循签名密钥管理的最佳实践(例如定期密钥轮换), 可以缓解大量威胁。

或者,承载令牌可以包含对 授权信息的引用,而不是直接编码该信息。 使用引用可能需要资源服务器与授权服务器之间的额外交互, 以将引用解析为授权信息。此类交互机制 未由本规范定义,但 Token Introspection [RFC7662] 定义了一种这样的机制。

本文档不指定访问令牌的编码或内容; 因此,有关保证访问令牌完整性保护手段的详细建议 超出本规范范围。 JSON Web Token Profile for Access Tokens [RFC9068] 中描述了 访问令牌的一种编码和签名机制示例。

为了处理访问令牌重定向,授权 服务器在令牌中包含预期接收方(受众)的身份非常重要, 通常是单个资源服务器(或资源服务器列表)。 也建议将令牌的使用限制到特定范围。

第 1.5 节 提供了相关信息, 用于防止访问令牌泄露,并为客户端、资源服务器和授权服务器之间的 通信提供机密性和完整性。

7.1.3. 建议 摘要

7.1.3.1. 保护 承载令牌

客户端实现必须确保 承载令牌不会泄露给非预期方,因为这些方将 能够使用它们访问受保护资源。这 是使用承载令牌时的主要安全考虑, 也是后续所有更具体建议的基础。

7.1.3.2. 验证 TLS 证书链

客户端在向受保护资源发出请求时 必须验证 TLS 证书链。未能这样做可能使 DNS 劫持攻击能够窃取 令牌并获得非预期访问权限。

7.1.3.3. 始终使用 TLS (https)

客户端在使用 承载令牌发出请求时,必须始终使用 TLS (https) 或等效的传输安全。未能这样做会使令牌暴露于 诸多攻击之下,可能让攻击者获得非预期访问权限。

7.1.3.4. 不要 在 HTTP cookie 中存储承载令牌

实现不得将 承载令牌存储在可能以明文发送的 cookie 中(这是 cookie 的默认传输模式)。确实将承载令牌存储在 cookie 中的 实现必须采取防范跨站请求伪造的措施。

7.1.3.5. 颁发 短生命周期承载令牌

授权服务器应颁发 短生命周期承载令牌,尤其是在向运行于 Web 浏览器或其他 可能发生信息泄露的环境中的客户端颁发令牌时。 使用短生命周期承载令牌可以降低令牌被 泄露的影响。

7.1.3.6. 颁发 有范围的承载令牌

授权服务器应颁发包含受众限制的承载令牌, 将其使用范围限定到预期资源服务器或 资源服务器集合。

7.1.3.7. 不要 在页面 URL 中传递承载令牌

承载令牌不得 在页面 URL 中传递(例如作为查询字符串参数)。 相反,承载令牌应在采取了机密性措施的 HTTP 消息标头或 消息正文中传递。浏览器、Web 服务器和其他软件可能无法 充分保护浏览器历史记录、Web 服务器日志和其他 数据结构中的 URL。如果承载令牌在页面 URL 中传递, 攻击者可能能够从历史数据、日志或 其他不安全位置窃取它们。

7.1.4. 访问令牌 权限限制

与访问令牌关联的权限应限制为特定应用程序或用例 所需的最低权限。这可以防止客户端超出资源所有者授权的权限。 它还可以防止用户超出相应安全策略授权的 权限。权限限制也有助于降低访问令牌泄露的影响。

特别是,访问令牌应限制到某些 资源服务器(受众限制),最好是单个资源 服务器。为了实现这一点,授权服务器将 访问令牌与某些资源服务器相关联,并且每个资源 服务器都有义务针对每个请求验证随该请求发送的访问 令牌是否旨在用于该特定 资源服务器。如果不是,资源服务器必须拒绝服务 相应请求。客户端和授权服务器可以分别利用本文档和 [RFC8707] 中指定的 scoperesource 参数, 来确定其希望访问的资源服务器。

此外,访问令牌应限制到资源服务器或资源上的 某些资源和操作。为了实现这一点, 授权服务器将访问令牌与相应资源和操作相关联, 并且每个资源服务器都有义务针对每个请求验证随该请求 发送的访问令牌是否旨在用于对特定资源执行 该特定操作。如果不是,资源服务器必须拒绝 服务相应请求。客户端和授权服务器可以利用 [RFC9396] 中指定的参数 scopeauthorization_details 来确定这些资源和/或操作。

7.2. 客户端认证

根据客户端注册和凭据生命周期管理的整体过程, 这可能会影响授权服务器对特定客户端的信心水平。

例如,对动态注册客户端的认证并不证明客户端的身份, 它只确保向授权服务器重复发出的请求来自同一个客户端实例。 此类客户端在允许请求的范围方面可能受到限制, 或可能有其他限制,例如更短的令牌生命周期。

相比之下,如果存在一个已注册应用,其开发者身份 已验证、已签署合同,并被颁发仅在安全后端服务中使用的 客户端密钥,则授权服务器可能允许此客户端请求更敏感的范围 或被颁发生命周期更长的令牌。

7.3. 客户端冒充

如果机密客户端的凭据被窃取, 恶意客户端可以冒充该客户端并获得对 受保护资源的访问权限。

授权服务器应强制执行明确的资源所有者 认证,并向资源所有者提供有关客户端以及所请求授权范围和生命周期的信息。 由资源所有者在当前客户端的上下文中审查这些信息,并授权或拒绝请求。

授权服务器不应在没有认证客户端或依赖其他措施以 确保重复请求来自原始客户端而非冒充者的情况下, 自动处理重复的授权请求(没有主动的资源所有者交互)。

7.3.1. 冒充 原生应用

如上所述,授权 服务器不应在没有用户同意或交互的情况下自动处理授权请求, 除非能够确保客户端的身份。这包括用户先前已批准给定客户端 ID 授权请求的情况——除非能够证明客户端身份, 否则该请求应按没有先前请求被批准的方式处理。

授权服务器可以接受诸如已声明的 https 方案重定向等措施作为身份证明。 某些操作系统可能提供其他平台特定的身份功能, 也可以视情况接受。

7.3.2. 访问令牌 权限限制

客户端应请求具有必要最小范围的访问令牌。 授权服务器在选择如何满足所请求范围时应考虑客户端身份, 并可以颁发范围少于请求范围的访问令牌。

与访问令牌关联的权限应限制为 特定应用程序或用例所需的最低权限。这 可以防止客户端超出资源所有者授权的权限。 它还可以防止用户超出相应安全策略授权的权限。 权限限制也有助于降低访问令牌泄露的影响。

特别是,访问令牌应限制到某些 资源服务器(受众限制),最好是单个资源 服务器。为了实现这一点,授权服务器将 访问令牌与某些资源服务器相关联,并且每个资源 服务器都有义务针对每个请求验证随该请求发送的访问 令牌是否旨在用于该特定 资源服务器。如果不是,资源服务器必须拒绝服务 相应请求。客户端和授权服务器可以分别利用 [RFC8707] 中指定的 scoperesource 参数, 来确定其希望访问的资源服务器。

7.4. 客户端冒充 资源所有者

资源服务器可能基于访问令牌为之颁发的 资源所有者身份,或基于客户端凭据授予中的客户端身份作出访问控制决策。 如果这两种选项都可能存在,则根据实现细节, 客户端身份可能会被误认为资源所有者身份。例如, 如果客户端在向授权服务器注册时能够选择自己的 client_id,恶意客户端可能将其设置为标识最终用户的值 (例如,如果使用 OpenID Connect,则为 sub 值)。 如果资源服务器无法正确区分颁发给客户端的访问令牌和 颁发给最终用户的访问令牌,则客户端随后可能能够访问最终用户的资源。

如果授权服务器对客户端 ID 和用户 标识符使用公共命名空间,导致资源服务器无法区分由资源所有者授权的访问 令牌和由客户端自身授权的访问令牌,则授权服务器不应允许客户端影响其 client_id 或任何其他可能导致与真实资源所有者混淆的 Claim。 在无法避免这种情况时,授权服务器必须为资源服务器提供其他方式, 以区分这两种类型的访问令牌。

7.5. 授权码 安全考虑

7.5.1. 授权 码注入

授权码注入是一种攻击,其中客户端在其重定向 URI 中接收到 来自攻击者的授权码,而不是来自合法授权服务器的授权码。 如果没有适当保护,客户端没有机制可知该攻击已经发生。 授权码注入可能导致攻击者获得对受害者账户的访问权限, 也可能导致受害者意外获得对攻击者账户的访问权限。

7.5.1.1. 对策

为了防止授权码注入到 客户端,客户端必须使用 code_challengecode_verifier,并且授权服务器 必须强制执行其使用,除非同时满足以下两个条件:

  • 客户端是机密客户端。

  • 在特定部署和特定 请求中,授权服务器有合理保证认为客户端正确实现了 OpenID Connect nonce 机制。

在这种情况下,仍然建议使用并强制执行 code_challengecode_verifier

code_challenge 或 OpenID Connect nonce 值必须是 特定于事务的,并安全绑定到启动该事务的客户端和用户代理。 如果某个事务导致错误,则必须为 code_challengenonce 选择新值。

依赖客户端验证 OpenID Connect nonce 参数 意味着授权服务器无法确认客户端实际上已经保护自己免受授权码注入攻击。 如果攻击者能够向客户端注入授权码, 客户端仍会交换该注入的授权码并获得令牌, 并且只会在稍后验证 nonce 时发现不匹配后拒绝 ID token。 相比之下,授权服务器强制执行 code_challengecode_verifier 参数可提供更高的安全结果, 因为授权服务器能够预先识别授权码 注入攻击,并从一开始就避免颁发任何令牌。

历史说明:尽管 PKCE [RFC7636] (其中创建了 code_challengecode_verifier 参数) 最初被设计为一种保护原生应用免受授权码外泄攻击的机制, 但各种 OAuth 客户端,包括 Web 应用程序和其他机密 客户端,都容易受到授权码注入攻击,而 code_challengecode_verifier 机制解决了这些攻击。

7.5.2. 授权码 重用

如果授权码能够被使用超过一次, 则可能发生多种类型的攻击。

第 4.1.3 节 所述,当收到已经用于 颁发访问令牌的授权码的第二个有效 请求时,授权服务器必须拒绝令牌请求并撤销任何已颁发的令牌。 如果攻击者能够外泄授权码并在合法客户端之前使用它, 攻击者将获得访问令牌,而合法客户端则不会获得。 撤销任何已颁发的令牌意味着攻击者的 令牌随后也将被撤销,从而阻止攻击进一步进行。

但是,只有当包含授权码的 请求本身也有效(包括任何其他参数,例如 code_verifier 和客户端认证)时,授权服务器才应撤销已颁发的令牌。 当收到包含无效参数的重放授权码时,授权服务器 不应撤销任何已颁发的令牌。如果这样做, 对于能够获得授权码但无法获得客户端认证或 code_verifier 的攻击者而言,就会创造拒绝服务机会: 攻击者可在合法客户端之前发送无效的授权码请求, 从而在合法客户端发出有效请求时撤销合法客户端的令牌。

7.5.3. HTTP 307 重定向

重定向可能包含用户 凭据的请求的授权服务器,不得使用 307 状态码 ([RFC9110] 的 第 15.4.8 节)进行重定向。 如果此类请求使用 HTTP 重定向(而不是例如 JavaScript),AS 应使用状态码 303 (“See Other”)。

在授权端点,一个典型的协议流程是 AS 提示用户在表单中输入其凭据,然后将该表单 (使用 POST 方法)提交回授权 服务器。AS 检查凭据,如果成功,则将 用户代理重定向到客户端的重定向 URI。

如果使用 307 状态码进行重定向, 用户代理会通过 POST 请求将用户凭据发送给客户端。

这会向客户端泄露敏感凭据。如果 客户端是恶意的,它可以使用这些凭据在 AS 处冒充 用户。

该行为对开发者而言可能出乎意料,但它定义于 [RFC9110] 的 第 15.4.8 节。此状态码不要求用户 代理将 POST 请求重写为 GET 请求,从而丢弃 POST 请求内容中的表单数据。

在 HTTP [RFC9110] 中,只有状态码 303 明确强制将 HTTP POST 请求重写为 HTTP GET 请求。对于所有其他状态码,包括常见的 302, 用户代理可以选择不将 POST 重写为 GET 请求, 因而向客户端泄露用户凭据。(然而在实践中, 大多数用户代理只会在 307 重定向时表现出这种行为。)

7.6. 确保端点 真实性

与中间人攻击相关的风险通过强制使用通道安全机制 (例如 [RFC8446]) 与授权端点和令牌端点通信来缓解。 有关更多详细信息,请参见 第 1.5 节

7.7. 凭据猜测 攻击

授权服务器必须防止攻击者猜测访问 令牌、授权码、刷新令牌、资源所有者 密码和客户端凭据。

攻击者猜测生成的令牌(以及其他不打算由最终用户处理的 凭据)的概率必须小于或等于 2^(-128),并且应 小于或等于 2^(-160)。

授权服务器必须利用其他方式保护 供最终用户使用的凭据。

7.8. 网络钓鱼攻击

此协议及类似协议的广泛部署可能导致最终用户 对被重定向到要求其输入密码的网站这一做法习以为常。 如果最终用户在输入凭据前不谨慎验证这些网站的真实性, 攻击者就可能利用这种做法窃取资源所有者的密码, 以及其他可被钓鱼获取的凭据,例如 OTP。

服务提供商应尝试教育最终用户了解网络钓鱼攻击带来的风险, 并应提供使最终用户易于确认其站点真实性的机制, 例如使用抗钓鱼认证器,因为抗钓鱼认证器只有在平台成功验证 站点来源后,才会向用户提供用于登录某个站点的凭据。 客户端开发者应考虑其与用户代理交互方式(例如外部、嵌入式)的 安全影响,以及最终用户验证授权服务器真实性的能力。

有关缓解网络钓鱼攻击风险的更多详细信息, 请参见 第 1.5 节

7.9. 跨站请求 伪造

攻击者可能尝试向受害者设备上合法客户端的重定向 URI 注入请求,例如导致客户端访问攻击者控制下的资源。 这是称为跨站请求伪造(CSRF)的攻击的一种变体。

传统对策是客户端在 state 参数中传递随机值, 也称为 CSRF Token,该值按描述将请求与重定向 URI 绑定到用户代理会话。 此对策在 [RFC6819] 的 第 5.3.5 节 中有详细描述。code_verifier 参数或 OpenID Connect nonce 值也提供相同保护。

当使用 code_verifier 而不是 statenonce 进行 CSRF 保护时,需要注意:

  • 客户端必须确保 AS 支持客户端 打算使用的 code_challenge_method。 如果授权服务器不支持请求的方法, 则必须改用 statenonce 进行 CSRF 保护。

  • 如果 state 用于携带应用程序状态,并且 其内容的完整性是一个关注点,则客户端必须保护 state 免受篡改和交换。这可以通过将 state 的内容绑定到浏览器会话和/或使用已签名/加密的 state 值 [I-D.bradley-oauth-jwt-encoded-state] 来实现。

因此,AS 必须提供一种方式来检测其支持的代码挑战 方法,要么通过符合 [RFC8414] 的 AS 元数据, 要么提供特定于部署的方式来确保或确定支持。

7.10. 点击劫持

[RFC6819] 的 第 4.4.1.9 节 所述, 授权 请求容易受到点击劫持攻击,也称为用户 界面重绘攻击。在此类攻击中,攻击者将 授权端点用户界面嵌入到无害上下文中。 用户以为正在与该上下文交互,例如 点击按钮,却无意中与授权 端点用户界面交互。也可以实现相反效果: 用户以为正在与授权端点交互, 却可能无意中将密码输入到攻击者提供的、 覆盖在原始用户界面上的输入字段中。点击劫持 攻击可以设计得让用户几乎难以察觉, 例如使用几乎不可见的 iframe 覆盖在 其他元素之上。

攻击者可以利用此向量获取用户的认证 凭据,更改授予客户端的访问范围, 并可能访问用户的资源。

授权服务器必须防止点击劫持攻击。 [RFC6819] 中描述了多种 对策,包括使用 X-Frame-Options HTTP 响应标头字段和 frame-busting JavaScript。除此之外,授权服务器还应 使用 Content Security Policy (CSP) level 2 [CSP-2] 或更高版本。

为了有效,CSP 必须用于授权端点,并且 在适用时用于其他用于认证用户和 授权客户端的端点(例如设备授权端点、登录 页面、错误页面等)。这可以防止支持 CSP 的用户代理中 未授权来源的框架嵌入。客户端可以允许被 其重定向端点所用来源之外的某些其他来源嵌入。 因此,授权服务器应允许 管理员为特定客户端配置允许的来源, 和/或允许客户端动态注册这些来源。

使用 CSP 允许授权服务器在单个响应标头字段中 指定多个来源,并使用灵活模式约束这些来源 (详见 [CSP-2])。 该标准第 2 级提供了一种稳健机制,可通过使用 限制框架来源的策略(使用 frame-ancestors) 以及限制允许在 HTML 页面上执行的脚本来源的策略 (使用 script-src),来防止点击劫持。 以下列表显示了此类策略的非规范性示例:

HTTP/1.1 200 OK
Content-Security-Policy: frame-ancestors https://ext.example.org:8000
Content-Security-Policy: script-src 'self'
X-Frame-Options: ALLOW-FROM https://ext.example.org:8000
...

由于某些用户代理不支持 [CSP-2],此技术 应与其他技术结合使用,包括 [RFC6819] 中描述的技术,除非授权服务器明确不支持此类旧版用户代理。 即使在这种情况下,仍应采用额外的 对策。

7.11. 注入和输入 验证

当应用程序未经过滤地使用输入或其他外部 变量,并导致应用程序逻辑被修改时,就会发生注入攻击。 这可能允许攻击者获得对应用程序设备或其数据的访问权限, 造成拒绝服务,或引入范围广泛的恶意副作用。

授权服务器和客户端必须将接收到的参数视为 潜在恶意的外部输入,并应用适当保护, 特别是 stateredirect_uri 参数的值。

7.12. 开放重定向

开放重定向器是将用户浏览器 转发到从查询参数获得的任意 URI 的端点。 此类端点有时会被实现,例如在用户随后被重定向到外部网站前 显示消息,或在用户被中断(例如登录提示)之前, 将用户重定向回其原本打算访问的 URL。

当 AS 或客户端具有开放 重定向器时,可能发生以下攻击。

7.12.1. 作为开放 重定向器的客户端

客户端不得暴露开放重定向器。 攻击者可能使用开放重定向器生成指向客户端的 URL, 并利用它们外泄授权码,如 [RFC9700] 的 第 4.1.1 节 所述。 另一种滥用情况是生成看似指向客户端的 URL。 这可能诱骗用户信任该 URL 并在浏览器中访问它。 这可被滥用于网络钓鱼。

为了防止开放重定向,客户端应仅在 目标 URL 位于白名单中,或请求的来源和完整性可被认证时 才进行重定向。OWASP [owasp_redir] 描述了防范开放重定向的对策。

7.12.2. 作为开放重定向器的 授权服务器

与客户端一样,攻击者可能尝试利用用户对 授权服务器(尤其是其 URL)的信任来执行 网络钓鱼攻击。OAuth 授权服务器经常将用户重定向 到其他网站(客户端),但必须安全地这样做。

第 4.1.2.1 节 已经通过说明 在 client_idredirect_uri 组合无效的情况下, 授权服务器不得自动重定向用户代理,来防止开放重定向。

但是,攻击者也可以利用正确注册的 重定向 URI 执行网络钓鱼攻击。例如,攻击者可以通过动态客户端注册 [RFC7591] 注册客户端,并执行以下攻击之一:

  1. 故意发送错误的授权请求, 例如使用无效 scope 值,从而指示授权服务器将 用户代理重定向到其钓鱼站点。

  2. 故意发送有效的授权请求,其中 client_idredirect_uri 由攻击者控制。用户认证后, 授权服务器提示用户同意该请求。如果用户 注意到请求存在问题并拒绝该请求,授权 服务器仍会将用户代理重定向到钓鱼站点。 在这种情况下,无论用户采取什么操作,用户代理都会被重定向到 钓鱼站点。

  3. 故意发送有效的静默认证 请求(prompt=none),其中 client_idredirect_uri 由 攻击者控制。在这种情况下, 授权服务器会自动将用户代理重定向到 钓鱼站点。

授权服务器必须采取预防措施来防止这些 威胁。授权服务器必须始终 先认证用户,并且除静默认证 用例外,在重定向用户之前,在需要时提示用户输入凭据。 基于其风险评估,授权服务器需要决定 是否可以信任重定向 URI。它可以考虑内部执行的或通过外部服务执行的 URI 分析,以评估 URI 背后内容的可信度和 可信性,以及重定向 URI 和 其他客户端数据的来源。

只有在信任 重定向 URI 时,授权服务器才应自动重定向用户代理。 如果 URI 不受信任,授权服务器可以告知用户并依赖 用户作出正确决定。

7.13. 传输安全

在某些部署中,包括利用负载均衡器的部署, 到资源服务器的 TLS 连接会在实际提供资源的 服务器之前终止。这可能使令牌在 TLS 连接终止的 前端服务器与提供资源的后端服务器之间 不受保护。在此类部署中,必须采用充分措施来确保 前端服务器与后端服务器之间访问令牌的机密性; 对令牌加密就是一种可能措施。

有关更多信息,请参见 [RFC9110] 的 第 17.2 节

7.14. 授权服务器 混淆缓解

混淆是一种针对 OAuth 客户端与 两个或更多授权服务器交互,且至少一个授权 服务器受攻击者控制的场景的攻击。例如,如果攻击者使用动态注册 将客户端注册到其自己的授权服务器,或者如果某个授权服务器 被攻破,就可能出现这种情况。

当 OAuth 客户端只能与一个授权服务器交互时, 不需要混淆防御。然而,在 OAuth 客户端与两个或更多 授权服务器交互的场景中,客户端必须防止混淆攻击。 以下讨论两种不同方法。

对于两种防御,客户端都必须针对每个授权请求存储 其发送授权请求到的颁发者,将此信息绑定到 用户代理,并检查授权响应是否来自 正确的颁发者。客户端必须确保后续访问令牌请求 (如适用)发送到同一颁发者。颁发者通过相关 元数据作为授权端点和令牌端点组合的抽象标识符, 这些端点将在流程中使用。如果颁发者标识符 不可用,例如既未使用 OAuth 2.0 Authorization Server Metadata [RFC8414] 也未使用 OpenID Connect Discovery [OpenID.Discovery],则可以改用该元组的其他唯一标识符 或该元组本身。为简化表述, 下文将此类特定于部署的标识符归入颁发者(或 颁发者标识符)之下。

注意:仅存储授权服务器 URL 不足以识别 混淆攻击。攻击者可能将未被攻破的 AS 的授权端点 URL 声明为 “他们的” AS URL,但将令牌端点声明在其控制之下。

有关几种混淆攻击类型的详细描述, 请参见 [RFC9700] 的 第 4.4 节

7.14.1. 通过颁发者标识进行 混淆防御

这种防御要求授权服务器在授权响应中向客户端发送其 颁发者标识符。收到授权 响应时,客户端必须将收到的颁发者标识符与存储的 颁发者标识符进行比较。如果不匹配,客户端必须中止 交互。

可以通过不同方式将此颁发者标识符传输 给客户端:

  • 例如,颁发者信息可以通过可选响应参数 iss 传输(参见 第 4.1.2 节)。

  • 当使用 OpenID Connect,并且在授权 响应中返回 ID Token 时,客户端可以评估 ID Token 中的 iss claim。

在两种情况下,iss 值都必须按照 [RFC9207] 进行评估。

虽然这种防御可能需要使用额外参数来传输 颁发者信息,但它是一种稳健且相对简单的混淆防御。

7.14.2. 通过不同重定向 URI 进行 混淆防御

对于这种防御,客户端必须为其交互的每个 颁发者使用不同的重定向 URI。

客户端必须通过将颁发者的不同重定向 URI 与接收授权响应的 URI 进行比较,检查授权响应是否来自正确 的颁发者。如果不匹配,客户端必须中止流程。

虽然这种防御建立在现有 OAuth 功能之上, 但在客户端只为使用许多不同颁发者而注册一次的场景中 (如某些开放银行方案)无法使用,并且由于与 客户端注册紧密集成,自动部署更加困难。

此外,攻击者可能能够通过使用客户端分配给攻击者 AS 的重定向 URI,在“诚实的” AS 处注册一个新客户端, 从而绕过这种防御提供的保护。攻击者随后可以按上述方式运行 攻击,将客户端 ID 替换为其新创建客户端的客户端 ID。

因此,只有在其他选项不可用时才应使用这种防御。

8. 原生应用程序

原生应用程序是安装并执行在资源所有者所用设备上的客户端 (即桌面应用程序或原生移动应用程序)。原生应用程序需要对 与安全、平台能力以及整体最终用户体验相关的问题 给予特殊考虑。

本节中的指南主要是在原生移动应用的上下文中给出, 而不是桌面应用。与这里描述的 OAuth 流程相关的、 提供给应用开发者的能力方面,原生移动平台 比桌面平台更加成熟。虽然该指南主要聚焦于移动应用, 但其中许多内容通常也可适用于桌面应用。

授权端点要求客户端与资源所有者的用户代理之间 进行交互。当前最佳实践是在外部用户代理 (通常是浏览器)中执行 OAuth 授权请求, 而不是在嵌入式用户代理(例如使用 web-view 实现的 用户代理)中执行。

原生应用程序可以通过几种不同方式 捕获来自授权服务器的响应,每种方式具有不同的安全属性。 例如,使用带有“应用声明 URL”或自定义 URL 方案的重定向 URI, 该方案注册到操作系统以调用客户端作为处理程序; 手动复制和粘贴凭据;运行本地 Web 服务器; 安装用户代理扩展;或者提供一个重定向 URI, 该 URI 标识客户端控制下的服务器托管资源, 而该资源随后使响应可供原生应用程序使用。

以前,原生应用常用嵌入式用户代理 (通常使用 web-view 实现)来进行 OAuth 授权请求。 这种方法有许多缺点,包括宿主应用能够复制用户凭据和 cookie, 以及用户需要在每个应用中从头开始认证。有关使用嵌入式用户代理 进行 OAuth 的缺点的更深入分析,请参见 第 8.5.1 节

使用系统浏览器的原生应用授权请求更加安全, 并且可以利用用户在设备上的认证状态。能够使用浏览器中 现有的认证会话可实现单点登录,因为用户每次使用新应用时 不需要向授权服务器重新认证(除非授权服务器策略要求)。

无需改变 OAuth 协议本身,就可以支持原生应用与浏览器之间的 授权流程,因为 OAuth 授权请求和响应已经以 URI 的形式定义。这涵盖了可用于应用间通信的 URI。 一些假定所有客户端都是机密 Web 客户端的 OAuth 服务器实现 将需要增加对公开原生应用客户端以及它们所使用的 重定向 URI 类型的理解,以支持此最佳实践。

8.1. 原生应用的客户端 认证

作为分发给多个用户的应用的一部分而静态包含的密钥 不是机密密钥,因为某个用户可以检查自己的副本并获知共享密钥。 因此,授权服务器不得要求原生应用客户端 使用共享密钥进行客户端认证,因为这除了客户端标识之外 没有任何价值,而客户端标识已经由 client_id 请求参数提供。

仍要求原生应用客户端使用静态包含的共享密钥的 授权服务器,必须将该客户端视为公开客户端 (如 第 2.1 节 中所定义),并且 不接受该密钥作为客户端身份的证明。没有 额外措施时,此类客户端会受到客户端冒充攻击 (参见 第 7.3.1 节)。

8.1.1. 原生应用客户端的 注册

除非使用类似 Dynamic Client Registration [RFC7591] 的机制来为 每个实例配置凭据,否则原生应用被归类为公开客户端, 如 第 2.1 节 中所定义,并且必须作为此类客户端向授权服务器注册。 授权服务器必须在客户端注册详情中记录客户端类型, 以便相应地识别和处理请求。

8.1.2. 原生应用 证明

草案规范 [I-D.ietf-oauth-attestation-based-client-auth] 定义了一种机制,原生应用可以使用该机制获得 与密钥绑定的证明,以向授权服务器或资源服务器认证。 这可以为移动应用的身份提供更高保证级别。

8.2. 在原生应用中使用应用间 URI 通信进行 OAuth

正如 Web 上的 OAuth 使用 URI 来发起 授权请求并将授权响应返回给请求网站一样, 原生应用也可以使用 URI 在设备浏览器中发起授权请求, 并将响应返回给请求的原生应用。

通过采用 Web 上用于 OAuth 的相同方法, Web 上所见的好处(例如单点登录会话的可用性以及 独立认证上下文的安全性)同样可在原生应用上下文中获得。 重用相同方法还通过依赖基于标准的 Web 流程来 降低实现复杂性并提高互操作性,而这些流程不特定于 某个平台。

原生应用必须使用外部用户代理来执行 OAuth 授权请求。这通过在浏览器中打开授权请求 (详见 第 8.3 节) 并使用会将授权响应返回给原生应用的重定向 URI (定义于 第 8.4 节) 来实现。

8.3. 从原生应用发起 授权请求

需要用户授权的原生应用,会按照 第 4.1 节 使用授权码授予类型创建授权请求 URI, 并使用能够被原生应用接收的重定向 URI。

原生应用授权请求中的重定向 URI 的功能 类似于基于 Web 的授权请求中的重定向 URI。 它不是将授权响应返回给 OAuth 客户端的服务器, 而是使原生应用所使用的重定向 URI 将响应返回给该应用。 第 8.4 节 记录了几种 可在不同平台中将授权响应返回给原生应用的重定向 URI 选项。 任何允许应用接收 URI 并检查其参数的重定向 URI 都是可行的。

构造授权请求 URI 后,应用使用平台特定的 API 在外部用户代理中打开该 URI。通常,所使用的外部用户代理是 默认浏览器,即系统上配置为处理 httphttps 方案 URI 的应用;但是,也可以使用不同的浏览器选择标准和 其他类别的外部用户代理。

此最佳实践将浏览器作为原生应用推荐的外部 用户代理。也可以使用专门为用户授权设计、 并且能够像浏览器一样处理授权请求和响应的外部用户代理。 其他外部用户代理,例如由授权服务器提供的原生应用, 可能满足此最佳实践中列出的标准,包括使用相同的重定向 URI 属性, 但其使用不在本规范范围内。

一些平台支持称为“应用内浏览器标签页”的浏览器功能, 其中应用可以在应用上下文内呈现一个浏览器标签页, 而无需切换应用,但仍保留浏览器的关键优势, 例如共享认证状态和安全上下文。在支持该功能的平台上, 出于可用性原因,建议应用将应用内浏览器标签页用于 授权请求。

8.4. 在原生应用中接收 授权响应

原生应用有几种重定向 URI 选项可用于 从浏览器接收授权响应,其可用性和用户体验 因平台而异。

8.4.1. 已声明的 "https" 方案 URI 重定向

一些操作系统,尤其是移动操作系统, 允许应用声明其控制域中的 https URI (参见 第 4.2.2 节,出自 [RFC9110])。 当浏览器遇到已声明 URI 时,不是在浏览器中加载页面, 而是启动原生应用,并将该 URI 作为启动参数提供。

此类 URI 可作为原生应用的重定向 URI。 对授权服务器而言,它们与普通的基于 Web 的 客户端重定向 URI 无法区分。示例如下:

https://app.example.com/oauth2redirect/example-provider

由于仅凭重定向 URI 不足以将公开原生 应用客户端与机密 Web 客户端区分开来, 第 8.1.1 节 要求 在客户端注册期间记录客户端类型,以使服务器能够确定客户端类型并 相应地采取行动。

与其他原生应用重定向选项相比,应用声明的 https 方案重定向 URI 具有一些优势, 因为目标应用的身份由操作系统向授权服务器保证。 因此,在可能的情况下,原生应用应优先使用它们而不是 其他选项。

8.4.2. 回环 接口重定向

能够在回环网络接口上打开端口而无需特殊权限的 原生应用(通常是桌面操作系统上的应用) 可以使用回环接口来接收 OAuth 重定向。

回环重定向 URI 使用 http 方案, 并由回环 IP 字面量以及客户端正在监听的任意端口构造。

也就是说,IPv4 为 http://127.0.0.1:{port}/{path}, IPv6 为 http://[::1]:{port}/{path}。下面是一个 使用随机分配端口的 IPv4 回环接口重定向示例:

http://127.0.0.1:51004/oauth2redirect/example-provider

下面是一个使用随机分配端口的 IPv6 回环接口重定向示例:

http://[::1]:61023/oauth2redirect/example-provider

虽然使用名称 localhost 的重定向 URI (即 http://localhost:{port}/{path})与回环 IP 重定向类似地工作,但不建议使用 localhost。 指定使用回环 IP 字面量而非 localhost 的 重定向 URI,可以避免无意中监听回环接口之外的网络接口。 它也不太容易受到客户端防火墙和用户设备上配置错误的主机名解析的影响。

对于回环 IP 重定向 URI,授权服务器必须允许 在请求时指定任意端口,以适应客户端在请求时 从操作系统获得可用临时端口的情况。

客户端不应假定设备支持某个特定版本的 Internet Protocol。建议客户端尝试使用 IPv4 和 IPv6 绑定到回环接口,并使用可用的版本。

8.4.3. 私用 URI 方案重定向

许多移动和桌面计算平台支持通过 URI 进行应用间 通信,方式是允许应用注册私用 URI 方案 (有时俗称“自定义 URL 方案”), 例如 com.example.app。当浏览器或其他应用尝试 加载带有私用 URI 方案的 URI 时,注册该方案的应用 会被启动以处理该请求。

许多支持私用 URI 方案的环境并不提供 声明某个方案并阻止其他方使用另一个应用方案的机制。 因此,使用私用 URI 方案的客户端容易受到 针对其重定向 URI 的潜在攻击,所以只有在前述更安全的选项 不可用时,才应使用此选项。

要使用私用 URI 方案重定向执行授权请求, 原生应用会使用标准授权请求启动浏览器, 但其中的重定向 URI 使用它在操作系统中注册的 私用 URI 方案。

在选择要与应用关联的 URI 方案时,应用必须使用 基于其控制下域名的 URI 方案,并按相反顺序表示, 这正如 第 3.8 节,出自 [RFC7595] 对私用 URI 方案的建议。

例如,控制域名 app.example.com 的应用可以使用 com.example.app 作为其方案。 一些授权服务器会基于域名分配客户端标识符,例如 client1234.usercontent.example.net,它也可以在 同样反转后用作该方案的域名。然而,像 myapp 这样的方案不满足此要求,因为它 并非基于域名。

当同一发布者有多个应用时,必须谨慎确保 每个方案在该组内都是唯一的。在使用基于反序域名的 应用标识符的平台上,这些标识符可以被重用为 OAuth 重定向的私用 URI 方案,以帮助避免此问题。

根据 第 3.2 节,出自 [RFC3986] 的要求,由于私用 URI 方案重定向没有命名权限, 因此方案组件之后只出现一个斜杠(/)。 使用私用 URI 方案的完整重定向 URI 示例为:

com.example.app:/oauth2redirect/example-provider

当授权服务器完成请求时,它会像通常一样 重定向到客户端的重定向 URI。由于该重定向 URI 使用私用 URI 方案,因此操作系统会启动原生应用, 并将该 URI 作为启动参数传入。然后,原生应用使用正常流程 处理授权响应。

8.5. 原生应用中的安全 考虑

8.5.1. 原生应用中的 嵌入式用户代理

嵌入式用户代理在技术上是授权原生应用的一种可行方法。 根据定义,这些嵌入式用户代理对于授权服务器的第三方使用而言并不安全, 因为托管嵌入式用户代理的应用可以访问用户完整的认证凭据, 而不仅仅是原本打算授予该应用的 OAuth 授权授予。 它们通常也会被操作系统沙箱化,并且依赖 Web 来源的 WebAuthn 等机制会被禁用。

在典型的基于 web-view 的嵌入式用户代理实现中, 宿主应用可以记录登录表单中输入的每一次按键, 以捕获用户名和密码,自动提交表单以绕过用户同意, 并复制会话 cookie,然后使用它们以用户身份执行 已认证操作。

即使由与授权服务器同属一方的可信应用使用, 嵌入式用户代理也违反最小权限原则,因为它们可以访问 比自身所需更强大的凭据,从而可能扩大攻击面。

鼓励用户在没有通常地址栏和浏览器所具有的 可见证书验证功能的嵌入式用户代理中输入凭据, 会使用户无法知道自己是否正在登录合法站点;即使确实是, 这也会训练他们认为在先不验证站点的情况下输入凭据是可以接受的。

除安全问题外,嵌入式用户代理不会与其他应用或浏览器 共享认证状态,这要求用户为每次授权请求都重新登录, 通常被认为是一种较差的用户体验。

8.5.2. 原生应用中的伪造外部 用户代理

发起授权请求的原生应用对用户界面具有 很大控制权,并且可能呈现一个伪造的外部用户代理, 即将嵌入式用户代理伪装成外部用户代理。

当所有善意行为者都使用外部用户代理时, 其优势在于安全专家能够检测恶意行为者, 因为任何伪造外部用户代理的人都可被证明是恶意的。 另一方面,如果善意与恶意行为者都使用嵌入式用户代理, 恶意行为者不需要伪造任何东西,从而更难被检测。 一旦检测到恶意应用,可能可以利用这些知识 在恶意软件扫描软件中将该应用签名列入黑名单, 采取移除措施(对于通过应用商店分发的应用), 以及其他步骤来降低该恶意应用的影响和传播。

授权服务器也可以直接防范伪造的外部 用户代理,方法是要求一种仅真正外部用户代理可用的认证因素。

在使用应用内浏览器标签页时特别关注安全的用户 也可以采取额外步骤,从应用内浏览器标签页中 在完整浏览器中打开请求,并在那里完成授权, 因为大多数应用内浏览器标签页模式的实现都提供此功能。

8.5.3. 原生应用中的恶意 外部用户代理

如果恶意应用能够将自己配置为操作系统中 https 方案 URI 的默认处理程序, 它就能够拦截使用默认浏览器的授权请求, 并滥用这种信任位置达到恶意目的,例如对用户进行网络钓鱼。

这种攻击并不限于 OAuth;以这种方式配置的恶意应用 会对用户构成超出原生应用 OAuth 使用范围的一般且持续风险。 许多操作系统通过要求明确的用户操作来更改 httphttps 方案 URI 的默认处理程序, 来缓解此问题。

8.5.4. 原生应用中的回环 重定向考虑

回环接口重定向 URI 可以使用 http 方案(即不使用 TLS)。这对于回环接口重定向 URI 是可接受的,因为 HTTP 请求从不离开设备。

客户端应仅在开始授权请求时打开网络端口, 并在响应返回后关闭该端口。

客户端应仅监听回环网络接口, 以避免受到其他网络行为者的干扰。

客户端应使用回环 IP 字面量而不是字符串 localhost, 如 第 8.4.2 节 所述。

9. 基于浏览器的应用

基于浏览器的应用是在 Web 浏览器中运行的客户端, 通常以 JavaScript 编写,也称为“单页应用”。这些类型的应用 具有与原生应用类似的特定安全考虑。

TODO:在 browser-based apps BCP 最终确定后,引入其规范性文本。

10. 与 OAuth 2.0 的差异

本草案整合了 OAuth 2.0 [RFC6749]、 OAuth 2.0 for Native Apps [RFC8252]、 Proof Key for Code Exchange [RFC7636]、 OAuth 2.0 for Browser-Based Apps [I-D.ietf-oauth-browser-based-apps]、 OAuth Security Best Current Practice [RFC9700] 以及 Bearer Token Usage [RFC6750] 中的功能。

如果后续草案更新或废止了原始 [RFC6749] 中的功能, 则本草案中的相应功能会按照后续草案中描述的规范性 变更进行更新,或被完全移除。

下面列出从 OAuth 2.0 发生变化的非规范性列表:

10.1. 移除 OAuth 2.0 Implicit grant

OAuth 2.0 Implicit grant 从 OAuth 2.1 中省略, 因为它已在 [RFC9700] 中弃用。

移除 Implicit grant 的意图是不再在授权响应中颁发访问令牌, 因为此类令牌容易泄露和注入,并且无法对客户端进行发送方约束。 此行为过去由客户端使用 response_type=token 参数表示。 OAuth 2.1 不再定义 response_type 参数的此值。

移除 response_type=token 不会影响从授权端点返回其他工件的 其他扩展响应类型, 例如由 [OpenID.Connect] 定义的 response_type=id_token

10.2. 令牌请求中的 Redirect URI 参数

在 OAuth 2.0 中,授权码流程中的令牌端点请求 (第 4.1.3 节,出自 [RFC6749]) 包含一个可选的 redirect_uri 参数。该参数意在防止 授权码注入攻击,并且如果原始授权请求中发送了 redirect_uri 参数,则它是必需的。只有当特定客户端 注册了多个重定向 URI 时,授权请求才要求 redirect_uri 参数。然而在实践中,许多授权服务器实现 即使只注册了一个重定向 URI,也要求授权请求包含 redirect_uri 参数,导致令牌端点也要求该 redirect_uri 参数。

在 OAuth 2.1 中,授权码注入通过 code_challengecode_verifier 参数防止, 因而在令牌请求中包含 redirect_uri 参数没有任何作用。 因此,它已被移除。

对于希望同时支持 OAuth 2.0 和 OAuth 2.1 客户端的 授权服务器的向后兼容性,授权服务器必须允许客户端在 令牌请求(第 4.1.3 节)中发送 redirect_uri 参数,并且必须按 [RFC6749] 中所述强制执行该参数。 授权服务器可以使用请求中的 client_id 来确定是否针对它知道将使用旧 OAuth 2.0 行为的特定客户端 强制执行此行为。

仅遵循 OAuth 2.1 建议的客户端不会在 令牌请求中发送 redirect_uri,因此将无法与 期望令牌请求中存在该参数的授权服务器兼容。

11. IANA 考虑

本文档不要求任何 IANA 操作。

所有引用的注册表都由 [RFC6749] 以及本工作所基于的相关文档定义。 本规范不要求对这些注册表进行更改。

12. 参考文献

12.1. 规范性参考文献

[BCP195]
Saint-Andre, P., "安全使用传输层安全性 (TLS) 的建议", .
[RFC2119]
Bradner, S., "用于 RFC 中表示 要求级别的关键词", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/info/rfc2119>.
[RFC2617]
Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S., Leach, P., Luotonen, A., and L. Stewart, "HTTP 认证: Basic 和 Digest 访问认证", RFC 2617, DOI 10.17487/RFC2617, , <https://www.rfc-editor.org/info/rfc2617>.
[RFC3629]
Yergeau, F., "UTF-8,ISO 10646 的转换 格式", STD 63, RFC 3629, DOI 10.17487/RFC3629, , <https://www.rfc-editor.org/info/rfc3629>.
[RFC3986]
Berners-Lee, T., Fielding, R., and L. Masinter, "统一资源 标识符 (URI):通用语法", STD 66, RFC 3986, DOI 10.17487/RFC3986, , <https://www.rfc-editor.org/info/rfc3986>.
[RFC4949]
Shirey, R., "Internet 安全 术语表,版本 2", FYI 36, RFC 4949, DOI 10.17487/RFC4949, , <https://www.rfc-editor.org/info/rfc4949>.
[RFC5234]
Crocker, D., Ed. and P. Overell, "语法规范的增强 BNF: ABNF", STD 68, RFC 5234, DOI 10.17487/RFC5234, , <https://www.rfc-editor.org/info/rfc5234>.
[RFC6749]
Hardt, D., Ed., "OAuth 2.0 授权框架", RFC 6749, DOI 10.17487/RFC6749, , <https://www.rfc-editor.org/info/rfc6749>.
[RFC6750]
Jones, M. and D. Hardt, "OAuth 2.0 授权框架:承载令牌用法", RFC 6750, DOI 10.17487/RFC6750, , <https://www.rfc-editor.org/info/rfc6750>.
[RFC7235]
Fielding, R., Ed. and J. Reschke, Ed., "超文本传输协议 (HTTP/1.1): 认证", RFC 7235, DOI 10.17487/RFC7235, , <https://www.rfc-editor.org/info/rfc7235>.
[RFC7521]
Campbell, B., Mortimore, C., Jones, M., and Y. Goland, "OAuth 2.0 客户端认证与 授权授予的断言框架", RFC 7521, DOI 10.17487/RFC7521, , <https://www.rfc-editor.org/info/rfc7521>.
[RFC7523]
Jones, M., Campbell, B., and C. Mortimore, "OAuth 2.0 客户端认证 和授权授予的 JSON Web Token (JWT) Profile", RFC 7523, DOI 10.17487/RFC7523, , <https://www.rfc-editor.org/info/rfc7523>.
[RFC7595]
Thaler, D., Ed., Hansen, T., and T. Hardie, "URI 方案的指南与 注册程序", BCP 35, RFC 7595, DOI 10.17487/RFC7595, , <https://www.rfc-editor.org/info/rfc7595>.
[RFC8174]
Leiba, B., "RFC 2119 关键词中大写与 小写的歧义", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/info/rfc8174>.
[RFC8252]
Denniss, W. and J. Bradley, "原生应用的 OAuth 2.0", BCP 212, RFC 8252, DOI 10.17487/RFC8252, , <https://www.rfc-editor.org/info/rfc8252>.
[RFC8259]
Bray, T., Ed., "JavaScript Object Notation (JSON) 数据交换格式", STD 90, RFC 8259, DOI 10.17487/RFC8259, , <https://www.rfc-editor.org/info/rfc8259>.
[RFC8446]
Rescorla, E., "传输层 安全性 (TLS) 协议版本 1.3", RFC 8446, DOI 10.17487/RFC8446, , <https://www.rfc-editor.org/info/rfc8446>.
[RFC9110]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP 语义", STD 97, RFC 9110, DOI 10.17487/RFC9110, , <https://www.rfc-editor.org/info/rfc9110>.
[RFC9111]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP 缓存", STD 98, RFC 9111, DOI 10.17487/RFC9111, , <https://www.rfc-editor.org/info/rfc9111>.
[RFC9207]
Meyer zu Selhausen, K. and D. Fett, "OAuth 2.0 授权服务器颁发者 标识", RFC 9207, DOI 10.17487/RFC9207, , <https://www.rfc-editor.org/info/rfc9207>.
[RFC9700]
Lodderstedt, T., Bradley, J., Labunets, A., and D. Fett, "OAuth 2.0 安全最佳当前实践", BCP 240, RFC 9700, DOI 10.17487/RFC9700, , <https://www.rfc-editor.org/info/rfc9700>.
[USASCII]
Institute, A. N. S., "编码字符 集 -- 7 位美国信息交换标准码,ANSI X3.4", .
[W3C.REC-xml-20081126]
Bray, T., Paoli, J., Sperberg-McQueen, C. M., Maler, E., and F. Yergeau, "可扩展标记语言", , <https://www.w3.org/TR/REC-xml/REC-xml-20081126.xml>.
[WHATWG.CORS]
WHATWG, "Fetch Standard:CORS 协议", , <https://fetch.spec.whatwg.org/#http-cors-protocol>.
[WHATWG.URL]
WHATWG, "URL", , <https://url.spec.whatwg.org/>.

12.2. 资料性参考文献

[CSP-2]
"Content Security Policy Level 2", , <https://www.w3.org/TR/CSP2>.
[I-D.bradley-oauth-jwt-encoded-state]
Bradley, J., Lodderstedt, T., and H. Zandbelt, "使用 JWT 在 OAuth 2 state 参数中编码 claim", 进行中的 工作, Internet-Draft, draft-bradley-oauth-jwt-encoded-state-09, , <https://datatracker.ietf.org/doc/html/draft-bradley-oauth-jwt-encoded-state-09>.
[I-D.ietf-oauth-attestation-based-client-auth]
Looker, T., Bastian, P., and C. Bormann, "OAuth 2.0 基于证明的客户端认证", 进行中的 工作, Internet-Draft, draft-ietf-oauth-attestation-based-client-auth-07, , <https://datatracker.ietf.org/doc/html/draft-ietf-oauth-attestation-based-client-auth-07>.
[I-D.ietf-oauth-browser-based-apps]
Parecki, A., De Ryck, P., and D. Waite, "基于浏览器的 应用程序的 OAuth 2.0", 进行中的工作, Internet-Draft, draft-ietf-oauth-browser-based-apps-26, , <https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps-26>.
[I-D.ietf-oauth-rfc7523bis]
Jones, M. B., Campbell, B., Mortimore, C., and F. Skokan, "OAuth 2.0 JSON Web Token (JWT) 客户端认证和 基于断言的授权授予的更新", 进行中的 工作, Internet-Draft, draft-ietf-oauth-rfc7523bis-05, , <https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rfc7523bis-05>.
[NIST800-63]
Burr, W., Dodson, D., Newton, E., Perlner, R., Polk, T., Gupta, S., and E. Nabbus, "NIST Special Publication 800-63-1,信息安全", , <http://csrc.nist.gov/publications/>.
[OMAP]
Huff, J., Schlacht, D., Nadalin, A., Simmons, J., Rosenberg, P., Madsen, P., Ace, T., Rickelton-Abdi, C., and B. Boyer, "在线多媒体 授权协议:授权访问 Internet 多媒体资源的行业标准", , <https://www.svta.org/product/online-multimedia-authorization-protocol/>.
[OpenID.Connect]
Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., and C. Mortimore, "OpenID Connect Core 1.0 incorporating errata set 2", , <https://openid.net/specs/openid-connect-core-1_0.html>.
[OpenID.Discovery]
Sakimura, N., Bradley, J., Jones, M., and E. Jay, "OpenID Connect Discovery 1.0 incorporating errata set 2", , <https://openid.net/specs/openid-connect-discovery-1_0.html>.
[OpenID.Messages]
Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., Mortimore, C., and E. Jay, "OpenID Connect Messages 1.0", , <http://openid.net/specs/openid-connect-messages-1_0.html>.
[owasp_redir]
"OWASP Cheat Sheet Series - 未验证重定向与 转发", , <https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html>.
[RFC6265]
Barth, A., "HTTP 状态管理 机制", RFC 6265, DOI 10.17487/RFC6265, , <https://www.rfc-editor.org/info/rfc6265>.
[RFC6819]
Lodderstedt, T., Ed., McGloin, M., and P. Hunt, "OAuth 2.0 威胁模型与安全考虑", RFC 6819, DOI 10.17487/RFC6819, , <https://www.rfc-editor.org/info/rfc6819>.
[RFC7009]
Lodderstedt, T., Ed., Dronia, S., and M. Scurtescu, "OAuth 2.0 令牌撤销", RFC 7009, DOI 10.17487/RFC7009, , <https://www.rfc-editor.org/info/rfc7009>.
[RFC7519]
Jones, M., Bradley, J., and N. Sakimura, "JSON Web Token (JWT)", RFC 7519, DOI 10.17487/RFC7519, , <https://www.rfc-editor.org/info/rfc7519>.
[RFC7591]
Richer, J., Ed., Jones, M., Bradley, J., Machulak, M., and P. Hunt, "OAuth 2.0 动态客户端 注册协议", RFC 7591, DOI 10.17487/RFC7591, , <https://www.rfc-editor.org/info/rfc7591>.
[RFC7592]
Richer, J., Ed., Jones, M., Bradley, J., and M. Machulak, "OAuth 2.0 动态客户端注册管理协议", RFC 7592, DOI 10.17487/RFC7592, , <https://www.rfc-editor.org/info/rfc7592>.
[RFC7636]
Sakimura, N., Ed., Bradley, J., and N. Agarwal, "OAuth 公开客户端的代码交换证明密钥", RFC 7636, DOI 10.17487/RFC7636, , <https://www.rfc-editor.org/info/rfc7636>.
[RFC7662]
Richer, J., Ed., "OAuth 2.0 令牌 自省", RFC 7662, DOI 10.17487/RFC7662, , <https://www.rfc-editor.org/info/rfc7662>.
[RFC8414]
Jones, M., Sakimura, N., and J. Bradley, "OAuth 2.0 授权 服务器元数据", RFC 8414, DOI 10.17487/RFC8414, , <https://www.rfc-editor.org/info/rfc8414>.
[RFC8628]
Denniss, W., Bradley, J., Jones, M., and H. Tschofenig, "OAuth 2.0 设备授权授予", RFC 8628, DOI 10.17487/RFC8628, , <https://www.rfc-editor.org/info/rfc8628>.
[RFC8705]
Campbell, B., Bradley, J., Sakimura, N., and T. Lodderstedt, "OAuth 2.0 Mutual-TLS 客户端认证 和证书绑定访问令牌", RFC 8705, DOI 10.17487/RFC8705, , <https://www.rfc-editor.org/info/rfc8705>.
[RFC8707]
Campbell, B., Bradley, J., and H. Tschofenig, "OAuth 2.0 的资源 指示符", RFC 8707, DOI 10.17487/RFC8707, , <https://www.rfc-editor.org/info/rfc8707>.
[RFC9068]
Bertocci, V., "OAuth 2.0 访问令牌的 JSON Web Token (JWT) Profile", RFC 9068, DOI 10.17487/RFC9068, , <https://www.rfc-editor.org/info/rfc9068>.
[RFC9126]
Lodderstedt, T., Campbell, B., Sakimura, N., Tonge, D., and F. Skokan, "OAuth 2.0 推送 授权请求", RFC 9126, DOI 10.17487/RFC9126, , <https://www.rfc-editor.org/info/rfc9126>.
[RFC9396]
Lodderstedt, T., Richer, J., and B. Campbell, "OAuth 2.0 丰富 授权请求", RFC 9396, DOI 10.17487/RFC9396, , <https://www.rfc-editor.org/info/rfc9396>.
[RFC9449]
Fett, D., Campbell, B., Bradley, J., Lodderstedt, T., Jones, M., and D. Waite, "OAuth 2.0 证明持有 (DPoP)", RFC 9449, DOI 10.17487/RFC9449, , <https://www.rfc-editor.org/info/rfc9449>.
[RFC9470]
Bertocci, V. and B. Campbell, "OAuth 2.0 Step Up 认证质询协议", RFC 9470, DOI 10.17487/RFC9470, , <https://www.rfc-editor.org/info/rfc9470>.
[W3C.REC-html401-19991224]
Hors, A. L., Ed., Raggett, D., Ed., and I. Jacobs, Ed., "HTML 4.01 规范", W3C REC REC-html401-19991224, W3C REC-html401-19991224, , <https://www.w3.org/TR/1999/REC-html401-19991224/>.

附录 A. 增强巴科斯-瑙尔范式 (ABNF) 语法

本节为本规范中定义的元素提供增强巴科斯-瑙尔范式 (ABNF) 语法描述,使用 [RFC5234] 的记法。下面的 ABNF 是以 Unicode 代码点 [W3C.REC-xml-20081126] 定义的;这些字符通常 以 UTF-8 编码。元素按照首次定义的顺序呈现。

下面的一些定义使用来自 [RFC3986] 的 "URI-reference" 定义。

下面的一些定义使用这些公共定义:

VSCHAR     = %x20-7E
NQCHAR     = %x21 / %x23-5B / %x5D-7E
NQSCHAR    = %x20-21 / %x23-5B / %x5D-7E

A.1. "client_id" 语法

client_id 元素定义于 第 2.4.1 节

client-id     = *VSCHAR

A.2. "client_secret" 语法

client_secret 元素定义于 第 2.4.1 节

client-secret = *VSCHAR

A.3. "response_type" 语法

response_type 元素定义于 第 4.1.1 节第 6.4 节

response-type = response-name *( SP response-name )
response-name = 1*response-char
response-char = "_" / DIGIT / ALPHA

A.4. "scope" 语法

scope 元素定义于 第 1.4.1 节

 scope       = scope-token *( SP scope-token )
 scope-token = 1*NQCHAR

A.5. "state" 语法

state 元素定义于 第 4.1.1 节第 4.1.2 节第 4.1.2.1 节

 state      = 1*VSCHAR

A.6. "redirect_uri" 语法

redirect_uri 元素定义于 第 4.1.1 节第 4.1.3 节

 redirect-uri      = URI-reference

A.7. "error" 语法

error 元素定义于 第 4.1.2.1 节第 3.2.4 节第 5.3 节

 error             = 1*NQSCHAR

A.8. "error_description" 语法

error_description 元素定义于 第 4.1.2.1 节第 3.2.4 节第 5.3 节

 error-description = 1*NQSCHAR

A.9. "error_uri" 语法

error_uri 元素定义于 第 4.1.2.1 节第 3.2.4 节第 5.3 节

 error-uri         = URI-reference

A.10. "grant_type" 语法

grant_type 元素定义于 第 3.2.2 节

 grant-type = grant-name / URI-reference
 grant-name = 1*name-char
 name-char  = "-" / "." / "_" / DIGIT / ALPHA

A.11. "code" 语法

code 元素定义于 第 4.1.3 节

 code       = 1*VSCHAR

A.12. "access_token" 语法

access_token 元素定义于 第 3.2.3 节

 access-token = 1*VSCHAR

A.13. "token_type" 语法

token_type 元素定义于 第 3.2.3 节第 6.1 节

 token-type = type-name / URI-reference
 type-name  = 1*name-char
 name-char  = "-" / "." / "_" / DIGIT / ALPHA

A.14. "expires_in" 语法

expires_in 元素定义于 第 3.2.3 节

 expires-in = 1*DIGIT

A.15. "refresh_token" 语法

refresh_token 元素定义于 第 3.2.3 节第 4.3 节

 refresh-token = 1*VSCHAR

A.16. 端点参数 语法

新端点参数的语法定义于 第 6.2 节

 param-name = 1*name-char
 name-char  = "-" / "." / "_" / DIGIT / ALPHA

A.17. "code_verifier" 语法

code_verifier 的 ABNF 如下。

code-verifier = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39

A.18. "code_challenge" 语法

code_challenge 的 ABNF 如下。

code-challenge = 43*128unreserved
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39

附录 B. application/x-www-form-urlencoded 媒体类型的使用

[RFC6749] 发布时, application/x-www-form-urlencoded 媒体类型定义在 [W3C.REC-html401-19991224] 的第 17.13.4 节中, 但未在 IANA MIME Media Types 注册表 (http://www.iana.org/assignments/media-types) 中注册。此外,该定义并不完整,因为它没有考虑非 US-ASCII 字符。

为了解决使用此媒体类型生成内容时的这一不足, 名称和值必须首先使用 UTF-8 字符编码方案 [RFC3629] 进行编码; 然后所得八位字节序列需要进一步使用 [W3C.REC-html401-19991224] 中定义的转义规则进行编码。

当解析使用此媒体类型的内容中的数据时, 反转名称/值编码后得到的名称和值也因此需要被视为 八位字节序列,并使用 UTF-8 字符编码方案解码。

例如,由六个 Unicode 代码点组成的值: (1) U+0020 (SPACE)、(2) U+0025 (PERCENT SIGN)、 (3) U+0026 (AMPERSAND)、(4) U+002B (PLUS SIGN)、 (5) U+00A3 (POUND SIGN) 和 (6) U+20AC (EURO SIGN),会被编码为 以下八位字节序列(使用十六进制表示):

20 25 26 2B C2 A3 E2 82 AC

然后在内容中表示为:

+%25%26%2B%C2%A3%E2%82%AC

附录 C. 序列化

本规范中的各种消息使用下面描述的方法之一进行序列化。 本节描述这些序列化方法的语法;其他章节描述它们何时可以以及必须使用。 注意,并非所有方法都可用于所有消息。

C.1. 查询字符串 序列化

为了使用查询字符串序列化来序列化参数, Client 通过使用由 [WHATWG.URL] 定义的 application/x-www-form-urlencoded 格式,将参数和值添加到 URL 的查询组件中来 构造该字符串。查询字符串序列化通常用于 HTTP GET 请求。

C.2. 表单编码 序列化

参数及其值通过将参数名称和值添加到 HTTP 请求的实体正文中, 并使用 附录 B 定义的 application/x-www-form-urlencoded 格式进行表单序列化。表单序列化通常用于 HTTP POST 请求。

C.3. JSON 序列化

参数通过在最高结构层级添加每个参数, 被序列化为 JSON [RFC8259] 对象结构。参数名称和字符串值表示为 JSON 字符串。数值表示为 JSON 数字。布尔值表示为 JSON 布尔值。除非另有指定,省略的参数和没有值的参数应从对象中省略, 而不表示为 JSON null 值。参数可以具有 JSON 对象或 JSON 数组作为其值。参数顺序不重要,并且可以变化。

附录 D. 扩展

以下是在发布时已确立的一些扩展列表:

附录 E. 致谢

本规范是 OAuth Working Group 的工作,其起点 基于以下规范的内容:OAuth 2.0 Authorization Framework (RFC 6749)、 OAuth 2.0 for Native Apps (RFC 8252)、OAuth Security Best Current Practice, 以及 OAuth 2.0 for Browser-Based Apps。编辑者感谢所有参与创建这些 本规范所基于的规范的人。

编辑者还要感谢以下人员,他们的想法、反馈、 修正以及措辞帮助塑造了本版本规范:Andrii Deinega、 Bob Hamburg、Brian Campbell、Daniel Fett、Deng Chao、Emelia Smith、Falko、Filip Skokan、Joseph Heenan、 Justin Richer、Karsten Meyer zu Selhausen、Michael Jones、Michael Peck、Roberto Polli、Tim Würtele 和 Vittorio Bertocci。

围绕本规范的讨论也曾在 2021 年和 2022 年的 OAuth Security Workshop 上进行。作者感谢该研讨会的组织者(Guido Schmitz、Steinar Noem 和 Daniel Fett)举办了有助于协作和社区输入的活动。

附录 F. 文档历史

[[ 将从最终规范中移除 ]]

-15

-14

-13

-12

-11

-10

-09

-08

-07

-06

-05

-04

-03

-02

-01

-00

作者地址

Dick Hardt
Hellō
Aaron Parecki
Okta
Torsten Lodderstedt
SPRIND