RFC 9396
OAuth 2.0 Rich Authorization Requests
RFC 9396: OAuth 2.0 Rich Authorization Requests โจ
Okay bestie, this one's the Standards Track spec ๐ It's published by the IETF (Internet Engineering Task Force), dated May 2023, and it's part of the ISSN 2070-1721 series. The main authors are T. Lodderstedt (yes.com), J. Richer (Bespoke Engineering), and B. Campbell (Ping Identity). The header doesn't list anything it obsoletes or updates. ๐ฆ
Abstract
This document specifies a new parameter, authorization_details, that carries fine-grained authorization data inside OAuth messages. ๐
Status of This Memo
This is an Internet Standards Track document. ๐ธ
It's a product of the Internet Engineering Task Force (IETF) and represents the consensus of the IETF community. It went through public review and was approved for publication by the Internet Engineering Steering Group (IESG). If you want more about Internet Standards, check Section 2 of RFC 7841. โจ
You can find the current status of this document, any errata, and how to send feedback at https://www.rfc-editor.org/info/rfc9396. ๐
Copyright Notice
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) as they're in effect on the publication date. Please read these carefully because they spell out your rights and restrictions for this document. ๐ซถ Code Components pulled from this document must include the Revised BSD License text described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as the Revised BSD License describes.
Table of Contents
1. Introduction
1.1. Conventions and Terminology
2. Request Parameter "authorization_details"
2.1. Authorization Details Types
2.2. Common Data Fields
3. Authorization Request
3.1. Relationship to the "scope" Parameter
3.2. Relationship to the "resource" Parameter
4. Authorization Response
5. Authorization Error Response
6. Token Request
6.1. Comparing Authorization Details
7. Token Response
7.1. Enriched Authorization Details in Token Response
8. Token Error Response
9. Resource Servers
9.1. JWT-Based Access Tokens
9.2. Token Introspection
10. Metadata
11. Implementation Considerations
11.1. Using Authorization Details in a Certain Deployment
11.2. Minimal Implementation Support
11.3. Use of Machine-Readable Type Schemas
11.4. Large Requests
12. Security Considerations
13. Privacy Considerations
14. IANA Considerations
14.1. OAuth Parameters Registration
14.2. JSON Web Token Claims Registration
14.3. OAuth Token Introspection Response Registration
14.4. OAuth Authorization Server Metadata Registration
14.5. OAuth Dynamic Client Registration Metadata Registration
14.6. OAuth Extensions Error Registration
15. References
15.1. Normative References
15.2. Informative References
Appendix A. Additional Examples
A.1. OpenID Connect
A.2. Remote Electronic Signing
A.3. Access to Tax Data
A.4. eHealth
Acknowledgements
Authors' Addresses
1. Introduction ๐
The OAuth 2.0 Authorization Framework [RFC6749] gives OAuth clients the scope parameter, which lets them ask for a specific scope (the limited capability) on an access token. That's enough for static setups and coarse-grained requests, like "give me read access to the resource owner's profile." ๐ But it isn't enough for fine-grained requirements, like "please let me transfer an amount of 45 Euros to Merchant A" or "please give me read access to directory A and write access to file X." ๐
This spec adds a new parameter, authorization_details, so clients can state their fine-grained authorization requirements using the expressiveness of JSON [RFC8259] data structures. โจ
Say you want an authorization request for a credit transfer (called "payment initiation" in lots of open banking initiatives). It can be written as a JSON object like this: ๐ฆ
{
"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"
}
Figure 1: Example of an Authorization Request for a Credit Transfer
That object holds all the juicy payment details, like amount, currency, and creditor, which are needed to inform the user and get their consent. ๐ The authorization server (AS) and the matching resource server (RS) (the one providing the payment initiation API) will enforce this consent together. ๐
For a deeper dive into the challenges from new use cases in the open banking and electronic signing spaces, check out [Transaction-Auth]. ๐ง
On top of custom authorization requests, this spec also introduces a set of common data type fields that can be used across different APIs. ๐ซถ
1.1. Conventions and Terminology
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. ๐
This spec uses the terms "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", and "client", all defined by The OAuth 2.0 Authorization Framework [RFC6749]. ๐
2. Request Parameter "authorization_details" ๐
The request parameter authorization_details holds, in JSON notation, an array of objects. Each JSON object has the data that specifies the authorization requirements for one type of resource. The type of resource or access requirement is set by the type field, which is defined like this: ๐ธ
type: An identifier for the authorization details type, as a string. The value of the type field decides what the object that contains it is allowed to hold. The value must be unique for the described API within the context of the AS. This field is REQUIRED. ๐ฅ
An authorization_details array MAY contain multiple entries of the same type. โจ
Figure 2 shows an authorization_details of type payment_initiation, using the example data from above: ๐ฆ
[
{
"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"
}
]
Figure 2: Example of "authorization_details" for a Credit Transfer
Figure 3 shows a combined request that asks for access to account information and permission to start a payment: ๐ง
[
{
"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"
}
]
Figure 3: Example of "authorization_details" for a Combined Request
The JSON objects with type fields of account_information and payment_initiation are the different authorization_details the AS uses to ask for consent. ๐
| Note: The AS will make this data subsequently available to the
| respective RSs (see Section 9).
2.1. Authorization Details Types ๐
The AS controls how the value of the type parameter is interpreted, along with which object fields that type allows. ๐ Still, the type value is generally documented and meant for developers to use. It is RECOMMENDED that API designers pick type values that can be copied without ambiguity. ๐ For example, some glyphs have multiple Unicode code points for the same visual character, so a developer could accidentally type a different character than the one the AS defined. ๐ To cut down on that confusion, you can limit the value to ASCII [RFC0020] characters, provide a machine-readable listing of data type values, or tell developers to copy and paste straight from the documentation. ๐ซถ
If an application or API is expected to be deployed across different servers, like in an open standard, the API designer is RECOMMENDED to use a collision-resistant namespace under their control, such as a URI the API designer controls. ๐
This example shows how an implementation could use the namespace https://scheme.example.org/ to get collision-resistant type values: ๐ฆ
{
"type": "https://scheme.example.org/files",
"locations": [
"https://example.com/files"
],
"permissions": [
{
"path": "/myfiles/A",
"access": [
"read"
]
},
{
"path": "/myfiles/A/X",
"access": [
"read",
"write"
]
}
]
}
Figure 4: Example of "authorization_details" with a URL as Type Identifier
2.2. Common Data Fields โจ
okay so this section is giving "reusable building blocks" ๐งฑ๐ the spec defines a set of common data fields that are meant to work across lots of different kinds of APIs. you don't HAVE to use them, they're just there for API designers to grab and use. the allowed values for every field depend on the API being protected, which is decided by the "type" value.
locations: an array of strings for where the resource or RS lives. these are usually URIs pointing at the RS, and this field lets a client point at a particular RS (more on that in Section 12) ๐actions: an array of strings for the kinds of actions to take at the resource ๐ฌdatatypes: an array of strings for the kinds of data being requested from the resource ๐identifier: a string that names one specific resource available at the API ๐privileges: an array of strings for the types or levels of privilege being asked for at the resource ๐
when you mix these fields together, the permissions get multiplied ๐งฎ the client is basically asking for the product of all the values. so the object means "all the actions, at all the locations, for all the datatypes" listed inside it. in the example below, the client wants read AND write access to the contacts and photos of customers in a customer_information API. if that gets approved, the client can mix and match any combo of rights the API offers, like reading the photos or writing the contacts. no cap, that's a lot of power ๐ญ
[ { "type": "customer_information", "locations": [ "https://example.com/customers" ], "actions": [ "read", "write" ], "datatypes": [ "contacts", "photos" ] } ]
Figure 5: Example of "authorization_details" with Common Data Fields
if the client wants tighter control over its access, it can send multiple objects ๐ฏ in this one, the client asks for read access to the contacts and write access to the photos, both on the same API endpoint. if that's granted, the client can't write to the contacts. iconic restraint ๐
[ { "type": "customer_information", "locations": [ "https://example.com/customers" ], "actions": [ "read" ], "datatypes": [ "contacts" ] }, { "type": "customer_information", "locations": [ "https://example.com/customers" ], "actions": [ "write" ], "datatypes": [ "photos" ] } ]
Figure 6: Example of "authorization_details" with Common Data Fields in Multiple Objects
an API MAY define its own extensions, depending on the type of the authorization object ๐งฉ API designers are expected to mix the common fields from this spec with their own API-specific ones. the next example is non-normative (so it's just illustrating, not a rule โจ) and shows both kinds of fields across two made-up API types. the first request has actions, locations, and datatypes from this spec, plus the API-specific geolocation field for photos taken at certain coordinates ๐ธ the second one has actions and identifier from this spec, plus the API-specific currency field ๐ธ
[ { "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" } ]
Figure 7: Example of "authorization_details" Using Common and Extension Data Fields
if this gets approved, the access token ends up with the union of the requested access for each of the two APIs, same vibe as before ๐
3. Authorization Request ๐
the authorization_details authorization request parameter can be used to specify authorization requirements anywhere the scope parameter already does that job. the main places are:
- authorization requests as specified in [RFC6749]
- device authorization requests as specified in [RFC8628]
- backchannel authentication requests as defined in [OID-CIBA]
for authorization requests defined in [RFC6749], implementers MAY consider using pushed authorization requests [RFC9126] to level up the security, privacy, and reliability of the flow ๐ check out Sections 12, 13, and 11.4 for the details.
how the parameter gets encoded depends on the context you're in ๐งช for an authorization request under [RFC6749], the parameter is encoded using the application/x-www-form-urlencoded format of the serialized JSON. Figure 8 shows this, using the example from Section 2 (the line breaks are just for display, not part of the actual request) ๐
GET /authorize?response_type=code &client_id=s6BhdRkqt3 &state=af0ifjsldkj &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb &code_challenge_method=S256 &code_challenge=K2-ltc83acc4h0c9w6ESC_rEMTJ3bwc-uCHaoeK1t8U &authorization_details=%5B%7B%22type%22%3A%22account%5Finfo rmation%22%2C%22actions%22%3A%5B%22list%5Faccounts%22%2C%22 read%5Fbalances%22%2C%22read%5Ftransactions%22%5D%2C%22loca tions%22%3A%5B%22https%3A%2F%2Fexample%2Ecom%2Faccounts%22% 5D%7D%2C%7B%22type%22%3A%22payment%5Finitiation%22%2C%22act ions%22%3A%5B%22initiate%22%2C%22status%22%2C%22cancel%22%5 D%2C%22locations%22%3A%5B%22https%3A%2F%2Fexample%2Ecom%2Fp ayments%22%5D%2C%22instructedAmount%22%3A%7B%22currency%22% 3A%22EUR%22%2C%22amount%22%3A%22123%2E50%22%7D%2C%22credito rName%22%3A%22Merchant%20A%22%2C%22creditorAccount%22%3A%7B %22iban%22%3A%22DE02100100109307118603%22%7D%2C%22remittanc eInformationUnstructured%22%3A%22Ref%20Number%20Merchant%22 %7D%5D HTTP/1.1 Host: server.example.com
Figure 8: Example of Authorization Request with "authorization_details"
once the authorization_details data is in, the AS asks the user for consent to the requested access permissions ๐โโ๏ธ
Note: The user may also grant a subset of the requested authorization details.
in Figure 9, the client wants to see account information and start a payment ๐ณ
[ { "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" } ]
Figure 9: URL Decoded "authorization_details"
3.1. Relationship to the "scope" Parameter ๐ค
authorization_details and scope can show up in the same authorization request to carry independent authorization requirements ๐
this spec supports using both together partly so existing OAuth-based apps can move over to authorization_details bit by bit, instead of all at once ๐ฆ it is RECOMMENDED that a given API use only one form of requirement specification.
the AS MUST process both sets of requirements together for the given authorization request. how the AS combines them is specific to the APIs being protected, and that's outside the scope of this spec ๐ง
when collecting user consent, the AS MUST present the merged set of requirements from the authorization request ๐
if the resource owner grants the requested access, the AS will issue tokens to the client that are tied to the respective authorization_details (and scope values, if applicable) ๐๏ธ
3.2. Relationship to the "resource" Parameter ๐
the resource authorization request parameter, defined in [RFC8707], can be used to narrow down which resources the requested scope can apply to โจ it has no impact at all on how the AS processes the authorization_details parameter. separate lanes, bestie ๐ฃ๏ธ
4. Authorization Response ๐ฌ
this specification does not define any extensions to the authorization response. nothing new here, we're good ๐ซถ
5. Authorization Error Response ๐จ
the AS MUST refuse to process any unknown authorization details type, or any authorization details that don't conform to their type definition. the AS MUST abort processing and respond to the client with the error invalid_authorization_details if any of the following are true of the objects in the authorization_details structure:
- contains an unknown authorization details type value,
- is an object of a known type but contains unknown fields,
- contains fields of the wrong type for the authorization details type,
- contains fields with invalid values for the authorization details type, or
- is missing required fields for the authorization details type.
6. Token Request ๐ช
the authorization_details token request parameter can be used to say which authorization details the client wants the AS to put on an access token. the AS checks whether the underlying grant (for grant types like authorization_code, refresh_token, etc.) or the client's policy (for grant type client_credentials) allows issuing an access token with the requested authorization details. if not, the AS refuses the request with the error code invalid_authorization_details, which works similarly to invalid_scope ๐ฅบ
6.1. Comparing Authorization Details โจ
okay so, a lot of OAuth moves let the AS and RS make security calls based on whether a request is asking for "more" or "less" than an earlier one ๐ like, when you refresh a token, the client can ask for a new access token with "fewer permissions" than the resource owner already approved. the token just carries the smaller set of permissions, and the resource owner's original approval stays exactly the same ๐ซถ
here's the tea though: the fields in authorization_details mean different things for each API (or set of APIs), so there's no standard way to compare two random authorization detail requests ๐ an AS should not rely on simple object comparison in most cases, because overlapping some fields could have side effects on what access actually gets granted, depending on how the API was designed and deployed. it's kinda like the scope values some APIs use, honestly ๐
when a new request shows up next to an existing one, the AS can use the same processing it used to grant the original request to figure out whether the resource owner needs to sign off again ๐ the exact details depend on how that type of authorization request is defined (that part is outside this spec), but there are common patterns you can borrow โจ
let's run it back with our running example ๐ฆ the example authorization request from Section 3, once the user approved it, got an authorization code tied to these privileges:
- list accounts,
- access the balance of one or more accounts,
- access the transactions of one or more accounts, and
- initiate, check the status of, and cancel a payment.
now the client wants an access token with just the power to list accounts ๐ง it asks like this:
[
{
"type": "account_information",
"actions": [
"list_accounts"
],
"locations": [
"https://example.com/accounts"
]
}
]
Figure 10: Example of "authorization_details" Reduced Privileges
the example API is built so that every field in the account_information type holds additive rights, with each value in the actions and locations arrays unlocking a different piece of access ๐ so to check this one, the AS would:
- verify that the authorization code issued in the previous step contains an authorization details object of type account_information,
- verify whether the approved list of actions contains list_accounts, and
- verify whether the locations value includes only previously approved locations.
if all the checks pass, the AS issues the requested access token with the smaller set of access ๐
heads up: this comparison only applies to this specific API type definition ๐จ a different type could have totally different rules. for instance, one actions value could subsume the rights of another. like, if a client first asks for a token with write access, that implies both read and write access to this API:
[
{
"type": "example_api",
"actions": [
"write"
]
}
]
Figure 11: Example of "authorization_details" Requesting "write" Access to an API
later, the same client refreshes and asks for read access:
[
{
"type": "example_api",
"actions": [
"read"
]
}
]
Figure 12: Example of "authorization_details" Requesting "read" Access to an API
the AS compares the type value and the actions value and sees that read is already covered by the write access granted earlier ๐
this same API could also have a privileges value called admin, which means the token can do any of the functions on the resources ๐ if the client gets admin privileges, the authorization_details look like this:
[
{
"type": "example_api",
"privileges": [
"admin"
]
}
]
Figure 13: Example of "authorization_details" with "admin" Access to an API
the AS compares the type value and finds that the privileges value subsumes any read or write access granted before ๐ just know that other API definitions can use privileges where the values don't subsume each other.
next up: the client can use the common data element locations (see Section 2.2) to ask for an access token limited to one RS ๐ฏ in our running example, the client may want every permission from the approved payment_initiation grant that applies to the RS at https://example.com/payments, so it asks like this:
[
{
"type": "payment_initiation",
"locations": [
"https://example.com/payments"
]
}
]
Figure 14: Example of "authorization_details" Requesting an Audience-Restricted Access Token
7. Token Response ๐
on top of the token response parameters defined in [RFC6749], the AS MUST also return the authorization_details as granted by the resource owner and assigned to the matching access token ๐
the authorization details attached to the access token in a token response come from the authorization_details parameter in the matching token request. if the client doesn't include the authorization_details token request parameter, the AS decides the resulting authorization_details however it wants ๐คทโโ๏ธ
the AS MAY omit values in the authorization_details when responding to the client ๐ซฃ
for our running example, the response looks like this:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "example",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
"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"
}
]
}
Figure 15: Example Token Response
7.1. Enriched Authorization Details in Token Response โจ
the authorization details on the access token MAY be different from what the client asked for ๐ besides the user approving less than what was requested, there are some use cases where the AS enriches the data in an authorization details object. whether enrichment is allowed, and exactly how it works, is part of the definition of that authorization details type ๐ง
for example, a client might ask for account information but leave it up to the user to choose which accounts it can access ๐ฆ during authorization, the user picks the accounts they want to share with the client. one way to pass those selected accounts along is for the AS to add them to the matching authorization details object ๐
in this example, the requested authorization_details parameter might look like this. the empty arrays are placeholders where the AS will add data during enrichment. this is only an illustration, not a suggestion for how any authorization details type should be designed ๐
"authorization_details": [
{
"type": "account_information",
"access": {
"accounts": [],
"balances": [],
"transactions": []
},
"recurringIndicator":true
}
]
Figure 16: Example of Requested "authorization_details"
then the AS expands the authorization details object and fills in the account identifiers ๐
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JokF0XG5Qx2TlKWIA",
"authorization_details":[
{
"type":"account_information",
"access":{
"accounts":[
{
"iban":"DE2310010010123456789"
},
{
"maskedPan":"123456xxxxxx1234"
}
],
"balances":[
{
"iban":"DE2310010010123456789"
}
],
"transactions":[
{
"iban":"DE2310010010123456789"
},
{
"maskedPan":"123456xxxxxx1234"
}
]
},
"recurringIndicator":true
}
]
}
Figure 17: Example of Enriched "authorization_details"
another one: the client wants access to a medical record but doesn't know the record number when it asks ๐ฉบ so it says what kind of access it wants, without a location or identifier:
{
"authorization_details": [
{
"type": "medical_record",
"sens": [ "HIV", "ETH", "MART" ],
"actions": [ "read" ],
"datatypes": [ "Patient", "Observation", "Appointment" ]
}
]}
Figure 18: Example of Requested "authorization_details"
when the user interacts with the AS, they pick which of the medical records they're responsible for and are OK sharing with the client ๐ that choice comes back with the access token:
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JokF0XG5Qx2TlKWIA",
"authorization_details":[
{
"type": "medical_record",
"sens": [ "HIV", "ETH", "MART" ],
"actions": [ "read" ],
"datatypes": [ "Patient", "Observation", "Appointment" ],
"identifier": "patient-541235",
"locations": [ "https://records.example.com/" ]
}
]
}
Figure 19: Example of Enriched "authorization_details"
Note: The client needs to be aware upfront of the possibility that a certain authorization details object can be enriched. It is assumed that this property is part of the definition of the respective authorization details type. โ ๏ธ
8. Token Error Response ๐
the Token Error Response MUST follow the rules given in Section 5 ๐ซถ
9. Resource Servers โจ
okay so the RS (resource server) needs to actually enforce the authorization details that got approved, right? ๐ก๏ธ That means the AS MUST make this data available to the RS, no excuses ๐
. The AS MAY also add the authorization_details field to access tokens in JSON Web Token (JWT) format or to token introspection responses.
9.1. JWT-Based Access Tokens ๐ง
If the access token is a JWT [RFC7519], the AS is RECOMMENDED to add the authorization details object, filtered to the specific audience, as a top-level claim. ๐
The AS will typically add more claims to the JWT too, like user ID, roles, and transaction-specific data, because the RS needs those to process the request ๐ซถ. Which claims a particular RS needs is defined by the RS-specific policy agreed with the AS. ๐
Here's an example JWT for the payment initiation example from earlier ๐ฆ:
{
"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"
}
}
Figure 20: Example of "authorization_details" in JWT-Based Access Token
In this case, the AS added the following example claims to the JWT-based access token ๐:
sub: tells you which user the client is asking to start a payment for ๐โโ๏ธ.txn: the transaction id, used to trace the transaction across the services of provider example.com ๐.debtorAccount: an API-specific field with the debtor account. This account wasn't passed in theauthorization_details. The user picked it during the authorization process instead ๐. Theuser_rolefield shows what role the user has with respect to this account, and here they're the owner ๐. The payment API (the RS) uses this data for access control ๐.
9.2. Token Introspection ๐
Token introspection [RFC7662] lets an RS ask the AS what's up with an access token ๐. If the AS includes authorization detail information for the token in its response, that information MUST be conveyed with authorization_details as a top-level member of the introspection response JSON object ๐ซฃ. The authorization_details member MUST contain the same structure defined in Section 2, potentially filtered and extended for the RS making the introspection request โจ.
Here's an introspection response for the payment initiation example ๐:
{
"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"
}
}
Figure 21: Example of "authorization_details" in Introspection Response
10. Metadata ๐
To advertise that it supports this feature, the AS puts its list of supported authorization details types in the AS metadata response [RFC8414] ๐ธ. It uses the metadata parameter authorization_details_types_supported, which is a JSON array.
Here's what that looks like:
{
...
"authorization_details_types_supported":[
"payment_initiation",
"account_information"
]
}
Figure 22: Example of Server Metadata about the Supported Authorization Details
Clients MAY indicate which authorization details types they'll use when requesting authorization, using the client registration metadata parameter authorization_details_types, which is also a JSON array ๐ฆ.
Example time:
{
...
"authorization_details_types":[
"payment_initiation"
]
}
Figure 23: Example of Server Metadata about Authorization Details
The registration of authorization details types with the AS is outside the scope of this specification ๐.
11. Implementation Considerations ๐ ๏ธ
11.1. Using Authorization Details in a Certain Deployment ๐งฉ
Using authorization details in a deployment takes a few steps:
- Define authorization details types.
- Publish authorization details types in the OAuth server metadata.
- Determine how authorization details are shown to the user in the user consent prompt.
- If needed, enrich authorization details in the user consent process (e.g., add selected accounts or set expirations).
- If needed, determine how authorization details are reflected in access token content or introspection responses.
- Determine how the RSs process the authorization details or token data derived from authorization details.
- If needed, entitle clients to use certain authorization details types.
11.2. Minimal Implementation Support ๐
General AS implementations that support this specification should provide these basic functions:
- Support advertisement of supported authorization details types in OAuth server metadata
- Accept the
authorization_detailsparameter in authorization requests in conformance with this specification - Support storage of consented authorization details as part of a grant
- Implement default behavior for adding authorization details to access tokens and token introspection responses in order to make them available to RSs (similar to scope values). This should work with any grant type, especially
authorization_codeandrefresh_token.
Processing and presentation of authorization details varies a lot between different authorization details types ๐. So implementations should support customization of that behavior. In particular, implementations should allow deployments to:
- determine presentation of the authorization details;
- modify requested authorization details in the user consent process, e.g., adding fields; and
- merge requested and preexisting authorization details.
One approach would be a mechanism that allows registering extension modules. Each module would be responsible for rendering the matching user consent and for any transformation needed to get the data to the RS through structured access tokens or token introspection responses ๐ง.
11.3. Use of Machine-Readable Type Schemas ๐ค
Implementations might allow deployments to use machine-readable schema languages for defining authorization details types, which makes it easier to create and validate authorization details objects against those schemas โจ. For example, if an authorization details type were defined using JSON Schemas [JSON.Schema], the JSON Schema identifier could be used as the type value in the respective authorization details objects.
Note, however, that type values are identifiers understood by the AS and, to the extent necessary, the client and RS ๐. This specification makes no assumption that a type value points to a machine-readable schema format, or that any party in the system (such as the client, AS, or RS) will dereference or process the contents of the type field in any specific way.
11.4. Large Requests ๐ฆ
Authorization request URIs that carry authorization_details in a request parameter or a request object can get really long ๐ญ. So implementers should consider using the request_uri parameter as defined in [RFC9101], together with the pushed request object mechanism as defined in [RFC9126], to pass authorization_details in a reliable and secure way ๐. Here's an example of a pushed authorization request that sends the authorization request data straight to the AS over an HTTPS-protected connection:
POST /as/par HTTP/1.1
Host: as.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3
response_type=code&
client_id=s6BhdRkqt3
&state=af0ifjsldkj
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
&code_challenge_method=S256
&code_challenge=K2-ltc83acc4h0c9w6ESC_rEMTJ3bwc-uCHaoeK1t8U
&authorization_details=%5B%7B%22type%22%3A%22account_information%22
%2C%22actions%22%3A%5B%22list_accounts%22%2C%22read_balances%22%2C%
22read_transactions%22%5D%2C%22locations%22%3A%5B%22https%3A%2F%2Fe
xample.com%2Faccounts%22%5D%7D%2C%7B%22type%22%3A%22payment_initiat
ion%22%2C%22actions%22%3A%5B%22initiate%22%2C%22status%22%2C%22canc
el%22%5D%2C%22locations%22%3A%5B%22https%3A%2F%2Fexample.com%2Fpaym
ents%22%5D%2C%22instructedAmount%22%3A%7B%22currency%22%3A%22EUR%22
%2C%22amount%22%3A%22123.50%22%7D%2C%22creditorName%22%3A%22Merchan
t123%22%2C%22creditorAccount%22%3A%7B%22iban%22%3A%22DE021001001093
07118603%22%7D%2C%22remittanceInformationUnstructured%22%3A%22Ref%2
0Number%20Merchant%22%7D%5D
Figure 24: Example of Large Request including "authorization_details"
12. Security Considerations ๐
okay bestie, this is the serious part, so listen up ๐ซถ The authorization_details parameter travels through the user agent during an OAuth authorization request, which means the user can literally poke at it ๐ That makes it vulnerable to modifications by the user. So if the integrity of authorization_details matters to you (it should!), clients MUST protect authorization_details against tampering and swapping. ๐ You can do that by signing the request using signed request objects as defined in [RFC9101], or by using the request_uri authorization request parameter as defined in [RFC9101] together with [RFC9126], which passes the URI of the request object to the AS.
String comparisons inside an authorization_details parameter are done as defined by [RFC8259]. No extra transformation or normalization happens when you check whether string values are equivalent. No cap, no sneaky fixing things along the way ๐
The common data field locations let a client say exactly where it plans to use a certain authorization. That means permissions can be unambiguously assigned to RSs ๐ฏ When there are multiple RSs in the mix, this stops unintended client authorizations through audience restriction. For example, a read scope value that could apply to an email service and also to a cloud service is not going to slip between the two. Iconic prevention, honestly โจ
The AS MUST properly sanitize and handle the data passed in the authorization_details in order to prevent injection attacks. ๐ก๏ธ This one is non-negotiable, periodt.
The Security Considerations of [RFC6749], [RFC7662], and [RFC8414] also apply, so go read those too ๐
13. Privacy Considerations ๐ฆ
It is especially important for implementers to design and use authorization details in a privacy-preserving manner. Protecting people's data is literally the whole vibe ๐ซถ
Any sensitive personal data included in authorization_details must be prevented from leaking, for example through referrer headers. ๐ Ways to do that include encrypted request objects as defined in [RFC9101], or sending authorization_details over end-to-end encrypted connections between the client and the AS by using [RFC9126] along with the request_uri authorization request parameter as defined in [RFC9101]. The second option doesn't need application-level encryption, but it does need another message exchange between the client and the AS.
Here's the tea, though. Even if the request data is encrypted, an attacker could use the AS to learn the user's data ๐ฑ They could inject the encrypted request data into an authorization request on a device under their control, then use the AS's user consent screens to show the decrypted user data in the clear. Implementations need to consider this attack vector and put appropriate countermeasures in place. For example, they can show only portions of the data, or, if possible, check whether the assumed user context is still the same after user authentication. Very "who is actually in the room" energy ๐
The AS needs to take the privacy implications into account when it shares authorization_details with the client or RSs. The AS should share this data with those parties on a "need to know" basis, as determined by local policy. Only the people who need it, bestie, that's the rule ๐ธ
14. IANA Considerations ๐
14.1. OAuth Parameters Registration
The following parameter has been registered in the "OAuth Parameters" registry [IANA.OAuth.Parameters] established by [RFC6749]. Look at it, registered and everything โจ
Name: authorization_details
Parameter Usage Location: authorization request, token request,
token response
Change Controller: IETF
Reference: RFC 9396
14.2. JSON Web Token Claims Registration
The following value has been registered in the IANA "JSON Web Token Claims" registry established by [RFC7519]. ๐
Claim Name: authorization_details
Claim Description: The claim authorization_details contains a JSON
array of JSON objects representing the rights of the access token.
Each JSON object contains the data to specify the authorization
requirements for a certain type of resource.
Change Controller: IETF
Reference: Section 9.1 of RFC 9396
14.3. OAuth Token Introspection Response Registration
The following value has been registered in the IANA "OAuth Token Introspection Response" registry established by [RFC7662]. ๐ง
Name: authorization_details
Description: The member authorization_details contains a JSON array
of JSON objects representing the rights of the access token. Each
JSON object contains the data to specify the authorization
requirements for a certain type of resource.
Change Controller: IETF
Reference: Section 9.2 of RFC 9396
14.4. OAuth Authorization Server Metadata Registration
The following values have been registered in the IANA "OAuth Authorization Server Metadata" registry of [IANA.OAuth.Parameters] established by [RFC8414]. Metadata, but make it registered ๐
Metadata Name: authorization_details_types_supported
Metadata Description: JSON array containing the authorization
details types the AS supports
Change Controller: IETF
Reference: Section 10 of RFC 9396
14.5. OAuth Dynamic Client Registration Metadata Registration
The following value has been registered in the IANA "OAuth Dynamic Client Registration Metadata" registry of [IANA.OAuth.Parameters] established by [RFC7591]. ๐
Client Metadata Name: authorization_details_types
Client Metadata Description: Indicates what authorization details
types the client uses.
Change Controller: IETF
Reference: Section 10 of RFC 9396
14.6. OAuth Extensions Error Registration
The following value has been registered in the "OAuth Extensions Error Registry" of [IANA.OAuth.Parameters] established by [RFC6749]. An error code with its own registry entry, that's main character energy ๐
Name: invalid_authorization_details
Usage Location: token endpoint, authorization endpoint
Protocol Extension: OAuth 2.0 Rich Authorization Requests
Change Controller: IETF
Reference: Section 5 of RFC 9396
15. References ๐
15.1. Normative References
These are the must-reads, the ones you genuinely need before anything else makes sense ๐
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC7519] Jones, M., Bradley, J., and N. Sakimura, "JSON Web Token
(JWT)", RFC 7519, DOI 10.17487/RFC7519, May 2015,
<https://www.rfc-editor.org/info/rfc7519>.
[RFC7662] Richer, J., Ed., "OAuth 2.0 Token Introspection",
RFC 7662, DOI 10.17487/RFC7662, October 2015,
<https://www.rfc-editor.org/info/rfc7662>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
[RFC8414] Jones, M., Sakimura, N., and J. Bradley, "OAuth 2.0
Authorization Server Metadata", RFC 8414,
DOI 10.17487/RFC8414, June 2018,
<https://www.rfc-editor.org/info/rfc8414>.
[RFC8628] Denniss, W., Bradley, J., Jones, M., and H. Tschofenig,
"OAuth 2.0 Device Authorization Grant", RFC 8628,
DOI 10.17487/RFC8628, August 2019,
<https://www.rfc-editor.org/info/rfc8628>.
[RFC8707] Campbell, B., Bradley, J., and H. Tschofenig, "Resource
Indicators for OAuth 2.0", RFC 8707, DOI 10.17487/RFC8707,
February 2020, <https://www.rfc-editor.org/info/rfc8707>.
15.2. Informative References
These are the extra-credit reads, the ones that add context and are lowkey worth your time ๐ฆ
[CSC] Cloud Signature Consortium, "Architectures and protocols
for remote signature applications", Version 1.0.4.0, June
2019, <https://cloudsignatureconsortium.org/wp-
content/uploads/2020/01/CSC_API_V1_1.0.4.0.pdf>.
[ETSI] ETSI, "Electronic Signatures and Infrastructures (ESI);
Protocols for remote digital signature creation", V1.1.1,
ETSI TS 119 432, March 2019,
<https://www.etsi.org/deliver/
etsi_ts/119400_119499/119432/01.01.01_60/
ts_119432v010101p.pdf>.
[IANA.OAuth.Parameters]
IANA, "OAuth Parameters",
<https://www.iana.org/assignments/oauth-parameters>.
[JSON.Schema]
OpenJS Foundation, "JSON Schema",
<https://json-schema.org/>.
[OID-CIBA] Fernandez, G., Walter, F., Nennker, A., Tonge, D., and B.
Campbell, "OpenID Connect Client-Initiated Backchannel
Authentication Flow - Core 1.0", 1 September 2021,
<https://openid.net/specs/openid-client-initiated-
backchannel-authentication-core-1_0.html>.
[OIDC] Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., and
C. Mortimore, "OpenID Connect Core 1.0 incorporating
errata set 1", 8 November 2014,
<https://openid.net/specs/openid-connect-core-1_0.html>.
[RFC0020] Cerf, V., "ASCII format for network interchange", STD 80,
RFC 20, DOI 10.17487/RFC0020, October 1969,
<https://www.rfc-editor.org/info/rfc20>.
[RFC6749] Hardt, D., Ed., "The OAuth 2.0 Authorization Framework",
RFC 6749, DOI 10.17487/RFC6749, October 2012,
<https://www.rfc-editor.org/info/rfc6749>.
[RFC7591] Richer, J., Ed., Jones, M., Bradley, J., Machulak, M., and
P. Hunt, "OAuth 2.0 Dynamic Client Registration Protocol",
RFC 7591, DOI 10.17487/RFC7591, July 2015,
<https://www.rfc-editor.org/info/rfc7591>.
[RFC8259] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data
Interchange Format", STD 90, RFC 8259,
DOI 10.17487/RFC8259, December 2017,
<https://www.rfc-editor.org/info/rfc8259>.
[RFC9101] Sakimura, N., Bradley, J., and M. Jones, "The OAuth 2.0
Authorization Framework: JWT-Secured Authorization Request
(JAR)", RFC 9101, DOI 10.17487/RFC9101, August 2021,
<https://www.rfc-editor.org/info/rfc9101>.
[RFC9126] Lodderstedt, T., Campbell, B., Sakimura, N., Tonge, D.,
and F. Skokan, "OAuth 2.0 Pushed Authorization Requests",
RFC 9126, DOI 10.17487/RFC9126, September 2021,
<https://www.rfc-editor.org/info/rfc9126>.
[Transaction-Auth]
Lodderstedt, T., "Transaction Authorization or why we need
to re-think OAuth scopes", 20 April 2019,
<https://medium.com/oauth-2/transaction-authorization-or-
why-we-need-to-re-think-oauth-scopes-2326e2038948>.
Appendix A. Additional Examples โจ
A.1. OpenID Connect โจ
okay so [OIDC] (OpenID Connect) has this JSON-based claims request parameter ๐ that lets a client (acting as an OpenID Connect Relying Party) ask for exactly the claims it wants, in a fine-grained, privacy-preserving way. It can also assign those claims to certain delivery mechanisms, i.e., the ID Token or the userinfo response. so precise, so iconic ๐
the combo of the scope value openid and the extra claims parameter can sit right beside authorization_details in the same way as every non-OIDC scope value ๐ฆ
alternatively, there could be an authorization details type for OpenID Connect. this section gives an example of what that could look like, but defining that authorization details type is outside the scope of this specification ๐
these hypothetical examples try to pack every detail specific to the OpenID Connect part of an authorization process into one authorization JSON object ๐ง
the top-level fields are based on the definitions in [OIDC]:
claim_sets: the names of predefined claim sets, a replacement for the respective scope values, such asprofile๐ธmax_age: Maximum Authentication Age โณacr_values: requested Authentication Context Class Reference (ACR) values ๐ฅclaims: the claims JSON structure as defined in [OIDC] ๐
this is a simple request for some claim sets:
[
{
"type": "openid",
"locations": [
"https://op.example.com/userinfo"
],
"claim_sets": [
"email",
"profile"
]
}
]
Figure 25: Example of OpenID Connect Request Utilizing "authorization_details"
Note: locations specifies the location of the userinfo endpoint since this is the only place where an access token is used by a client (Relying Party) in OpenID Connect to obtain claims. ๐ซถ
a more sophisticated example is next, and it's giving main character energy ๐
[
{
"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
}
}
}
}
]
Figure 26: Advanced Example of OpenID Connect Request Utilizing "authorization_details"
A.2. Remote Electronic Signing ๐ฆ
this example is based on the concept laid out for remote electronic signing in ETSI TS 119 432 [ETSI] and the Cloud Signature Consortium (CSC) API for remote signature creation [CSC]. the signing era is here ๐๏ธโจ
[
{
"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"
}
]
Figure 27: Example of Electronic Signing
the top-level fields mean the following:
credentialID: identifier of the certificate to be used for signing ๐documentDigests: array containing the hash of every document to be signed (hash fields). the correspondinglabelfield identifies each document to the user, e.g., to be used in user consent ๐hashAlgorithm: algorithm that was used to calculate the hash values ๐งฎ
the AS is supposed to ask the user for consent for the creation of signatures for the documents listed in the structure. then the client uses the access token issued as a result of that process to call the document signature API at the respective signing service and actually create the signature. this access token is bound to the client, the user ID and the hashes (and signature algorithm) as consented by the user. bound, locked in, no cap ๐๐
A.3. Access to Tax Data ๐งพ
this example is inspired by an API that lets third parties access a citizen's tax declarations and income statements, for example, to determine their creditworthiness. the financial main character era ๐ธ
[
{
"type": "tax_data",
"locations": [
"https://taxservice.govehub.no.example.com"
],
"actions":"read_tax_declaration",
"periods": ["2018"],
"duration_of_access": 30,
"tax_payer_id": "23674185438934"
}
]
Figure 28: Example of Tax Data Access
the top-level fields mean the following:
periods: the periods the client wants to access ๐duration_of_access: how long the clients intend to access the data, in days โฑ๏ธtax_payer_id: identifier of the taxpayer (if known to the client) ๐ชช
A.4. eHealth ๐ฉบ
these two examples are inspired by requirements for APIs used in the Norwegian eHealth system. they're giving serious responsibility energy and I respect it ๐ซถ
in this use case, the physical therapist sits in front of their computer using a local Electronic Health Records (EHR) system. they want to look at the electronic patient records of a certain patient, and they also want to fetch the patient's journal entries in another system, perhaps at another institution or a national service. access to this data is provided by an API ๐ฅ
the information necessary to authorize the request at the API is only known by the EHR system and must be presented to the API. so the EHR system is the keeper of the keys ๐
in the first example, the authorization details object contains the identifier of an organization. in this case, the API needs to know if the given organization has the lawful basis for processing personal health information to give access to sensitive data. very important stuff, bestie ๐ง
"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]"
}
}
}
}
}
Figure 29: eHealth Example
in the second example, the API requires more information to authorize the request. this time the authorization details object contains extra info about the health institution and the profession the user has at the time of the request. that extra detail could be used for both authorization and data minimization. it's so detailed it's giving detective ๐ต๏ธโโ๏ธ๐
[
{
"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"
}
]
}
}
}
}
]
Figure 30: Advanced eHealth Example
the fields are described as follows:
patient_identifier: the identifier of the patient, made up of a system identifier in OID format (namespace) and the actual value within that namespace ๐reason_for_request: the reason why the user wants to access a certain API ๐ฌrequesting_entity: specification of the requester by means of identity, role and organizational context. this data is provided to facilitate authorization and for auditing purposes ๐
in this use case, the AS authenticates the requester, who is not the patient, and approves access based on policies. the requester and the patient are two different people, and the policies do the checking ๐ก๏ธ
Acknowledgements ๐
We would like to thank Daniel Fett, Sebastian Ebling, Dave Tonge, Mike Jones, Nat Sakimura, and Rob Otto for their valuable feedback during the preparation of this specification. the group chat thanks you ๐ซถ๐
We would also like to thank Vladimir Dzhuvinov, Takahiko Kawasaki, Daniel Fett, Dave Tonge, Travis Spencer, Joergen Binningsboe, Aamund Bremer, Steinar Noem, Francis Pouatcha, Jacob Ideskog, Hannes Tschofenig, and Aaron Parecki for their valuable feedback to this specification. literally a whole squad ๐งโจ
Authors' Addresses ๐ฆ
- Torsten Lodderstedt yes.com Email: torsten@lodderstedt.net
- Justin Richer Bespoke Engineering Email: ietf@justin.richer.org
- Brian Campbell Ping Identity Email: bcampbell@pingidentity.com
that's the whole spec, bestie! ๐๐ โจ
๐ฆ where this page comes from
- ietf: rfc9396 2023-05
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms