RFC 9470
OAuth 2.0 Step Up Authentication Challenge Protocol
OAuth 2.0 Step Up Authentication Challenge Protocol ๐
Okay so bestie, this is RFC 9470 ๐ฆ It's a Standards Track document from the IETF, dated September 2023, and it's written by V. Bertocci (Auth0/Okta) and B. Campbell (Ping Identity). It's published in the ISSN 2070-1721 series. It doesn't obsolete or update any other RFCs, so it's a fresh standalone spec. Very main character energy ๐
The whole vibe is about step up authentication: resource servers needing a stronger login, or a more recent one, depending on what the request is doing. Lowkey the most relatable problem ever ๐ญ
Abstract
Okay so resource servers sometimes need different authentication strengths or recentness depending on what a request is about. This doc gives resource servers a way to tell the client that the authentication behind the current access token isn't strong enough (or recent enough) for the request, and it explains how the client can fix that. It also codifies a way for a client to ask an authorization server for a specific authentication strength or recentness when it processes an authorization request. It's giving very organized energy โจ
Status of This Memo ๐ธ
This is an Internet Standards Track document. Honestly the most official slay ๐
This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). If you want more info on Internet Standards, check out Section 2 of RFC 7841.
Info about the current status of this document, any errata, and how to give feedback is at https://www.rfc-editor.org/info/rfc9470.
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) in effect on the date of publication of this document. Please read these carefully, because they spell out your rights and restrictions for 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.
Table of Contents ๐ง
1. Introduction 1.1. Conventions and Terminology
- Protocol Overview
- Authentication Requirements Challenge
- Authorization Request
- Authorization Response
- Authentication Information Conveyed via Access Token 6.1. JWT Access Tokens 6.2. OAuth 2.0 Token Introspection
- Authorization Server Metadata
- Deployment Considerations
- Security Considerations
- IANA Considerations 10.1. OAuth Extensions Error Registration 10.2. OAuth Token Introspection Response Registration
- References 11.1. Normative References 11.2. Informative References Acknowledgements Authors' Addresses
1. Introduction ๐ฆ
Okay so in simple API authorization setups, the authorization server picks an authentication technique for each request based on stuff like the scopes asked for, the resource, the client's identity, and other characteristics it knows at provisioning time. That works fine in a lot of situations, but it falls short in some important ones. Like, imagine an eCommerce API that needs different authentication strengths depending on whether the item being bought goes over a certain threshold. That threshold is estimated dynamically by the API itself using logic the authorization server can't see ๐ An API might also decide it needs a more recent user authentication based on its own risk evaluation of the request.
This document extends the set of error codes defined by [RFC6750] with a new value, insufficient_user_authentication. Resource servers can use it to tell the client that the authentication event tied to the access token in the request doesn't meet the resource server's authentication requirements. It also adds acr_values and max_age parameters to the Bearer authentication scheme challenge defined by [RFC6750]. The resource server can use these to explicitly tell the client what authentication strength or recentness it needs ๐
The client can then use that info to go back to the authorization server with an authorization request that specifies the authentication requirements the protected resource indicated. This happens by including the acr_values or max_age authorization request parameters, as defined in [OIDC].
These extensions make it possible to implement interoperable step up authentication with minimal work from resource servers, clients, and authorization servers. Iconic, honestly โจ
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", "authorization server", "authorization endpoint", "authorization request", "client", "protected resource", and "resource server" as defined by "The OAuth 2.0 Authorization Framework" [RFC6749]. Those terms are the OAuth basics, so no cap, you'll want that one bookmarked ๐
Part 1 of 4 is done, bestie! ๐ Part 2 is coming next ๐
2. Protocol Overview โจ
okay so bestie, this section is basically the whole story in one go ๐ it walks through an end-to-end step up authentication scenario that follows this spec. the big assumption is that the client already has an access token for the protected resource before any of this kicks off ๐
Here's the abstract flow (Figure 1). It's a little busy, but the main characters are the Client, the Resource Server, the User Agent, and the Authorization Server ๐
+----------+ +--------------+
| | | |
| |-----------(1) request ------------------>| |
| | | |
| |<---------(2) challenge ------------------| Resource |
| | | Server |
| Client | | |
| |-----------(5) request ------------------>| |
| | | |
| |<-----(6) protected resource -------------| |
| | +--------------+
| |
| |
| | +-------+ +---------------+
| |->| | | |
| | | |--(3) authorization request-->| |
| | | User | | |
| | | Agent |<-----------[...]------------>| Authorization |
| | | | | Server |
| |<-| | | |
| | +-------+ | |
| | | |
| |<-------- (4) access token --------------| |
| | | |
+----------+ +---------------+
Figure 1: Abstract Protocol Flow
And here's the play-by-play ๐ฌ
- The client requests a protected resource, presenting an access token.
- The resource server decides that the circumstances in which the presented access token was obtained offer insufficient authentication strength and/or recentness ๐ฌ so it denies the request and returns a challenge. The challenge describes (using a combination of
acr_valuesandmax_age) what authentication requirements must be met for the resource server to authorize a request. - The client sends the user agent to the authorization server with an authorization request that includes the
acr_valuesand/ormax_agethe resource server asked for in the previous step. - Whatever sequence the grant of choice needs plays out. This includes the necessary steps to authenticate the user in line with the
acr_valuesand/ormax_agevalues of the authorization request. Then the authorization server hands a new access token back to the client, and that token contains or references info about the authentication event ๐ง - The client retries the request from step 1, this time presenting the newly obtained access token.
- The resource server checks that the user authentication done while getting the new token meets its requirements, and returns the representation of the requested protected resource ๐
The validation operations in steps 2 and 6 imply that the resource server has a way of evaluating the authentication that happened when the access token was obtained. In this document, the resource server's assessment of the specific authentication method used to get a token for the requested resource is called an "authentication level" ๐ This document describes how the resource server can do that assessment when the access token is a JSON Web Token (JWT) [RFC9068] or is validated via introspection [RFC7662]. Other ways of figuring out the authentication level are possible, as long as the authorization server and the protected resource agree on them, but they are beyond the scope of this specification. Given an authentication level for a token, the resource server decides whether it meets the security criteria for the requested resource.
"authentication level" and "step up" are metaphors in this specification, and they're doing a lot of vibe work ๐ฆ They do NOT mean there's some absolute, interoperable ranking of authentication methods. The level idea just comes from the fact that a resource server may only want to accept certain authentication methods. When a token comes from an authentication method the resource server doesn't want to accept (below the threshold or level it will accept), the resource server seeks to step up (i.e., renegotiate) from the current authentication level to one it may accept. "Step up" is meant to convey a shift from the original level to one that's acceptable to the resource server ๐
Now, the new token usually supersedes the old ones because it has a higher authentication level, which is the typical "step up authentication" vibe. But it's important to remember this isn't always true ๐ For example, a resource server might require a higher level and a shorter validity for one particular request. That gives you a token that's great for one-off calls but leads to frequent prompts, which is a suboptimal user experience if you reuse it for routine stuff. In that case the client is better off keeping both the old tokens (tied to a lower authentication level) and the new one, and picking the right token for each API call ๐ This isn't a brand-new requirement for clients, since incremental consent and least-privilege principles already need similar heuristics for managing access tokens with different scopes and permission levels. This document does NOT recommend any specific token-caching strategy. That choice depends on the characteristics of each scenario and stays application-dependent, just like in the core OAuth cases.
Also, recall that OAuth 2.0 [RFC6749] assumes access tokens are treated as opaque by clients โ ๏ธ The token format might be unreadable to the client, or it might change at any time to become unreadable. So, during any token-caching strategy, a client MUST NOT attempt to inspect the content of the access token to figure out the associated authentication information or other details (see Section 6 of [RFC9068] for a more detailed discussion).
Clients: do not poke inside the token. It's opaque for a reason ๐ โโ๏ธ
3. Authentication Requirements Challenge ๐
This specification introduces a new error code value for the challenge of the Bearer authentication scheme's error parameter (from [RFC6750]), and for other OAuth authentication schemes, such as those seen in [RFC9449], which use the same error parameter ๐
insufficient_user_authentication: The authentication event associated with the access token presented with the request does not meet the authentication requirements of the protected resource.
Note: the logic through which the resource server figures out that the current request doesn't meet the authentication requirements, plus all the related stuff (like expressing, deploying, and publishing those requirements), is out of scope for this document ๐
This specification also defines the following WWW-Authenticate auth-param values for those OAuth authentication schemes, so the authentication requirements can be sent back to the client ๐ฌ
acr_values: A space-separated string listing the authentication context class reference values in order of preference. The protected resource REQUIRES one of these values for the authentication event associated with the access token. As defined in Section 1.2 of [OIDC], the authentication context conveys info about how authentication takes place (e.g., what authentication method(s) or assurance level to meet). ๐ง
max_age: This value indicates the allowable elapsed time in seconds since the last active authentication event associated with the access token. An active authentication event means a user interacting with the authorization server in response to an authentication prompt. Note that, while the auth-param value can be conveyed as a token or quoted-string (see Section 11.2 of [RFC9110]), it has to represent a non-negative integer โฑ๏ธ
Figure 2 is an example of a Bearer authentication scheme challenge with the WWW-Authenticate header using:
- the
insufficient_user_authenticationerror code value to tell the client that the access token presented isn't enough to get into the protected resource, and - the
acr_valuesparameter to let the client know that the expected authentication level corresponds to the authentication context class reference identified bymyACR.
Note that while this specification only defines usage of the above auth-params with the insufficient_user_authentication error code, it does not preclude future specifications or profiles from defining their usage with other error codes ๐ฎ
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="insufficient_user_authentication",
error_description="A different authentication level is required",
acr_values="myACR"
Figure 2: Authentication Requirements Challenge Indicating acr_values
Figure 3 shows a challenge telling the client that the last active authentication event tied to the presented access token is too old, and a more recent authentication is needed ๐ฐ๏ธ
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="insufficient_user_authentication",
error_description="More recent authentication is required",
max_age="5"
Figure 3: Authentication Requirements Challenge Indicating max_age
The auth-params max_age and acr_values MAY both occur in the same challenge if the resource server needs to express requirements about both recency and authentication level โจ If the resource server determines that the request is also lacking the scopes required by the requested resource, it MAY include the scope attribute with the value necessary to access the protected resource, as described in Section 3.1 of [RFC6750] ๐
4. Authorization Request โจ
okay so bestie, when the resource server hits you with a challenge carrying the insufficient_user_authentication error code, you SHOULD parse the WWW-Authenticate header for acr_values and max_age and use them, if they're there, to build your authorization request ๐. That request then goes to the authorization server's authorization endpoint through the user agent, so you can get a fresh access token that meets the requirements. The acr_values and max_age parameters are both OPTIONAL and come from Section 3.1.2.1. of [OIDC] ๐. This document doesn't change how the authorization server processes those parameters, which is giving "plug and play" ๐. Any authorization server running OpenID Connect can join this flow with little or no changes. See Section 5 for more details ๐ง.
Here's an example authorization request URI that might show up after you get the challenge in Figure 2. It tells the authorization server the client wants authentication done according to the authentication context class reference myACR ๐ฆ.
https://as.example.net/authorize?client_id=s6BhdRkqt3
&response_type=code&scope=purchase&acr_values=myACR
Figure 4: Authorization Request Indicating acr_values
After the challenge in Figure 3, a client might send the user agent to this example authorization request URI. The max_age parameter tells the authorization server that the user-authentication event has to have happened no more than five seconds ago โฑ๏ธ๐
.
https://as.example.net/authorize?client_id=s6BhdRkqt3
&response_type=code&scope=purchase&max_age=5
Figure 5: Authorization Request Indicating max_age
5. Authorization Response ๐
okay the vibe here is about what the authorization server does with those params ๐โโ๏ธ. Section 5.5.1.1 of [OIDC] says an authorization server that gets a request with acr_values MAY try to authenticate the user in a way that satisfies the requested authentication context class reference, and then put that value in the acr claim of the ID Token it returns ๐ธ. The same section also says that if the desired level can't be reached, the authorization server SHOULD put a value in the acr claim that reflects the authentication level of the current session (if there is one) ๐ฅบ. Then Section 3.1.2.1 [OIDC] says that if a request includes max_age, the authorization server MUST include the auth_time claim in the issued ID Token ๐.
An authorization server following this specification will respond to acr_values and max_age by putting acr and auth_time in the access token. See Section 6 for details ๐. OIDC leaves the authorization server free to decide how it handles acr in the ID Token when it's requested through acr_values. For access tokens, though, this specification says the authorization server SHOULD treat the requested acr value as necessary to fulfill the request ๐ฅ. So the requested acr value gets included in the access token only if the authentication met its requirements. Otherwise, the authorization request fails and returns an unmet_authentication_requirements error, as defined in [OIDCUAR] ๐
โโ๏ธ. This recommended behavior helps stop clients from getting stuck in a loop, where the authorization server keeps handing back tokens the resource server already said don't meet its requirements ๐๐ญ.
6. Authentication Information Conveyed via Access Token ๐ฆ
so, to check whether an access token meets the protected resource's requirements, the resource server needs a way to read info about the authentication event that got the token in the first place ๐ต๏ธโโ๏ธ. This spec gives guidance on sending that info along with two common token-validation methods:
- the one in [RFC9068], where the access token is a JWT and gets checked against a set of validation rules, and
- the one in [RFC7662], where the token is validated and decoded by sending it to an introspection endpoint.
Authorization servers and resource servers MAY pick other encoding and validation methods too ๐. Those are out of scope for this document, though ๐.
6.1. JWT Access Tokens
When access tokens are JSON Web Tokens (JWTs) [RFC7519], the auth_time and acr claims (per Section 2.2.1 of [RFC9068]) carry the time and context of the user-authentication event that the authentication server ran while getting the access token ๐ง. Keep in mind that those two values are set at user-authentication time and won't change when access tokens get renewed ๐ซถ. See Section 2.2.1 of [RFC9068] for details. Below is a conceptual example showing the decoded content of a JWT access token ๐.
Header:
{"typ":"at+JWT","alg":"ES256","kid":"LTacESbw"}
Claims:
{
"iss": "https://as.example.net",
"sub": "someone@example.net",
"aud": "https://rs.example.com",
"exp": 1646343000,
"iat": 1646340200,
"jti" : "e1j3V_bKic8-LAEB_lccD0G",
"client_id": "s6BhdRkqt3",
"scope": "purchase",
"auth_time": 1646340198,
"acr": "myACR"
}
Figure 6: Decoded JWT Access Token
6.2. OAuth 2.0 Token Introspection
"OAuth 2.0 Token Introspection" [RFC7662] gives a protected resource a way to ask the authorization server whether an access token is active, and to find out metainformation about it ๐. These two top-level introspection response members carry info about the user-authentication event that the authentication server ran while getting the access token ๐ :
acr: a string naming the authentication context class reference that was satisfied by the user-authentication event that happened ๐.auth_time: the time the user authentication happened, as a JSON numeric value counting the seconds from1970-01-01T00:00:00ZUTC until the date and time of that authentication event โฐ.
Here's an example introspection response with info about the user-authentication event that got the access token ๐.
HTTP/1.1 200 OK
Content-Type: application/json
{
"active": true,
"client_id": "s6BhdRkqt3",
"scope": "purchase",
"sub": "someone@example.net",
"aud": "https://rs.example.com",
"iss": "https://as.example.net",
"exp": 1639528912,
"iat": 1618354090,
"auth_time": 1646340198,
"acr": "myACR"
}
Figure 7: Introspection Response
7. Authorization Server Metadata ๐๏ธ
Authorization servers can announce that they support this specification by putting the acr_values_supported value in their metadata document. That document is defined in [RFC8414], and acr_values_supported is defined in Section 3 of [OIDCDISC] ๐. If acr_values_supported shows up in the authorization server metadata document, it signals that the authorization server will understand and honor the acr_values and max_age parameters in incoming authorization requests ๐ซถโจ.
8. Deployment Considerations ๐ก
This specification helps resource servers pass requirements along to clients, which can lead to a smooth step-up authentication experience ๐ฅฐ. But the user experience you can get in any deployment depends on the policies that each resource server and authorization server pair sets up ๐ค. Putting limits on those policies is out of scope for this spec. That means resource servers and authorization servers can totally end up with requirements users can't possibly meet, or that lead to a rough user experience ๐ฌ. The authentication prompts the authorization server shows because of how this method passes requirements along might make users do things like use multiple devices, or have access to devices that meet certain security requirements, and so on ๐ฑ๐ป. Those extra requirements are about how to comply with a requirement, not about naming the requirement itself, so they're out of scope for this spec ๐.
9. Security Considerations ๐
Okay, this section is serious business, so pay attention ๐. This specification adds to OAuth mechanisms that were already defined, and their security considerations apply too:
- OAuth 2.0 [RFC6749],
- JWT access tokens [RFC9068],
- Bearer WWW-Authenticate [RFC6750],
- token introspection [RFC7662], and
- authorization server metadata [RFC8414].
This document MUST NOT be used to position OAuth as an authentication protocol. ๐จ
For this spec, how a user authenticated with the authorization server to get an access token is key info, because a resource server might decide whether to grant access based on how that authentication was done ๐. Still, this specification doesn't define how authentication actually happens. It relies on a separate authentication layer to handle the details ๐งฉ. Like other OAuth-family specs, this document assumes a session exists, without getting into how it gets set up or maintained, which protocols implement that layer (for example, OpenID Connect), and so on ๐.
Depending on the resource server's policies, the acr_values parameter introduced in Section 3 might accidentally reveal info about the authenticated user, the resource itself, the authorization server, and any other context-specific data that an attacker could use to learn about their target ๐ฑ. For example, a resource server that asks for an acr value showing a high level of assurance for some users but not others might end up identifying potential high-privilege users to target with spear phishing attacks ๐ฃ. Implementers should be careful about what they disclose in the challenge and under which circumstances ๐ซก. The logic that checks the incoming access token to decide whether to return a challenge can run either before or after the usual token-validation logic, whether that's JWT validation, introspection, or some other method ๐. The resource server MAY return a challenge without verifying that the client presented a valid token. But this approach leaks the required properties of an authorization token to an actor who hasn't proven they can get a token for this resource server ๐ซฃ.
As this specification gives the resource server a way to trigger user interaction, it's important for the authorization server and clients to remember that a malicious resource server might abuse that feature ๐ฉ.
10. IANA Considerations ๐
10.1. OAuth Extensions Error Registration
okay so this spec adds one new error value to the OAuth Extensions Error Registry ๐๏ธ (that's the [IANA.OAuth.Params] list that [RFC6749] set up). here's the entry, bestie ๐
Name: insufficient_user_authentication
Usage Location: resource access error response
Protocol Extension: OAuth 2.0 Step Up Authentication Challenge
Protocol
Change controller: IETF
Specification document(s): Section 3 of RFC 9470
10.2. OAuth Token Introspection Response Registration
and this one adds two new values to the OAuth Token Introspection Response registry ๐ฆ (set up by [RFC7662]). the entries are below โจ
Authentication Context Class Reference:
Name: acr
Description: Authentication Context Class Reference
Change Controller: IETF
Specification Document(s): Section 6.2 of RFC 9470
Authentication Time:
Name: auth_time
Description: Time when the user authentication occurred
Change Controller: IETF
Specification Document(s): Section 6.2 of RFC 9470
11. References
11.1. Normative References
These are the must-reads ๐ and the BCP 14 stuff lives here too:
[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.[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.[RFC6750]Jones, M. and D. Hardt, "The OAuth 2.0 Authorization Framework: Bearer Token Usage", RFC 6750, DOI 10.17487/RFC6750, October 2012,https://www.rfc-editor.org/info/rfc6750.[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.
11.2. Informative References
these are the extra reading for the curious ๐
[IANA.OAuth.Params]IANA, "OAuth Parameters",https://www.iana.org/assignments/oauth-parameters.[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.[OIDCDISC]Sakimura, N., Bradley, J., Jones, M., and E. Jay, "OpenID Connect Discovery 1.0 incorporating errata set 1", 8 November 2014,https://openid.net/specs/openid-connect-discovery-1_0.html.[OIDCUAR]Lodderstedt, T., "OpenID Connect Core Error Code unmet_authentication_requirements", 8 May 2019,https://openid.net/specs/openid-connect-unmet-authentication-requirements-1_0.html.[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.[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.[RFC9068]Bertocci, V., "JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens", RFC 9068, DOI 10.17487/RFC9068, October 2021,https://www.rfc-editor.org/info/rfc9068.[RFC9110]Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Semantics", STD 97, RFC 9110, DOI 10.17487/RFC9110, June 2022,https://www.rfc-editor.org/info/rfc9110.[RFC9449]Fett, D., Campbell, B., Bradley, J., Lodderstedt, T., Jones, M., and D. Waite, "OAuth 2.0 Demonstrating Proof of Possession (DPoP)", RFC 9449, DOI 10.17487/RFC9449, September 2023,https://www.rfc-editor.org/info/rfc9449.
Acknowledgements
okay the thank-you section is my favourite part ๐ฅน first up, I wanted to thank the Academy, the viewers at home, the shampoo manufacturers, etc. ๐ โจ
This specification was developed within the OAuth Working Group under the chairpersonship of Rifaat Shekh-Yusef and Hannes Tschofenig, with Paul Wouters and Roman Danyliw serving as Security Area Directors. ๐ซถ Also huge thanks to these people, who contributed ideas, feedback, corrections, and wording that helped shape this specification: Caleb Baker, Ivan Kanakarakis, Pieter Kasselman, Aaron Parecki, Denis Pinkas, Dima Postnikov, and Filip Skokan. ๐
Some early discussion of the motivations and concepts that precipitated the initial draft version of this document occurred at the 2021 OAuth Security Workshop. The authors thank the organizers of the workshop (Guido Schmitz, Steinar Noem, and Daniel Fett) for hosting an event that is conducive to collaboration and community input. ๐ธ
Authors' Addresses
the authors, in case you want to slide into their DMs about it ๐ง
Vittorio Bertocci
Auth0/Okta
Email: vittorio@auth0.com
Brian Campbell
Ping Identity
Email: bcampbell@pingidentity.com
๐ฆ where this page comes from
- ietf: rfc9470 2023-09
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms