RFC 8705

OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens

πŸ“… published February 2020, from ietf

πŸ“œ RFC 8705: OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens πŸŽ€

okay so bestie, this one is literally about making OAuth tokens way harder to steal πŸ’…βœ¨ It's written by a whole squad: B. Campbell (Ping Identity), J. Bradley (Yubico), N. Sakimura (Nomura Research Institute), and T. Lodderstedt (YES.com AG) 🫢 It came out in February 2020 and it's a Standards Track doc, so it's the real deal, not just a vibe πŸ”₯ It's part of the IETF's ISSN 2070-1721 series. It doesn't obsolete anything and it doesn't update anything either, it's a fresh addition 🌸

Abstract

This doc is giving: OAuth client authentication AND certificate-bound access and refresh tokens, all powered by mutual TLS (mTLS) with X.509 certificates πŸ’œ Clients get a way to authenticate to the authorization server with mutual TLS, using either self-signed certificates or full public key infrastructure (PKI) 🧚 Authorization servers can bind access tokens to a client's mutual-TLS certificate, and protected resources get a way to check that the token being presented was actually issued to the client presenting it πŸ¦‹ Iconic energy, no cap.

Status of This Memo

Okay so this is a Standards Track document, which means it's an actual Internet Standard in the making πŸ’– It's a product of the Internet Engineering Task Force (IETF), it represents the consensus of the IETF community, got public review, and was approved for publication by the Internet Engineering Steering Group (IESG) πŸŽ€ If you want more on Internet Standards, check Section 2 of RFC 7841 πŸ‘‘ For the current status, errata, and feedback, head to https://www.rfc-editor.org/info/rfc8705 🌈

Copyright (c) 2020 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 stand on the publication date, so go read those carefully because they spell out your rights and restrictions πŸ’… Any Code Components pulled out of this doc must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they come with no warranty, per the Simplified BSD License πŸ“

Table of Contents

1.  Introduction
  1.1.  Requirements Notation and Conventions
  1.2.  Terminology
2.  Mutual TLS for OAuth Client Authentication
  2.1.  PKI Mutual-TLS Method
    2.1.1.  PKI Method Metadata Value
    2.1.2.  Client Registration Metadata
  2.2.  Self-Signed Certificate Mutual-TLS Method
    2.2.1.  Self-Signed Method Metadata Value
    2.2.2.  Client Registration Metadata
3.  Mutual-TLS Client Certificate-Bound Access Tokens
  3.1.  JWT Certificate Thumbprint Confirmation Method
  3.2.  Confirmation Method for Token Introspection
  3.3.  Authorization Server Metadata
  3.4.  Client Registration Metadata
4.  Public Clients and Certificate-Bound Tokens
5.  Metadata for Mutual-TLS Endpoint Aliases
6.  Implementation Considerations
  6.1.  Authorization Server
  6.2.  Resource Server
  6.3.  Certificate Expiration and Bound Access Tokens
  6.4.  Implicit Grant Unsupported
  6.5.  TLS Termination
7.  Security Considerations
  7.1.  Certificate-Bound Refresh Tokens
  7.2.  Certificate Thumbprint Binding
  7.3.  TLS Versions and Best Practices
  7.4.  X.509 Certificate Spoofing
  7.5.  X.509 Certificate Parsing and Validation Complexity
8.  Privacy Considerations
9.  IANA Considerations
  9.1.  JWT Confirmation Methods Registration
  9.2.  Authorization Server Metadata Registration
  9.3.  Token Endpoint Authentication Method Registration
  9.4.  Token Introspection Response Registration
  9.5.  Dynamic Client Registration Metadata Registration
10. References
  10.1.  Normative References
  10.2.  Informative References
Appendix A.  Example "cnf" Claim, Certificate, and JWK
Appendix B.  Relationship to Token Binding
Acknowledgements
Authors' Addresses

1. Introduction ✨

1.1. Requirements Notation and Conventions

1.2. Terminology


1. Introduction ✨

Okay so the OAuth 2.0 Authorization Framework [RFC6749] lets third-party client apps get delegated access to protected resources πŸ¦‹ In the classic abstract flow (Figure 1 below), the client grabs an access token from something called an authorization server, then uses that token to reach protected resources like HTTPS APIs πŸ’–

  +--------+                                 +---------------+
  |        |                                 |               |
  |        |<--(A)-- Get an access token --->| Authorization |
  |        |                                 |     Server    |
  |        |                                 |               |
  |        |                                 +---------------+
  |        |                                         ^
  |        |                                         |
  |        |
  |        |                               (C)       |
  | Client |                           Validate the
  |        |                           access token  |
  |        |
  |        |                                         |
  |        |                                         v
  |        |                                 +---------------+
  |        |                                 |      (C)      |
  |        |                                 |               |
  |        |<--(B)-- Use the access token -->|   Protected   |
  |        |                                 |    Resource   |
  |        |                                 |               |
  +--------+                                 +---------------+

                 Figure 1: Abstract OAuth 2.0 Protocol Flow

So the flow in Figure 1 has these steps:

  • (A) The client sends an HTTPS "POST" request to the authorization server and presents a credential for the authorization grant. For certain clients (the ones that were issued or otherwise set up with a set of client credentials) the request MUST be authenticated. In the response, the authorization server hands the client an access token πŸŽ€
  • (B) The client includes the access token when it makes a request to access a protected resource 🌸
  • (C) The protected resource validates the access token so it can authorize the request. Sometimes, like when the token is self-contained and cryptographically secured, the protected resource can validate it locally 🧚 Other times it has to call out to the authorization server to find out the state of the token and get metainformation about it πŸ’…

Okay so this doc layers enhanced security options on top of that abstract flow using client-certificate-based mutual TLS πŸ”₯ Section 2 gives options for authenticating the request in Step (A) ✨ Step (C) gets semantics for expressing the binding of the token to the client certificate, for both local and remote processing, in Sections 3.1 and 3.2 respectively πŸ’œ This means that, as Section 3 describes, access to a protected resource in Step (B) is only possible for the legit client, using a certificate-bound token and holding the private key that matches the certificate 🫢

OAuth 2.0 has a shared-secret method of client authentication, but it also lets you define and use extra client authentication mechanisms when talking directly to the authorization server 🌈 This doc describes one of those extras: mutual-TLS certificate-based authentication, which has better security characteristics than shared secrets πŸ‘‘ [RFC6749] covers client authentication for requests to the token endpoint, and extensions like Introspection [RFC7662], Revocation [RFC7009], and the Backchannel Authentication Endpoint in [OpenID.CIBA] also define endpoints that use client authentication. The mutual-TLS methods in this doc apply to those endpoints too πŸ“

Mutual-TLS certificate-bound access tokens make sure that only the party holding the private key for the certificate can use the token to access the associated resources πŸ’– This constraint is sometimes called key confirmation, proof-of-possession, or holder-of-key πŸ¦‹ It's different from the bearer token in [RFC6750], where anyone holding the access token can use it to get to the resources. Binding the token to the client's certificate stops stolen access tokens, and stops replay of access tokens by unauthorized parties, dead πŸ’€

Mutual-TLS certificate-bound access tokens and mutual-TLS client authentication are separate mechanisms. They're complementary, but they don't necessarily have to be deployed or used together πŸŽ€

This document also introduces additional client metadata parameters to support certificate-bound access tokens and mutual-TLS client authentication ✨ The authorization server can get client metadata through the Dynamic Client Registration Protocol [RFC7591], which defines ways to dynamically register OAuth 2.0 client metadata with authorization servers 🧚 Also, the metadata defined by [RFC7591], plus registered extensions to it, imply a general data model for clients that is useful for authorization server implementations, even when the Dynamic Client Registration Protocol isn't being used 🌸 Those implementations usually have some kind of user interface for managing client configuration πŸ’…

1.1. Requirements Notation and Conventions πŸ“œ

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 πŸ”₯

1.2. Terminology πŸ“–

Throughout this document, "mutual TLS" means this: in addition to the normal TLS server authentication with a certificate, a client presents its X.509 certificate and proves it has the matching private key to a server while negotiating a TLS session πŸ’œ In contemporary versions of TLS [RFC5246] [RFC8446], the client has to send the Certificate and CertificateVerify messages during the handshake, and the server has to verify the CertificateVerify and Finished messages πŸ¦‹


(Part 1 of 6. More parts coming, bestie, so stay tuned πŸ’–βœ¨)

2. Mutual TLS for OAuth Client Authentication ✨

okay bestie, this is the part where mutual TLS gets to be the star of the show πŸ¦‹ so the authorization server gets to decide whether a client has to use mutual TLS at all, based on policy or config for that client πŸ’…. Doesn't matter how the client got registered (dynamic, static, or whatever), the AS is the one calling the shots πŸ‘‘

Once mutual TLS is in play, the TLS connection to the AS MUST have been set up (or re-set up) with mutual-TLS X.509 client certificate auth. That means the client's Certificate and CertificateVerify messages both go out during the TLS handshake πŸ”₯

Now for every request that uses mutual-TLS client auth, the client MUST include the client_id parameter from Section 2.2 of [RFC6749] πŸŽ€. Why? Because it lets the AS spot the client super easily without digging through the certificate 🧚 The AS can look up the client config using that identifier and check the cert from the handshake against what it expects for that client. The AS MUST enforce the binding between client and certificate, as described in Section 2.1 or 2.2 below πŸ’–. If there's no certificate, or the one presented doesn't match what's expected for that client_id, the AS sends back a normal OAuth 2.0 error response per Section 5.2 of [RFC6749] with the invalid_client error code πŸ’€. That's the AS saying "nope, client auth failed" πŸ™ˆ

2.1. PKI Mutual-TLS Method

This is the classic way people use X.509 certs for authentication 🌸 It leans on a validated certificate chain [RFC5280] and a single subject distinguished name (DN) or a single subject alternative name (SAN) to authenticate the client. Only one subject name value of any type gets used per client, no mixing and matching πŸ’… The TLS handshake proves the client actually holds the private key matching the cert's public key, and that the chain checks out πŸ” The client is authenticated if the subject info in the cert matches the one single expected subject configured or registered for that client. Be careful with DN comparisons πŸ™ˆ: you need a predictable treatment of DN values, like the distinguishedNameMatch rule from [RFC4517], when comparing the cert's subject DN to the client's registered DN.

Revocation checking is possible with this method, but whether and how to check revocation is a deployment decision left up to the AS πŸ¦‹ Clients can rotate their certs without touching the authentication data at the AS, as long as they get a new cert with the same subject from a trusted CA ✨

2.1.1. PKI Method Metadata Value

For the PKI method, this spec defines and registers the following authentication method metadata value into the "OAuth Token Endpoint Authentication Methods" registry [IANA.OAuth.Parameters] πŸ“

