跳到主要内容

2. 令牌交换请求和响应 (Token Exchange Request and Response)

2.1. 请求 (Request)

客户端通过使用 [RFC6749] 第 4.5 节定义的扩展授权类型机制, 向授权服务器的令牌端点发起令牌请求, 以请求一个安全令牌.

客户端对授权服务器的认证使用 OAuth 2.0 提供的常规机制完成. [RFC6749] 第 2.3.1 节定义了基于密码的客户端认证, 但客户端认证是可扩展的, 也可以使用其他机制. 例如, [RFC7523] 定义了使用 bearer JSON Web Token (JWT) [JWT] 的客户端认证. 支持哪些客户端认证方法, 以及是否允许未经认证或未识别的客户端, 都是部署决策, 由授权服务器自行决定. 注意, 省略客户端认证会使任何持有受损令牌的人都能通过 STS 将该受损令牌进一步转换为其他令牌. 因此, 客户端认证允许 STS 执行额外授权检查, 判断哪些实体可以模拟其他实体, 或可以接收来自其他实体的委派.

客户端使用 HTTP POST 方法, 通过扩展授权类型向令牌端点发起令牌交换请求. 下列参数使用 application/x-www-form-urlencoded 格式和 UTF-8 字符编码包含在 HTTP 请求实体主体中, 如 [RFC6749] 附录 B 所述.

grant_type 必需. 值 urn:ietf:params:oauth:grant-type:token-exchange 表示正在执行令牌交换.

resource 可选. 一个 URI, 表示客户端打算使用所请求安全令牌的目标服务或资源. 这使授权服务器能够针对目标适当地应用策略, 例如确定要签发的令牌类型和内容, 或决定令牌是否以及如何加密. 在许多情况下, 客户端并不了解与其交互的系统在逻辑上的组织方式, 只知道它打算使用令牌的服务 URI. resource 参数允许客户端在令牌交换请求中提供位置, 通常是 https URL, 以与访问该资源时相同的形式向授权服务器说明其打算在何处使用已签发令牌. 授权服务器通常能够把资源 URI 值映射到适当策略. resource 参数的值必须是 [RFC3986] 第 4.3 节规定的绝对 URI, 可以包含查询组件, 但禁止包含片段组件. 可以使用多个 resource 参数表示已签发令牌打算用于列出的多个资源. 关于 resource 参数的其他背景和用途, 见 [OAUTH-RESOURCE].

audience 可选. 目标服务的逻辑名称, 表示客户端打算在该服务处使用所请求的安全令牌. 它的目的类似于 resource 参数, 但由客户端为目标服务提供逻辑名称. 对该名称的解释要求其值是客户端和授权服务器都能理解的内容. OAuth 客户端标识符、SAML 实体标识符 [OASIS.saml-core-2.0-os] 和 OpenID Connect Issuer Identifier [OpenID.Core] 都是可能用作 audience 参数值的示例. 但是, 与给定授权服务器一起使用的 audience 值必须在该服务器内唯一, 以确保它们能被正确解释为预期的值类型. 可以使用多个 audience 参数表示已签发令牌打算用于列出的多个受众. audienceresource 参数可以一起使用, 以逻辑名称和资源 URI 混合的方式表示多个目标服务.

scope 可选. 一个由空格分隔、区分大小写的字符串列表, 如 [RFC6749] 第 3.3 节所定义. 它允许客户端在令牌将被使用的服务或资源上下文中指定所请求安全令牌的期望作用域. 作用域的值及其相关语义是服务特定的, 预期会在相关服务文档中说明.

requested_token_type 可选. 第 3 节所述的一个标识符, 表示所请求安全令牌的类型. 如果未指定请求的类型, 则已签发令牌的类型由授权服务器自行决定, 并且可以由 resourceaudience 参数所指示服务或资源的需求知识决定.

subject_token 必需. 一个安全令牌, 表示请求所代表的一方的身份. 通常, 此令牌的主体将是响应该请求而签发的安全令牌的主体.

subject_token_type 必需. 第 3 节所述的一个标识符, 表示 subject_token 参数中安全令牌的类型.

actor_token 可选. 一个安全令牌, 表示执行动作的一方的身份. 通常, 这会是被授权使用所请求安全令牌并代表主体行事的一方.

actor_token_type 第 3 节所述的一个标识符, 表示 actor_token 参数中安全令牌的类型. 当请求中存在 actor_token 参数时此参数必需, 否则禁止包含.

在处理请求时, 授权服务器必须针对所指示的令牌类型执行适当的验证过程; 如果存在参与者令牌, 还必须针对该参与者令牌所指示的令牌类型执行适当的验证过程. 任何特定令牌的有效性准则和细节不在本文档范围内, 而是取决于相应令牌类型及其内容.

