| RFC 9396 | OAuth-RAR | 2023年5月 |
| Lodderstedt 等 | 标准轨道 | [页] |
本文档规定了一个新的参数 authorization_details,该参数
用于在 OAuth 消息中携带细粒度授权数据。¶
这是一份互联网标准轨道文档。¶
本文档是互联网工程任务组 (IETF) 的产物。它代表了 IETF 社区的共识。它已经过公开审查,并已由 互联网工程指导组 (IESG) 批准发布。关于互联网标准的 更多信息可在 RFC 7841 第 2 节中获得。¶
有关本文档当前状态、任何勘误以及如何提供反馈的信息, 可从 https://www.rfc-editor.org/info/rfc9396 获取。¶
Copyright (c) 2023 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
"OAuth 2.0 授权框架" [RFC6749] 定义了
scope 参数,使 OAuth
客户端能够
指定访问令牌所请求的范围,即受限能力。
这种机制足以实现静态场景和
粗粒度授权请求,例如“授予我对
资源所有者资料的读取访问权限”。但是,它不足以指定
细粒度授权要求,例如“请允许我向商户 A 转账 45 欧元”
或“请授予我对目录 A 的读取访问权限以及对文件 X 的写入访问权限”。¶
本规范引入了一个新的参数 authorization_details,
允许客户端使用 JSON
[RFC8259] 数据结构的表达能力来指定其细粒度授权要求。¶
例如,信用转账的授权请求(在若干开放银行计划中称为“支付 发起”)可以使用如下 JSON 对象来表示:¶
{
"type": "payment_initiation",
"locations": [
"https://example.com/payments"
],
"instructedAmount": {
"currency": "EUR",
"amount": "123.50"
},
"creditorName": "Merchant A",
"creditorAccount": {
"bic":"ABCIDEFFXXX",
"iban": "DE02100100109307118603"
},
"remittanceInformationUnstructured": "Ref Number Merchant"
}
该对象包含有关预期支付的详细信息,例如金额、 货币和债权人,这些信息是告知用户并获得其同意所必需的。授权 服务器(AS)和相应的资源服务器(RS)(提供支付发起 API)将共同 执行该同意。¶
关于开放银行和电子签名领域中新用例所带来挑战的全面 讨论,请参见 [Transaction-Auth]。¶
除了支持自定义授权请求之外,本规范还 引入了一组可跨不同 API 使用的通用数据类型字段。¶
本文档中的关键词 "MUST"、"MUST NOT"、"REQUIRED"、"SHALL"、"SHALL NOT"、"SHOULD"、"SHOULD NOT"、"RECOMMENDED"、"NOT RECOMMENDED"、 "MAY" 和 "OPTIONAL" 应按照 BCP 14 [RFC2119] [RFC8174] 中的说明来解释,但前提是它们以全大写形式出现,如此处所示。¶
本规范使用由 "OAuth 2.0 授权 框架" [RFC6749] 定义的术语:"access token"、"refresh token"、 "authorization server" (AS)、"resource server" (RS)、"authorization endpoint"、 "authorization request"、"authorization response"、"token endpoint"、 "grant type"、"access token request"、"access token response" 和 "client"。¶
请求参数 authorization_details 以 JSON 记法包含一个
对象数组。每个 JSON 对象都包含用于指定某种
资源类型的授权要求的数据。资源类型或访问要求由
type 字段决定,其定义如下:¶
type:
type 字段的值决定了包含该字段的对象
允许的内容。在 AS 的上下文中,该值对于所描述的 API 是唯一的。此字段为
REQUIRED。¶
authorization_details 数组 MAY 包含
相同 type 的多个条目。¶
图 2 展示了一个
使用上面示例数据的 payment_initiation 类型
authorization_details:¶
[
{
"type": "payment_initiation",
"actions": [
"initiate",
"status",
"cancel"
],
"locations": [
"https://example.com/payments"
],
"instructedAmount": {
"currency": "EUR",
"amount": "123.50"
},
"creditorName": "Merchant A",
"creditorAccount": {
"iban": "DE02100100109307118603"
},
"remittanceInformationUnstructured": "Ref Number Merchant"
}
]
图 3 展示了一个组合请求, 请求访问账户信息并请求发起支付的权限:¶
[
{
"type": "account_information",
"actions": [
"list_accounts",
"read_balances",
"read_transactions"
],
"locations": [
"https://example.com/accounts"
]
},
{
"type": "payment_initiation",
"actions": [
"initiate",
"status",
"cancel"
],
"locations": [
"https://example.com/payments"
],
"instructedAmount": {
"currency": "EUR",
"amount": "123.50"
},
"creditorName": "Merchant A",
"creditorAccount": {
"iban": "DE02100100109307118603"
},
"remittanceInformationUnstructured": "Ref Number Merchant"
}
]
具有 account_information 和
payment_initiation 这两个 type 字段的 JSON 对象表示
AS 用于请求同意的不同 authorization_details。¶
本规范定义了一组通用数据字段,这些字段设计为可在 不同类型的 API 中使用。本规范不要求 API 定义使用这些 通用字段,而是将其作为可重用的通用组件 提供给 API 设计者使用。所有字段的允许值由 受保护的 API 决定,具体由特定的 "type" 值定义。¶
locations:
actions:
datatypes:
identifier:
privileges:
当不同通用数据字段组合使用时,
客户端请求的权限是所有值的乘积。
该对象表示请求对象内列出的所有 actions 值
在对象内列出的所有 locations 值处,用于对象内列出的所有
datatypes
值。在下面的示例中,客户端请求对
customer_information API 中属于客户的
contacts 和 photos 同时拥有 read 和 write
访问权限。
如果该请求被授予,客户端
会认为它能够使用 API 所定义的任何权限组合,
例如对照片的读取访问权限以及对
联系人的写入访问权限。¶
[
{
"type": "customer_information",
"locations": [
"https://example.com/customers"
],
"actions": [
"read",
"write"
],
"datatypes": [
"contacts",
"photos"
]
}
]
如果客户端希望对其访问有更精细的控制,它可以发送
多个对象。在此示例中,
客户端请求对同一 API 端点中的 contacts 进行 read 访问,并
对 photos 进行 write 访问。
如果该请求被授予,客户端将无法写入联系人。¶
[
{
"type": "customer_information",
"locations": [
"https://example.com/customers"
],
"actions": [
"read"
],
"datatypes": [
"contacts"
]
},
{
"type": "customer_information",
"locations": [
"https://example.com/customers"
],
"actions": [
"write"
],
"datatypes": [
"photos"
]
}
]
API MAY 定义其自己的扩展,但受
相应授权对象的 type 约束。
预计 API 设计者将结合使用本规范定义的
通用数据字段以及 API 自身特定的
字段。下面的非规范性
示例展示了作为两个不同虚构 API type 值的一部分,
同时使用通用字段和 API 特定字段。第一个
访问请求包含此处指定的 actions、locations 和
datatypes
字段,以及 API 特定的 geolocation
字段,表示访问在给定坐标拍摄的照片。
第二个访问请求包含此处指定的 actions 和
identifier 字段,以及 API 特定的
currency 字段。¶
[
{
"type":"photo-api",
"actions":[
"read",
"write"
],
"locations":[
"https://server.example.net/",
"https://resource.local/other"
],
"datatypes":[
"metadata",
"images"
],
"geolocation":[
{
"lat":-32.364,
"lng":153.207
},
{
"lat":-35.364,
"lng":158.207
}
]
},
{
"type":"financial-transaction",
"actions":[
"withdraw"
],
"identifier":"account-14-32-32-3",
"currency":"USD"
}
]
如果该请求被批准,生成的访问令牌的访问权限将 是两个 API 各自请求的访问类型的并集,与上文相同。¶
authorization_details 授权请求参数可用于
在所有使用 scope 参数实现相同目的的位置指定授权要求,示例包括:¶
在 [RFC6749] 定义的授权请求的情况下,实现者 MAY 考虑 使用推送授权请求 [RFC9126] 来 改进流程的安全性、隐私性和可靠性。详见第 12、13 和 11.4 节。¶
参数编码由相应上下文决定。在根据 [RFC6749] 的
授权请求上下文中,该
参数使用序列化 JSON 的 application/x-www-form-urlencoded 格式进行编码,
如 图 8 所示,使用 第 2 节 中的示例(换行仅用于显示目的):¶
基于 authorization_details 参数中提供的数据,AS
将请求用户同意所请求的访问权限。¶
在 图 9 中,客户端希望获得 对账户信息的访问权限并发起支付:¶
[
{
"type": "account_information",
"actions": [
"list_accounts",
"read_balances",
"read_transactions"
],
"locations": [
"https://example.com/accounts"
]
},
{
"type": "payment_initiation",
"actions": [
"initiate",
"status",
"cancel"
],
"locations": [
"https://example.com/payments"
],
"instructedAmount": {
"currency": "EUR",
"amount": "123.50"
},
"creditorName": "Merchant A",
"creditorAccount": {
"iban": "DE02100100109307118603"
},
"remittanceInformationUnstructured": "Ref Number Merchant"
}
]
authorization_details 和 scope 可以在
同一个授权请求中用于携带独立的授权要求。¶
本规范支持 authorization_details 和 scope 的组合使用,
部分原因是允许现有基于 OAuth 的应用逐步迁移到
专门使用 authorization_details。RECOMMENDED 某个给定 API 仅使用一种要求
指定形式。¶
AS MUST 将这两组要求 彼此结合起来处理,用于给定授权请求。AS 如何 合并这些参数的细节特定于受保护的 API,超出本 规范的范围。¶
在收集用户同意时,AS MUST 呈现 授权请求所表示的合并后的要求集合。¶
如果资源所有者授予客户端所请求的访问权限,AS 将
向客户端颁发与相应
authorization_details(以及 scope 值,如适用)关联的令牌。¶
如
[RFC8707] 中定义的 resource
授权请求参数,
可用于进一步
确定所请求 scope 可应用到哪些资源。resource
参数不会影响 AS 处理
authorization_details 授权请求参数的方式。¶
AS MUST 拒绝处理任何未知授权
详情类型,或不符合相应类型定义的授权详情。若
authorization_details 结构中的对象存在以下任一情况,AS MUST 中止处理并以错误
invalid_authorization_details 响应客户端:¶
authorization_details 令牌请求参数可用于指定
客户端希望 AS 分配给访问令牌的授权详情。AS 会检查
底层授权(在 authorization_code、
refresh_token 等授权类型的情况下)或客户端策略(在
client_credentials 授权类型的情况下)是否允许颁发具有所请求授权
详情的访问令牌。否则,AS 会以错误码
invalid_authorization_details 拒绝请求(类似于 invalid_scope)。¶
除了 [RFC6749] 中定义的令牌响应参数之外,AS MUST 还返回由资源所有者授予并分配给相应
访问令牌的
authorization_details。¶
令牌响应中分配给访问令牌的授权详情由
对应令牌请求的 authorization_details 参数决定。如果
客户端未指定 authorization_details 令牌请求参数,AS
可自行决定生成的 authorization_details。¶
AS MAY 在返回给客户端的
authorization_details 中省略值。¶
对于我们的贯穿示例,它将如下所示:¶
为了使 RS 能够执行授权过程中批准的授权详情,
AS MUST 将这些数据提供给 RS。AS
MAY 将 authorization_details 字段添加到 JSON
Web Token (JWT) 格式的访问令牌中,或添加到令牌自省响应中。¶
如果访问令牌是 JWT [RFC7519],则 RECOMMENDED AS 将针对特定受众过滤后的授权详情对象添加为顶层 声明。¶
AS 通常还会向 JWT 添加 RS 处理请求所需的其他 声明,例如用户 ID、角色和交易特定数据。特定 RS 需要哪些声明由该 RS 与 AS 之间的特定策略定义。¶
下面展示了上述支付发起 示例的一个示例 JWT 内容:¶
{
"iss": "https://as.example.com",
"sub": "24400320",
"aud": "a7AfcPcsl2",
"exp": 1311281970,
"acr": "psd2_sca",
"txn": "8b4729cc-32e4-4370-8cf0-5796154d1296",
"authorization_details": [
{
"type": "https://scheme.example.com/payment_initiation",
"actions": [
"initiate",
"status",
"cancel"
],
"locations": [
"https://example.com/payments"
],
"instructedAmount": {
"currency": "EUR",
"amount": "123.50"
},
"creditorName": "Merchant A",
"creditorAccount": {
"iban": "DE02100100109307118603"
},
"remittanceInformationUnstructured": "Ref Number Merchant"
}
],
"debtorAccount": {
"iban": "DE40100100103307118608",
"user_role": "owner"
}
}
在这种情况下,AS 向基于 JWT 的 访问令牌添加了以下示例声明:¶
令牌自省 [RFC7662] 提供了一种手段,使
RS 能够查询 AS 以
确定有关访问令牌的信息。如果 AS 在其响应中包含该令牌的授权详情信息,
则该信息 MUST 以
authorization_details 作为自省响应 JSON
对象的顶层成员来传达。authorization_details 成员 MUST 包含
第 2 节 中定义的相同结构,
可能会针对发起自省请求的 RS 进行过滤和扩展。¶
下面是支付发起 示例的一个自省响应示例:¶
{
"active": true,
"sub": "24400320",
"aud": "s6BhdRkqt3",
"exp": 1311281970,
"acr": "psd2_sca",
"txn": "8b4729cc-32e4-4370-8cf0-5796154d1296",
"authorization_details": [
{
"type": "https://scheme.example.com/payment_initiation",
"actions": [
"initiate",
"status",
"cancel"
],
"locations": [
"https://example.com/payments"
],
"instructedAmount": {
"currency": "EUR",
"amount": "123.50"
},
"creditorName": "Merchant123",
"creditorAccount": {
"iban": "DE02100100109307118603"
},
"remittanceInformationUnstructured": "Ref Number Merchant"
}
],
"debtorAccount": {
"iban": "DE40100100103307118608",
"user_role": "owner"
}
}
为了通告其对此特性的支持,授权详情
类型的受支持列表通过元数据参数
authorization_details_types_supported 包含在 AS 元数据响应 [RFC8414] 中,该参数是一个 JSON 数组。¶
下面的示例对此进行了说明:¶
{
...
"authorization_details_types_supported":[
"payment_initiation",
"account_information"
]
}
客户端 MAY 使用客户端注册元数据参数
authorization_details_types 指示它们在请求授权时
将使用的授权详情类型,该参数是一个 JSON 数组。¶
下面的示例对此进行了说明:¶
{
...
"authorization_details_types":[
"payment_initiation"
]
}
向 AS 注册授权详情类型超出 本规范的范围。¶
支持本规范的通用 AS 实现应提供 以下基本功能:¶
authorization_details 参数¶
authorization_code 和 refresh_token。¶
不同授权详情类型之间,授权详情的处理和呈现将有显著差异。 因此,实现应支持对相应行为进行 自定义。特别是,实现应允许 部署:¶
支持此类自定义的一种方法是提供一种机制, 允许注册扩展模块,每个模块负责呈现 相应的用户同意,并负责为通过 结构化访问令牌或令牌自省响应向 RS 提供所需数据而进行的任何转换。¶
实现可以允许部署使用机器可读模式
语言来定义授权详情类型,以便根据此类模式创建和验证
授权详情对象。例如,如果某个授权详情
type 使用 JSON Schema [JSON.Schema] 来定义,则该 JSON Schema 标识符可用作
相应授权详情对象中的 type 值。¶
不过请注意,type 值是 AS
以及必要时客户端和 RS 所理解的标识符。
本规范不假设 type 值会指向
机器可读模式格式,也不假设系统中的任何一方(例如客户端、AS 或 RS)
会以任何特定方式解引用或处理 type 字段的内容。¶
在 OAuth 授权请求的情况下,
authorization_details 参数会通过用户代理发送,这使其容易受到用户修改的影响。如果
authorization_details 的完整性是一个关注点,则客户端 MUST 保护
authorization_details 免受篡改和替换。这可以通过使用 [RFC9101] 中定义的签名请求对象对请求进行签名来实现,或结合 [RFC9126] 使用
[RFC9101] 中定义的 request_uri 授权
请求参数,将请求对象的 URI 传递给 AS。¶
authorization_details 参数中的所有字符串比较都要
按照 [RFC8259] 的定义执行。在评估字符串值等价性时,不得执行额外的
转换或规范化。¶
通用数据字段 locations 允许客户端指定其
打算在何处使用某项授权,即可以将权限明确分配给 RS。
在有多个 RS 的情况下,这通过
受众限制防止了无意的客户端授权(例如,某个
read scope 值可能同时适用于电子邮件以及云服务)。¶
AS MUST 正确清理和处理
authorization_details 中传递的数据,以防止注入攻击。¶
实现者以保护隐私的方式设计和使用授权详情 尤其重要。¶
必须防止 authorization_details 中包含的任何敏感个人数据
泄漏,例如通过 referrer 头泄漏。实现选项包括使用
[RFC9101] 中定义的加密请求对象,或通过利用
[RFC9126] 以及
[RFC9101] 中定义的 request_uri 授权请求参数,
在客户端和 AS 之间通过端到端加密连接传输
authorization_details。后一种方式不需要应用级加密,
但它需要客户端和 AS 之间进行另一次消息交换。¶
即使请求数据已加密,攻击者仍可能通过 AS 来获知用户 数据:将加密请求数据注入到其控制的设备上的授权请求中, 并使用 AS 的用户同意屏幕以明文显示(解密后的)用户数据。 实现需要考虑此攻击向量并实施适当的对策,例如 只显示部分数据,或在可能的情况下确定假定的用户上下文是否仍然 相同(在用户认证之后)。¶
AS 在与客户端或 RS 共享
authorization_details 时,需要考虑隐私影响。AS 应按照本地策略确定的
“需要知道”原则与这些方共享此数据。¶
以下参数已注册到 由 [RFC6749] 建立的 "OAuth Parameters" 注册表 [IANA.OAuth.Parameters] 中。¶
以下值已注册到由 [RFC7662] 建立的 IANA "OAuth Token Introspection Response" 注册表中。¶
以下值已注册到由 [RFC7591] 建立的 [IANA.OAuth.Parameters] 的 IANA "OAuth Dynamic Client Registration Metadata" 注册表中。¶
OpenID Connect [OIDC]
指定了基于 JSON 的 claims 请求参数,可用于以细粒度
且保护隐私的方式指定客户端(作为 OpenID Connect Relying Party)希望接收的
声明,并将这些声明分配给某些传递机制,即
ID Token 或 userinfo 响应。¶
scope 值 openid 与附加
参数 claims 的组合,可以像任何非 OIDC scope 值一样,与 authorization_details 一起使用。¶
或者,也可以为 OpenID Connect 设置一种授权详情类型。本节给出了这种授权详情类型可能 是什么样子的示例,但定义此授权详情类型超出本规范的范围。¶
这些假设示例试图将授权过程中 OpenID Connect 部分特有的所有细节封装到授权 JSON 对象中。¶
claim_sets:
profile¶
max_age:
acr_values:
claims:
claims JSON 结构¶
这是对若干声明集的简单请求。¶
[
{
"type": "openid",
"locations": [
"https://op.example.com/userinfo"
],
"claim_sets": [
"email",
"profile"
]
}
]
[
{
"type": "openid",
"locations": [
"https://op.example.com/userinfo"
],
"max_age": 86400,
"acr_values": "urn:mace:incommon:iap:silver",
"claims": {
"userinfo": {
"given_name": {
"essential": true
},
"nickname": null,
"email": {
"essential": true
},
"email_verified": {
"essential": true
},
"picture": null,
"http://example.com/claims/groups": null
},
"id_token": {
"auth_time": {
"essential": true
}
}
}
}
]
以下示例基于 ETSI TS 119 432 [ETSI] 中提出的远程电子 签名概念,以及用于远程签名创建的 Cloud Signature Consortium (CSC) API [CSC]。¶
[
{
"type": "sign",
"locations": [
"https://signing.example.com/signdoc"
],
"credentialID": "60916d31-932e-4820-ba82-1fcead1c9ea3",
"documentDigests": [
{
"hash": "sTOgwOm+474gFj0q0x1iSNspKqbcse4IeiqlDg/HWuI=",
"label": "Credit Contract"
},
{
"hash": "HZQzZmMAIWekfGH0/ZKW1nsdt0xg3H6bZYztgsMTLw0=",
"label": "Contract Payment Protection Insurance"
}
],
"hashAlgorithmOID": "2.16.840.1.101.3.4.2.1"
}
]
顶层字段具有以下含义:¶
credentialID:
documentDigests:
hash 字段)的数组。此外,对应的 label
字段向用户标识相应文档,例如用于用户同意。¶
hashAlgorithm:
AS 应请求用户同意为该结构中列出的 文档创建签名。客户端使用作为该过程结果颁发的访问令牌 调用相应签名服务处的文档签名 API,以实际 创建签名。该访问令牌绑定到客户端、用户 ID 以及用户 已同意的哈希(和签名算法)。¶
此示例受到允许第三方访问公民 税务申报和收入报表的 API 启发,例如用于确定其信用状况。¶
[
{
"type": "tax_data",
"locations": [
"https://taxservice.govehub.no.example.com"
],
"actions":"read_tax_declaration",
"periods": ["2018"],
"duration_of_access": 30,
"tax_payer_id": "23674185438934"
}
]
顶层字段具有以下含义:¶
这两个示例受到挪威电子健康系统中使用的 API 要求启发。¶
在此用例中,物理治疗师坐在自己的计算机前, 使用本地电子健康记录(EHR)系统。他们希望查看某个患者的电子 患者记录,并且还希望从另一个系统(可能在另一个机构或国家服务中) 获取患者的病历条目。对此类数据的访问由 API 提供。¶
在 API 处授权该请求所需的信息仅由 EHR 系统知道,并且必须提交给 API。¶
在第一个示例中,授权详情对象包含一个 组织的标识符。在这种情况下,API 需要知道给定组织是否具有 处理个人健康信息以访问敏感数据的合法依据。¶
"authorization_details": {
"type": "patient_record",
"requesting_entity": {
"type": "Practitioner",
"identifier": [
{
"system": "urn:oid:2.16.578.1.12.4.1.4.4",
"value": "1234567"
}],
"practitioner_role": {
"organization": {
"identifier": {
"system": "urn:oid:2.16.578.1.12.4.1.2.101",
"type": "ENH",
"value": "[organizational number]"
}
}
}
}
}
在第二个示例中,API 需要更多信息来授权 请求。在这种情况下,授权详情对象包含有关 健康机构以及用户在请求时当前职业的附加信息。 这一额外详细级别可同时用于授权和数据最小化。¶
[
{
"type": "patient_record",
"location": "https://fhir.example.com/patient",
"actions": [
"read"
],
"patient_identifier": [
{
"system": "urn:oid:2.16.578.1.12.4.1.4.1",
"value": "12345678901"
}
],
"reason_for_request": "Clinical treatment",
"requesting_entity": {
"type": "Practitioner",
"identifier": [
{
"system": "urn:oid:2.16.578.1.12.4.1.4.4",
"value": "1234567"
}
],
"practitioner_role": {
"organization": {
"identifier": [
{
"system": "urn:oid:2.16.578.1.12.4.1.2.101",
"type": "ENH",
"value": "<organizational number>"
}
],
"type": {
"coding": [
{
"system":
"http://hl7.example.org/fhir/org-type",
"code": "dept",
"display": "Hospital Department"
}
]
},
"name": "Akuttmottak"
},
"profession": {
"coding": [
{
"system": "http://snomed.example.org/sct",
"code": "36682004",
"display": "Physical therapist"
}
]
}
}
}
}
]
字段描述:¶
patient_identifier:
reason_for_request:
requesting_entity:
在此用例中,AS 对请求者进行身份验证,该请求者不是 患者,并基于策略批准访问。¶
我们感谢 Daniel Fett、Sebastian Ebling、Dave Tonge、Mike Jones、Nat Sakimura 和 Rob Otto 在本 规范编写期间提供的宝贵反馈。¶
我们还感谢 Vladimir Dzhuvinov、 Takahiko Kawasaki、Daniel Fett、 Dave Tonge、Travis Spencer、 Joergen Binningsboe、Aamund Bremer、 Steinar Noem、Francis Pouatcha、 Jacob Ideskog、Hannes Tschofenig 和 Aaron Parecki 对本规范提供的宝贵 反馈。¶