tls_client_auth Indicates that client authentication to the authorization server will occur with mutual TLS using the PKI method of associating a certificate to a client.

2.1.2. Client Registration Metadata

So the AS needs to know which subject to expect, right? πŸ’œ That's where these metadata parameters come in for the OAuth 2.0 Dynamic Client Registration Protocol [RFC7591]. A client using the tls_client_auth method MUST use exactly one of the metadata parameters below to indicate the certificate subject value the AS should expect πŸ‘‘ One and only one, babe πŸ’…

tls_client_auth_subject_dn A string representation (as defined in [RFC4514]) of the expected subject distinguished name of the certificate the OAuth client will use in mutual-TLS authentication.

tls_client_auth_san_dns A string containing the value of an expected dNSName SAN entry in the certificate the OAuth client will use in mutual-TLS authentication.

tls_client_auth_san_uri A string containing the value of an expected uniformResourceIdentifier SAN entry in the certificate the OAuth client will use in mutual-TLS authentication.

tls_client_auth_san_ip A string representation of an IP address in either dotted decimal notation (for IPv4) or colon-delimited hexadecimal (for IPv6, as defined in [RFC5952]) that is expected to be present as an iPAddress SAN entry in the certificate the OAuth client will use in mutual-TLS authentication. Per Section 8 of [RFC5952], the IP address comparison of the value in this parameter and the SAN entry in the certificate is to be done in binary format 🧠

tls_client_auth_san_email A string containing the value of an expected rfc822Name SAN entry in the certificate the OAuth client will use in mutual-TLS authentication.

2.2. Self-Signed Certificate Mutual-TLS Method

This one is for self-signed certs, so you don't need to run a whole PKI 🌈 As a prerequisite, the client registers its X.509 certs with the AS, either directly using jwks from [RFC7591] or through a reference to a trusted source using jwks_uri from [RFC7591] 🫢 During auth, TLS proves the client has the private key for the public key in the cert from the handshake. Unlike the PKI method, the server does NOT validate the client's cert chain here 😭 The client is authenticated if the cert it presented during the handshake matches one of the certs configured or registered for that client. Plus, if you pair it with a jwks_uri, the client can rotate its certs without changing its auth data directly with the AS ✨

2.2.1. Self-Signed Method Metadata Value

For the Self-Signed Certificate method, this spec defines and registers the following authentication method metadata value into the "OAuth Token Endpoint Authentication Methods" registry [IANA.OAuth.Parameters] πŸ¦‹

self_signed_tls_client_auth Indicates that client authentication to the authorization server will occur using mutual TLS with the client utilizing a self-signed certificate.

2.2.2. Client Registration Metadata

For binding a cert to a client with the Self-Signed method, the existing jwks_uri or jwks metadata parameters from [RFC7591] carry the client's certs as JSON Web Keys (JWK) in a JWK Set [RFC7517] πŸ’– The jwks parameter is a JWK Set holding the client's public keys as an array of JWKs, while jwks_uri is a URL pointing at the client's JWK Set πŸ”— A cert is represented with the x5c parameter of an individual JWK within the set.

Quick heads up πŸ™ˆ: the members of the JWK that represent the public key (like n and e for RSA, or x and y for Elliptic Curve (EC)) are REQUIRED parameters per [RFC7518], so they'll be there even though they aren't used in this context. Also, Section 4.7 of [RFC7517] requires that the key in the first certificate of the x5c parameter matches the public key represented by those other members of the JWK 🧚

3. Mutual-TLS Client Certificate-Bound Access Tokens πŸ”

When the client uses mutual TLS on its connection to the token endpoint, the AS can bind the issued access token to the client cert πŸ’… That binding happens by associating the cert with the token in a way the protected resource can access, like embedding the certificate hash in the access token directly (using the syntax from Section 3.1), or through token introspection (Section 3.2) 🌸 Doing it this way decouples the binding from the client's auth with the AS, which means mutual TLS during protected resource access can just act as a proof-of-possession mechanism. Other ways of associating a cert with an access token are possible too, if the AS and the protected resource agree on them, but they're beyond this spec's scope πŸ¦‹

For a resource server to use cert-bound access tokens, it has to know in advance that mutual TLS will be used for some or all resource accesses πŸ‘‘ The access token itself can't decide whether to request mutual TLS, because from the TLS perspective it's just "Application Data," only exchanged after the handshake is done. The initial CertificateRequest happens during the handshake, before that Application Data is available πŸ™ˆ Later chances for a TLS client to present a cert might exist, like TLS 1.2 renegotiation [RFC5246] or TLS 1.3 post-handshake authentication [RFC8446], but this document makes no provision for using them 🧚 It's expected that a mutual-TLS resource server will either require mutual TLS for everything it hosts, or serve mutual-TLS-protected and regular resources on separate hostname and port combinations, though other setups are possible πŸ’œ How resource server policy gets synced with the AS is out of scope for this doc 🫢

Within a mutual-TLS-protected resource-access flow, the client makes protected resource requests as described in [RFC6750] πŸŽ€ Those requests MUST be made over a mutually authenticated TLS connection using the same cert that was used for mutual TLS at the token endpoint ✨

The protected resource MUST get the client cert used for mutual TLS from its TLS implementation layer, and MUST verify that it matches the cert associated with the access token πŸ”₯ If they don't match, the resource access attempt MUST be rejected with an error per [RFC6750], using an HTTP 401 status code and the invalid_token error code πŸ’€