在不存在一次性使用或其他令牌类型特定语义的情况下, 执行令牌交换这一行为不会影响主体令牌或参与者令牌的有效性. 此外, 交换是一次性事件, 不会在输入令牌和输出令牌之间建立紧密绑定. 因此, 例如输出令牌的过期时间可能受输入令牌影响, 但输入令牌的续期或延期并不预期反映到输出令牌属性中. 传播令牌吊销事件仍可能是合适或期望的做法. 不过, 这不是 STS 协议的一般属性, 而会取决于特定实现、令牌类型或部署.

2.1.1. 资源、受众和作用域之间的关系 (Relationship between Resource, Audience, and Scope)

请求令牌时, 客户端可以通过 audienceresource 参数指示它打算使用该令牌的期望目标服务, 也可以通过 scope 参数指示所请求令牌的期望作用域. 这种请求的语义是: 客户端请求一个具有所请求作用域、并可在所有所请求目标服务处使用的令牌. 实际上, 令牌所请求的访问权限是所有目标服务上的所有作用域的笛卡尔积.

授权服务器可能不愿或无法满足任何令牌请求, 但当请求的访问权限非常宽泛时, 请求无法满足的可能性会显著增大. 因此, 在不了解部署中各系统关系的情况下, 客户端应谨慎控制所请求访问权限的宽度, 尤其是目标服务的数量. 授权服务器可以使用第 2.2.2 节定义的 invalid_target 错误码, 告知客户端其同时请求访问的目标服务过多.

2.2. 响应 (Response)

授权服务器使用来自令牌端点的普通 OAuth 2.0 响应来回应令牌交换请求, 如 [RFC6749] 第 5 节所规定. 以下小节提供附加细节和说明.

2.2.1. 成功响应 (Successful Response)

如果请求有效, 并且满足授权服务器的所有策略和其他准则, 则成功令牌响应通过使用 application/json 媒体类型 (如 [RFC8259] 所规定) 和 HTTP 200 状态码, 将以下参数加入 HTTP 响应实体主体来构造. 这些参数通过把每个参数加入顶层而序列化为 JavaScript Object Notation (JSON) 结构. 参数名和字符串值作为 JSON 字符串包含. 数值作为 JSON 数字包含. 参数顺序无关紧要, 可以变化.

access_token 必需. 授权服务器为响应该令牌交换请求而签发的安全令牌. 这里使用 [RFC6749] 第 5.1 节中的 access_token 参数承载所请求的令牌, 从而使本令牌交换协议能够使用为令牌端点定义的现有 OAuth 2.0 请求和响应结构. 标识符 access_token 是出于历史原因使用的, 已签发令牌不必是 OAuth 访问令牌.

issued_token_type 必需. 第 3 节所述的一个标识符, 表示已签发安全令牌的表示形式.

token_type 必需. 一个大小写不敏感的值, 指明已签发访问令牌的使用方法, 如 [RFC6749] 第 7.1 节所规定. 它向客户端提供如何利用访问令牌访问受保护资源的信息. 例如, [RFC6750] 中规定的值 "Bearer" 表示已签发安全令牌是 bearer 令牌, 客户端只需按原样呈现该令牌, 除令牌本身内容外无需任何额外资格证明. 注意, 该参数的含义不同于 issued_token_type 参数; 后者声明已签发安全令牌的表示形式. 术语 "token type" 更常用于表示安全令牌的结构或语法表示, 本规范所有 *_token_type 参数也都采用这种含义. 如果已签发令牌不是访问令牌, 或不能作为访问令牌使用, 则使用 token_type 值 "N_A" 表示 OAuth 2.0 token_type 标识符在该上下文中不适用.

expires_in 推荐. 授权服务器签发令牌的有效生命周期, 单位为秒. 客户端通常不愿或无法检查令牌内容, 此参数提供一种一致且与令牌类型无关的指示, 说明该令牌预计可保持有效多久. 例如, 值 1800 表示令牌将在响应生成后三十分钟过期.

scope 如果已签发安全令牌的作用域与客户端请求的作用域相同, 则可选; 否则必需.

refresh_token 可选. 当交换是把一个临时凭据 (subject_token) 换成另一个临时凭据 (已签发令牌), 并用于其他上下文时, 通常不会签发刷新令牌. 在令牌交换客户端需要即使原始凭据不再有效也能访问某个资源的情况下, 可以签发刷新令牌 (例如用户不在场或离线场景, 此时用户已不再与客户端保持活动会话). 本规范的配置文件或部署应清楚记录客户端在什么条件下可以预期针对 urn:ietf:params:oauth:grant-type:token-exchange 授权类型请求获得刷新令牌.

