RFC 9470

OAuth 2.0 Step Up Authentication Challenge Protocol

๐Ÿ“… published September 2023, from ietf

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 (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

  1. Protocol Overview
  2. Authentication Requirements Challenge
  3. Authorization Request
  4. Authorization Response
  5. Authentication Information Conveyed via Access Token 6.1. JWT Access Tokens 6.2. OAuth 2.0 Token Introspection
  6. Authorization Server Metadata
  7. Deployment Considerations
  8. Security Considerations
  9. IANA Considerations 10.1. OAuth Extensions Error Registration 10.2. OAuth Token Introspection Response Registration
  10. 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 ๐ŸŽฌ

  1. The client requests a protected resource, presenting an access token.
  2. 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_values and max_age) what authentication requirements must be met for the resource server to authorize a request.
  3. The client sends the user agent to the authorization server with an authorization request that includes the acr_values and/or max_age the resource server asked for in the previous step.
  4. Whatever sequence the grant of choice needs plays out. This includes the necessary steps to authenticate the user in line with the acr_values and/or max_age values 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 ๐Ÿงš
  5. The client retries the request from step 1, this time presenting the newly obtained access token.
  6. 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_authentication error code value to tell the client that the access token presented isn't enough to get into the protected resource, and
  • the acr_values parameter to let the client know that the expected authentication level corresponds to the authentication context class reference identified by myACR.

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 from 1970-01-01T00:00:00Z UTC 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:

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:

11.2. Informative References

these are the extra reading for the curious ๐ŸŒˆ

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