Metadata for advertising server and client capabilities for mutual-TLS client certificate-bound access tokens is defined in Sections 3.3 and 3.4, respectively 🌈

3.1. JWT Certificate Thumbprint Confirmation Method πŸ’–

okay so when access tokens are JWTs [RFC7519], the certificate hash info SHOULD be represented with the x5t#S256 confirmation method member we're defining here πŸŽ€

to put the hash of a certificate inside a JWT, this spec adds a brand new JWT Confirmation Method [RFC7800] member called x5t#S256 for the X.509 Certificate SHA-256 Thumbprint ✨ its value is a base64url-encoded [RFC4648] SHA-256 [SHS] hash (aka thumbprint, fingerprint, or digest 🧚) of the DER encoding [X690] of the X.509 certificate [RFC5280]. the base64url-encoded value MUST omit all trailing pad '=' characters and MUST NOT include any line breaks, whitespace, or other additional characters πŸ™ˆ

here's an example JWT payload with an x5t#S256 certificate thumbprint confirmation method. the new JWT content this spec brings in is the cnf confirmation method claim at the bottom, which has the x5t#S256 member holding the hash of the client certificate the access token is bound to πŸ¦‹

  {
    "iss": "https://server.example.com",
    "sub": "ty.webb@example.com",
    "exp": 1493726400,
    "nbf": 1493722800,
    "cnf":{
      "x5t#S256": "bwcK0esc3ACC3DB2Y5_lESsXE8o9ltc05O89jdN-dg2"
    }
  }

Figure 2: Example JWT Claims Set with an X.509 Certificate Thumbprint Confirmation Method

3.2. Confirmation Method for Token Introspection πŸ”

okay, so OAuth 2.0 Token Introspection [RFC7662] is the thing that lets a protected resource ask an authorization server whether an access token is still active, plus grab some metainformation about it 🌸

for a mutual-TLS client certificate-bound access token, the hash of the certificate the token is bound to gets passed along to the protected resource as metainformation in a token introspection response πŸ’œ it uses the same cnf with x5t#S256 member structure as the certificate SHA-256 thumbprint confirmation method from Section 3.1, sitting as a top-level member of the introspection response JSON. the protected resource then compares that certificate hash to a hash of the client certificate that was used for mutual-TLS authentication, and rejects the request if they don't match. bestie, that's the whole security check 😭

here's an example introspection response for an active token with an x5t#S256 certificate thumbprint confirmation method. the new bit is the cnf confirmation method at the bottom, with the x5t#S256 member holding the hash of the client certificate the access token is bound to ✨

  HTTP/1.1 200 OK
  Content-Type: application/json

  {
    "active": true,
    "iss": "https://server.example.com",
    "sub": "ty.webb@example.com",
    "exp": 1493726400,
    "nbf": 1493722800,
    "cnf":{
      "x5t#S256": "bwcK0esc3ACC3DB2Y5_lESsXE8o9ltc05O89jdN-dg2"
    }
  }

Figure 3: Example Introspection Response for a Certificate-Bound Access Token

3.3. Authorization Server Metadata πŸŽ€

this doc adds a new authorization server metadata [RFC8414] parameter so the server can say it's able to issue certificate-bound access tokens 🧚

tls_client_certificate_bound_access_tokens OPTIONAL. This is a boolean that tells you whether the server supports mutual-TLS client certificate-bound access tokens. If it's left out, the default value is "false" πŸ’…

3.4. Client Registration Metadata 🌈

there's also a new client metadata parameter for when a client wants to say "hey, I plan to use certificate-bound access tokens" πŸ’–

tls_client_certificate_bound_access_tokens OPTIONAL. This is a boolean the client uses to show it intends to use mutual-TLS client certificate-bound access tokens. If it's left out, the default value is "false" 🫢

btw, if a client that said it wants mutual-TLS certificate-bound tokens makes a request to the token endpoint over a connection that isn't mutual TLS, it's up to the authorization server whether to throw an error or just hand out an unbound token πŸ™ˆ

4. Public Clients and Certificate-Bound Tokens πŸ‘‘

mutual-TLS OAuth client authentication and certificate-bound access tokens are independent of each other, so you can use one without the other πŸ’… for example, certificate-bound access tokens without mutual-TLS OAuth client authentication are a thing, and they're good for public clients (the ones with no authentication credentials tied to the client_id) πŸ¦‹ the authorization server would set up its TLS stack the same way as for the Self-Signed Certificate method, so it doesn't check whether the certificate the client shows during the handshake is signed by a trusted CA ✨ each instance of a client makes its own self-signed certificate for mutual TLS with both the authorization server and the resource server 🧚

the authorization server wouldn't use that mutual-TLS certificate to authenticate the client at the OAuth layer. instead, it binds the issued access token to the certificate, for which the client has proven it holds the matching private key πŸ” so the access token is tied to that certificate and can only be used by the client that has the certificate and private key and uses them to negotiate mutual TLS on connections to the resource server πŸ’œ when the authorization server issues a refresh token to a client like this, it SHOULD also bind the refresh token to the same certificate, and check that binding whenever the refresh token shows up to get new access tokens 🌸 how the refresh token binding actually works is up to the authorization server 🫢

5. Metadata for Mutual-TLS Endpoint Aliases πŸ“