2.2.2. 错误响应 (Error Response)

如果请求本身无效, 或者 subject_tokenactor_token 因任何原因无效, 或基于策略不可接受, 授权服务器必须按照 [RFC6749] 第 5.2 节构造错误响应. error 参数的值必须是 invalid_request 错误码.

如果授权服务器不愿或无法为 resourceaudience 参数所指示的任何目标服务签发令牌, 则错误响应中应当使用 invalid_target 错误码.

授权服务器可以按照 [RFC6749] 第 5.2 节所述, 使用 error_description 包含关于错误原因的附加信息.

也可以视情况使用其他错误码.

2.3. 令牌交换示例 (Example Token Exchange)

下面的示例展示了一个假设的令牌交换, 其中 OAuth 资源服务器在交换期间承担客户端角色. 它把在受保护资源请求中收到的访问令牌交换为一个新令牌, 该新令牌将用于调用后端服务 (示例中的额外换行和缩进仅为显示方便).

图 1 展示资源服务器收到一个受保护资源请求, 该请求在 Authorization 头部中包含 OAuth 访问令牌, 如 [RFC6750] 第 2.1 节所规定.

GET /resource HTTP/1.1
Host: frontend.example.com
Authorization: Bearer accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC

图 1: 受保护资源请求

在图 2 中, 资源服务器为令牌交换承担客户端角色, 并按照第 2.1 节规定的请求, 将图 1 请求中的访问令牌发送给授权服务器. subject_token 参数的值承载该访问令牌, subject_token_type 参数的值表示它是 OAuth 2.0 访问令牌. 充当客户端角色的资源服务器使用其标识符和密钥, 通过 HTTP Basic 认证方案向授权服务器认证. resource 参数指示后端服务的位置 <https://backend.example.com/api>, 已签发令牌将在那里使用.

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

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange
&resource=https%3A%2F%2Fbackend.example.com%2Fapi
&subject_token=accVkjcJyb4BWCxGsndESCJQbdFMogUC5PbRDqceLTC
&subject_token_type=
urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token

图 2: 令牌交换请求

授权服务器验证令牌交换请求中提交的客户端凭据和 subject_token. 根据 resource 参数, 授权服务器能够确定应应用于该请求的适当策略, 并签发一个适合在 <https://backend.example.com> 使用的令牌. 图 3 所示响应的 access_token 参数包含新令牌, 它本身是一个有效期一分钟的 bearer OAuth 访问令牌. 该令牌恰好是 JWT; 但是, 它的结构和格式对客户端是不透明的, 因此 issued_token_type 只表明它是访问令牌.

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

{
"access_token":"eyJhbGciOiJFUzI1NiIsImtpZCI6IjllciJ9.eyJhdWQiOiJo
dHRwczovL2JhY2tlbmQuZXhhbXBsZS5jb20iLCJpc3MiOiJodHRwczovL2FzLmV
4YW1wbGUuY29tIiwiZXhwIjoxNDQxOTE3NTkzLCJpYXQiOjE0NDE5MTc1MzMsIn
N1YiI6ImJkY0BleGFtcGxlLmNvbSIsInNjb3BlIjoiYXBpIn0.40y3ZgQedw6rx
f59WlwHDD9jryFOr0_Wh3CGozQBihNBhnXEQgU85AI9x3KmsPottVMLPIWvmDCM
y5-kdXjwhw",
"issued_token_type":
"urn:ietf:params:oauth:token-type:access_token",
"token_type":"Bearer",
"expires_in":60
}

图 3: 令牌交换响应

随后, 资源服务器可以使用新获得的访问令牌向后端服务器发起请求, 如图 4 所示.

GET /api HTTP/1.1
Host: backend.example.com
Authorization: Bearer eyJhbGciOiJFUzI1NiIsImtpZCI6IjllciJ9.eyJhdWQ
iOiJodHRwczovL2JhY2tlbmQuZXhhbXBsZS5jb20iLCJpc3MiOiJodHRwczovL2
FzLmV4YW1wbGUuY29tIiwiZXhwIjoxNDQxOTE3NTkzLCJpYXQiOjE0NDE5MTc1M
zMsInN1YiI6ImJkY0BleGFtcGxlLmNvbSIsInNjb3BlIjoiYXBpIn0.40y3ZgQe
dw6rxf59WlwHDD9jryFOr0_Wh3CGozQBihNBhnXEQgU85AI9x3KmsPottVMLPIW
vmDCMy5-kdXjwhw

图 4: 后端受保护资源请求

附录 A 中可以找到更多示例.