so negotiating client certificate-based mutual TLS goes like this: the TLS server asks the TLS client for a certificate, because the client doesn't just send one unprompted πŸ™ˆ a server can be set up so client certificates are optional, meaning the connection can keep going even if the client doesn't send one. but the server asking for a certificate can cause problems with some clients 😭 that's especially true of web browsers, which will usually throw an intrusive certificate selection popup at the end user when the server asks for a certificate πŸ’€

authorization servers that support both mutual-TLS clients and conventional clients MAY chose to isolate the server-side mutual-TLS behavior to only the clients that want to do mutual TLS, so the conventional clients don't get hit by any weird side effects πŸŽ€ to make that separation possible, this metadata parameter is introduced:

mtls_endpoint_aliases OPTIONAL. This is a JSON object holding alternative authorization server endpoints. When they're present, an OAuth client that wants to do mutual TLS uses them instead of the conventional endpoints ✨ the parameter value is made of one or more endpoint parameters, such as "token_endpoint", "revocation_endpoint", "introspection_endpoint", etc., which are conventionally defined at the top level of authorization server metadata πŸ¦‹ an OAuth client that wants mutual TLS (for OAuth client authentication and/or to get or use certificate-bound tokens), when it makes a request directly to the authorization server, MUST use the alias URL of the endpoint in mtls_endpoint_aliases when one is present, rather than the endpoint URL with the same name at the top level of the metadata πŸ‘‘ if an endpoint isn't in mtls_endpoint_aliases, the client uses the conventional endpoint URL from the top level of the authorization server metadata 🫢 metadata parameters inside mtls_endpoint_aliases that don't define endpoints an OAuth client sends direct requests to have no meaning, and SHOULD be ignored πŸ™ˆ

here's an example authorization server metadata document with the mtls_endpoint_aliases parameter. it lists aliases for the token, revocation, and introspection endpoints that a client doing mutual TLS would use instead of the conventional ones πŸ’– notice that the endpoints in mtls_endpoint_aliases use a different host than their conventional counterparts. that lets the authorization server tell its TLS behavior apart (through the TLS server_name extension [RFC6066] or by using actually distinct hosts) 🧚

  {
    "issuer": "https://server.example.com",
    "authorization_endpoint": "https://server.example.com/authz",
    "token_endpoint": "https://server.example.com/token",
    "introspection_endpoint": "https://server.example.com/introspect",
    "revocation_endpoint": "https://server.example.com/revo",
    "jwks_uri": "https://server.example.com/jwks",
    "response_types_supported": ["code"],
    "response_modes_supported": ["fragment","query","form_post"],
    "grant_types_supported": ["authorization_code", "refresh_token"],
    "token_endpoint_auth_methods_supported":
                    ["tls_client_auth","client_secret_basic","none"],
    "tls_client_certificate_bound_access_tokens": true,
    "mtls_endpoint_aliases": {
      "token_endpoint": "https://mtls.example.com/token",
      "revocation_endpoint": "https://mtls.example.com/revo",
      "introspection_endpoint": "https://mtls.example.com/introspect"
    }
  }

Figure 4: Example Authorization Server Metadata with Mutual-TLS Endpoint Aliases

6. Implementation Considerations πŸ”₯

6.1. Authorization Server 🌈

the authorization server needs to set up its TLS configuration properly for the OAuth client authentication methods it supports πŸ’…

if an authorization server supports mutual-TLS client authentication and other client authentication methods (or public clients) at the same time, it would make mutual TLS optional, i.e., a handshake is allowed to continue after the server asks for a client certificate even if the client doesn't send one 🫢

to support only the Self-Signed Certificate method, the authorization server would configure its TLS stack so it doesn't verify whether the certificate the client shows during the handshake is signed by a trusted CA certificate ✨

like Section 3 described, the authorization server binds the issued access token to the TLS client certificate. that means it will only issue certificate-bound tokens for a certificate the client has proven it holds the private key for πŸ”

the authorization server may also think about hosting the token endpoint and other endpoints that need client authentication on a separate host name or port 🧚 that way they don't have unintended effects on the TLS behavior of its other endpoints, like the authorization endpoint πŸ¦‹ and as Section 5 explained, it can go further and limit any possible impact of asking for client certificates by offering a separate set of endpoints on another host or port. those are aliases for the originals, and clients doing mutual TLS will use them in preference to the conventional endpoints πŸ’–

6.2. Resource Server πŸŽ€

okay bestie, so the authorization server and the resource server have very different jobs πŸ’… The authorization server does client authentication and gets the resource owner (the end user) to say yes. The resource server just looks at the access token the client shows up with and decides what to allow. It does not directly authenticate the client per se πŸ™ˆ The way the access token gets bound to the client certificate, and how the protected resource checks the proof-of-possession, means all of that is decoupled from whatever method the client used to log in at the authorization server. So mutual TLS during protected resource access can just act as a proof-of-possession mechanism, full stop ✨ That means it is not necessary for the resource server to validate the trust chain of the client's certificate in any of the methods defined in this document. The resource server would, therefore, configure its TLS stack so it does not verify whether the certificate presented by the client during the handshake is signed by a trusted CA certificate. Wild, but it makes sense once you see the split 🧚

6.3. Certificate Expiration and Bound Access Tokens πŸ¦‹

remember from Section 3 how an access token is bound to a specific client certificate? πŸ”— That means the same certificate has to be used for mutual TLS on protected resource access. It also means access tokens get invalidated when a client updates its certificate. The good news is this can be handled just like expired access tokens: the client requests a new access token (typically with a refresh token) and then retries the protected resource request πŸ’– Super smooth, no drama.

6.4. Implicit Grant Unsupported 🚫

okay so this document describes binding an access token to the client certificate presented on the TLS connection from the client to the authorization server's token endpoint. BUT binding access tokens that get issued straight from the authorization endpoint via the implicit grant flow is explicitly out of scope πŸ™…β€β™€οΈ Why? Because end users interact with the authorization endpoint through a web browser, and using client certificates in users' browsers brings operational and usability issues. It's just not worth it. If you want certificate-bound access tokens, you should use grant types where the client makes an access token request directly to the token endpoint, like the authorization code and refresh token grant types 🌸

6.5. TLS Termination 🌈

an authorization server or resource server MAY choose to terminate TLS connections at a load balancer, reverse proxy, or some other network intermediary πŸ’œ Now, how the client certificate metadata gets securely passed from that intermediary to the application server is out of scope for this specification. So that part's on you, bestie 🫢

7. Security Considerations πŸ”

this section is SO important, so pay attention, no cap 😭

7.1. Certificate-Bound Refresh Tokens πŸ“

the OAuth 2.0 Authorization Framework [RFC6749] requires that an authorization server (AS) binds refresh tokens to the client they were issued to, and that confidential clients (the ones with established authentication credentials with the AS) authenticate to the AS when they present a refresh token. So refresh tokens are indirectly certificate-bound through the client ID, plus the requirement for (certificate-based) authentication to the AS when they're issued to clients using the "tls_client_auth" or "self_signed_tls_client_auth" methods of client authentication. Section 4 covers certificate-bound refresh tokens issued to public clients (the ones without authentication credentials associated with the "client_id") ✨

7.2. Certificate Thumbprint Binding πŸ”

the binding between the certificate and access token from Section 3.1 uses a cryptographic hash of the certificate. It relies on the hash function having sufficient second-preimage resistance, so that it's computationally infeasible to find or create another certificate that produces the same hash output value. SHA-256 was used because it meets that requirement while also being widely available πŸ’… If, in the future, certificate thumbprints need to be computed with hash functions other than SHA-256, it is suggested that, for additional related JWT confirmation methods, members be defined for that purpose and registered in the IANA "JWT Confirmation Methods" registry [IANA.JWT.Claims] for JWT "cnf" member values πŸ¦‹

⚠️ community knowledge about how strong algorithms are, and what attacks are feasible, can change suddenly. Experience shows that a security document is a point-in-time statement, so readers are advised to seek out any errata or updates that apply to this document. Stay updated, periodt 😭

7.3. TLS Versions and Best Practices πŸŽ€

this document works with any TLS version that supports certificate-based client authentication. Both TLS 1.3 [RFC8446] and TLS 1.2 [RFC5246] are cited here because, at the time of writing, 1.3 is the newest version while 1.2 is the most widely deployed 🌸 General implementation and security considerations for TLS, including version recommendations, can be found in [BCP195].

TLS certificate validation (for both client and server certificates) needs a local database of trusted certificate authorities (CAs). Decisions about which CAs to trust, and how to make that call, are out of scope for this document 🧚

7.4. X.509 Certificate Spoofing πŸ‘€

if the PKI method of client authentication is used, an attacker could try to impersonate a client using a certificate with the same subject (DN or SAN) but issued by a different CA that the authorization server trusts. To cope with that threat, the authorization server SHOULD only accept, as trust anchors, a limited number of CAs whose certificate issuance policy meets its security requirements. There's an assumption here that the client and server agree out of band on the set of trust anchors the server uses to create and validate the certificate chain. Without that assumption, using a subject to identify the client certificate would open the server up to certificate spoofing attacks πŸ’€

7.5. X.509 Certificate Parsing and Validation Complexity 🧩

parsing and validating X.509 certificates and certificate chains is complex, and implementation mistakes have exposed security vulnerabilities before. The complexities of validation include (but are not limited to) [CX5P] [DCW] [RFC5280]:

  • checking basic constraints, basic and extended key usage constraints, validity periods, and critical extensions;
  • handling embedded NUL bytes in ASN.1 counted-length strings, and non-canonical or non-normalized string representations in subject names;
  • handling wildcard patterns in subject names;
  • recursive verification of certificate chains and checking certificate revocation.

For these reasons, implementors SHOULD use an established and well-tested X.509 library (such as one used by an established TLS library) for validating X.509 certificate chains, and SHOULD NOT attempt to write their own X.509 certificate validation procedures. Do not roll your own X.509 validation, bestie πŸ™…β€β™€οΈπŸ’…

8. Privacy Considerations 🫣

in TLS versions before 1.3, the client's certificate is sent unencrypted in the initial handshake, and third parties can potentially use it to monitor, track, and correlate client activity. This is probably of little concern for clients acting on behalf of a significant number of end users, because individual user activity won't be discernible amid the client activity as a whole. However, clients acting on behalf of a single end user, like a native application on a mobile device, should use TLS version 1.3 whenever possible, or consider the potential privacy implications of using mutual TLS on earlier versions πŸ”’

9. IANA Considerations πŸ“‹

9.1. JWT Confirmation Methods Registration 🌟

per this specification, the following value has been registered in the IANA "JWT Confirmation Methods" registry [IANA.JWT.Claims] for JWT "cnf" member values established by [RFC7800]:

Confirmation Method Value:  "x5t#S256"
Confirmation Method Description:  X.509 Certificate SHA-256
   Thumbprint
Change Controller:  IESG
Specification Document(s):  Section 3.1 of RFC 8705

9.2. Authorization Server Metadata Registration πŸ—‚οΈ

per this specification, the following values have been registered in the IANA "OAuth Authorization Server Metadata" registry [IANA.OAuth.Parameters] established by [RFC8414]:

Metadata Name:  "tls_client_certificate_bound_access_tokens"
Metadata Description:  Indicates authorization server support for
   mutual-TLS client certificate-bound access tokens.
Change Controller:  IESG
Specification Document(s):  Section 3.3 of RFC 8705

Metadata Name:  "mtls_endpoint_aliases"
Metadata Description:  JSON object containing alternative
   authorization server endpoints, which a client intending to do
   mutual TLS will use in preference to the conventional endpoints.
Change Controller:  IESG
Specification Document(s):  Section 5 of RFC 8705

9.3. Token Endpoint Authentication Method Registration πŸ”‘

per this specification, the following values have been registered in the IANA "OAuth Token Endpoint Authentication Methods" registry [IANA.OAuth.Parameters] established by [RFC7591]:

Token Endpoint Authentication Method Name:  "tls_client_auth"
Change Controller:  IESG
Specification Document(s):  Section 2.1.1 of RFC 8705

Token Endpoint Authentication Method Name:  "self_signed_tls_client_
   auth"
Change Controller:  IESG
Specification Document(s):  Section 2.2.1 of RFC 8705

9.4. Token Introspection Response Registration πŸ”

okay so "Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs)" [RFC7800] defined the "cnf" (confirmation) claim, which lets confirmation key information get carried in a JWT. But the same proof-of-possession semantics are also useful for introspected access tokens. The protected resource gets the confirmation key data as metainformation from a token introspection response, and uses that to verify proof-of-possession 🧚 So this specification defines and registers proof-of-possession semantics for OAuth 2.0 Token Introspection [RFC7662] using the "cnf" structure. When included as a top-level member of an OAuth token introspection response, "cnf" has the same semantics and format as the claim of the same name defined in [RFC7800]. This specification only explicitly uses the "x5t#S256" confirmation method member (see Section 3.2), but it needs to define and register the higher-level "cnf" structure as an introspection response member in order to define and use the more specific certificate thumbprint confirmation method πŸ’œ

So, the following values have been registered in the IANA "OAuth Token Introspection Response" registry [IANA.OAuth.Parameters] established by [RFC7662]:

Claim Name:  "cnf"
Claim Description:  Confirmation
Change Controller:  IESG
Specification Document(s):  [RFC7800] and RFC 8705

9.5. Dynamic Client Registration Metadata Registration ✨

okay so this part is all about registering new client metadata names πŸ’… Per this spec, these five-plus names got added to the IANA "OAuth Dynamic Client Registration Metadata" registry [IANA.OAuth.Parameters], which [RFC7591] set up. Here's the full list, slay πŸŽ€

Client Metadata Name:  "tls_client_certificate_bound_access_tokens"
Client Metadata Description:  Indicates the client's intention to use
   mutual-TLS client certificate-bound access tokens.
Change Controller:  IESG
Specification Document(s):  Section 3.4 of RFC 8705
Client Metadata Name:  "tls_client_auth_subject_dn"
Client Metadata Description:  String value specifying the expected
   subject DN of the client certificate.
Change Controller:  IESG
Specification Document(s):  Section 2.1.2 of RFC 8705
Client Metadata Name:  "tls_client_auth_san_dns"
Client Metadata Description:  String value specifying the expected
   dNSName SAN entry in the client certificate.
Change Controller:  IESG
Specification Document(s):  Section 2.1.2 of RFC 8705
Client Metadata Name:  "tls_client_auth_san_uri"
Client Metadata Description:  String value specifying the expected
   uniformResourceIdentifier SAN entry in the client certificate.
Change Controller:  IESG
Specification Document(s):  Section 2.1.2 of RFC 8705
Client Metadata Name:  "tls_client_auth_san_ip"
Client Metadata Description:  String value specifying the expected
   iPAddress SAN entry in the client certificate.
Change Controller:  IESG
Specification Document(s):  Section 2.1.2 of RFC 8705
Client Metadata Name:  "tls_client_auth_san_email"
Client Metadata Description:  String value specifying the expected
   rfc822Name SAN entry in the client certificate.
Change Controller:  IESG
Specification Document(s):  Section 2.1.2 of RFC 8705

The first one is the "I want certificate-bound tokens" flag πŸ”, and the other five are the expected-cert checks: subject DN plus the dNSName, uniformResourceIdentifier, iPAddress, and rfc822Name SAN entries. Main character energy, no cap 😭🫢


10. References πŸ“š

10.1. Normative References

10.2. Informative References


Appendix A. Example "cnf" Claim, Certificate, and JWK 🧚

okay this is the fun bit, the examples! πŸ¦‹ We've got an "x5t#S256" value plus the X.509 certificate it was calculated from, shown as Figures 5 and 6 in that order. Then Figure 7 shows a JWK version of the certificate's public key, with the "x5c" member along for the ride πŸ’–

"cnf":{"x5t#S256":"A4DtL2JmUMhAsvJj5tKyn64SqzmuXbMrJa0n761y5v0"}

Figure 5: x5t#S256 Confirmation Claim

-----BEGIN CERTIFICATE-----
MIIBBjCBrAIBAjAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDDARtdGxzMB4XDTE4MTAx
ODEyMzcwOVoXDTIyMDUwMjEyMzcwOVowDzENMAsGA1UEAwwEbXRsczBZMBMGByqG
SM49AgEGCCqGSM49AwEHA0IABNcnyxwqV6hY8QnhxxzFQ03C7HKW9OylMbnQZjjJ
/Au08/coZwxS7LfA4vOLS9WuneIXhbGGWvsDSb0tH6IxLm8wCgYIKoZIzj0EAwID
SQAwRgIhAP0RC1E+vwJD/D1AGHGzuri+hlV/PpQEKTWUVeORWz83AiEA5x2eXZOV
bUlJSGQgjwD5vaUaKlLR50Q2DmFfQj1L+SY=
-----END CERTIFICATE-----

Figure 6: PEM Encoded Self-Signed Certificate

{
 "kty":"EC",
 "x":"1yfLHCpXqFjxCeHHHMVDTcLscpb07KUxudBmOMn8C7Q",
 "y":"8_coZwxS7LfA4vOLS9WuneIXhbGGWvsDSb0tH6IxLm8",
 "crv":"P-256",
 "x5c":[
  "MIIBBjCBrAIBAjAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDDARtdGxzMB4XDTE4MTA
   xODEyMzcwOVoXDTIyMDUwMjEyMzcwOVowDzENMAsGA1UEAwwEbXRsczBZMBMGBy
   qGSM49AgEGCCqGSM49AwEHA0IABNcnyxwqV6hY8QnhxxzFQ03C7HKW9OylMbnQZ
   jjJ/Au08/coZwxS7LfA4vOLS9WuneIXhbGGWvsDSb0tH6IxLm8wCgYIKoZIzj0E
   AwIDSQAwRgIhAP0RC1E+vwJD/D1AGHGzuri+hlV/PpQEKTWUVeORWz83AiEA5x2
   eXZOVbUlJSGQgjwD5vaUaKlLR50Q2DmFfQj1L+SY="
   ]
 }

Figure 7: JSON Web Key

Appendix B. Relationship to Token Binding πŸ¦‹

okay so bestie, this appendix is basically the "how is this different from that other proof-of-possession thing?" section πŸ’… OAuth 2.0 Token Binding [TOKEN] lets you bind tokens and artifacts all over OAuth to a Token Binding key. It's giving the same vibe as the mutual-TLS certificate-bound access tokens in this document πŸ” Both are what people call proof-of-possession: the authorization server ties the access token it issues to an asymmetric key pair that the client holds, and then the client has to prove it has the private key for the TLS connection it uses to reach the protected resource. The details are a bit different between the two, but the core idea is the same ✨

Now the lowkey big difference πŸ‘€ Token Binding uses bare keys generated on the client, so you skip a whole pile of certificate headaches (creating them, handing them out, managing them). Honestly iconic πŸ™Œ BUT, and this is a huge but 😭, Token Binding is fairly new when this was written, and there's barely any support for it in app dev platforms and tooling. Until the core Token Binding specs get better support, real OAuth 2.0 Token Binding implementations are basically not feasible πŸ’€

Mutual TLS has been around forever and has super widespread support in web servers and dev platforms πŸ’ͺ So OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens can be built and deployed right now with the tools you already have πŸŽ€ In the future, the two specs will probably be deployed side by side, solving similar problems in different environments 🌈 And honestly, authorization servers might support both at once, using different proof-of-possession mechanisms for tokens issued to different clients. Main character energy for the auth server πŸ‘‘


Acknowledgements 🫢

Scott "not Tomlinson" Tomilson and Matt Peterson were involved in design and development work on a mutual-TLS OAuth client authentication implementation that came before this document πŸ’œ What they learned from that work helped shape some of the content here, so shoutout to them πŸ™

This specification was developed within the OAuth Working Group under the chairmanship of Hannes Tschofenig and Rifaat Shekh-Yusef 🌸 Eric Rescorla, Benjamin Kaduk, and Roman Danyliw served as Security Area Directors 🧚 Also, a whole crew of people contributed ideas, feedback, and wording that helped shape this spec, and they're all absolutely iconic: Vittorio Bertocci, Sergey Beryozkin, Ralph Bragg, Sophie Bremer, Roman Danyliw, Vladimir Dzhuvinov, Samuel Erdtman, Evan Gilman, Leif Johansson, Michael Jones, Phil Hunt, Benjamin Kaduk, Takahiko Kawasaki, Sean Leonard, Kepeng Li, Neil Madden, James Manger, Jim Manico, Nov Matake, Sascha Preibisch, Eric Rescorla, Justin Richer, Vincent Roca, Filip Skokan, Dave Tonge, and Hannes Tschofenig πŸ¦‹βœ¨


Authors' Addresses πŸ’Œ

Brian Campbell 🌟 Ping Identity

Email: brian.d.campbell@gmail.com

John Bradley πŸ“ Yubico

Email: ve7jtb@ve7jtb.com URI: http://www.thread-safe.com/

Nat Sakimura 🌈 Nomura Research Institute

Email: n-sakimura@nri.co.jp URI: https://nat.sakimura.org/

Torsten Lodderstedt πŸ’œ YES.com AG

Email: torsten@lodderstedt.net

πŸ“¦ where this page comes from