On the first episode of Identity, Unlocked, host Vittorio Bertocci, Principal Architect at Auth0, is joined by Brian Campbell. Brian joins the show to discuss the sender constraint and associated specifications.
The mechanism described by OAuth2 for using the access token to access resources, as defined in the bearer token usage specification (RFC6750), simply entails attaching a token to the request to the API. The approach is extremely simple to implement, but token leaks can have disastrous consequences: nothing stops an attacker from using a stolen bearer token to access a resource successfully.
"Sender constraint" indicates a series of techniques that bind tokens to a particular sender, for example, by forcing the token to travel on a specific channel or requiring proof of knowledge of a given key. The goal is to guarantee that only the legitimate sender can successfully use a token to access resources, thus making it impossible for an attacker to access a resource using leaked tokens.
There are two different specifications in the OAuth 2 family offering viable sender constraint capabilities today: OAuth 2.0 Mutual TLS Client Authentication and Certificate-Bound Access Tokens (MTLS, RFC8705) and OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP).
MTLS is robust and stable, but not easy to implement in various important scenarios. For a while, the identity community worked on an alternative, a set of specifications under the general token binding moniker (main one: RFC8471); however, support from key industry players disappeared or never materialized, making token binding non-viable.
DPoP quickly emerged as an easy to implement an alternative that could fill that gap, and although it is still in draft state, it is already very popular and making quick progress.
Brian touches on all those specs during the episode, walking us through the trajectory that led us to today's situation and taking the time to dig deeper into the trade-offs, strengths, and attention points of the various approaches. The episode concludes with important practical considerations to keep in mind when planning an implementation strategy for the sender constraint in your solutions today.
Read on for more in-depth information on this episode and make sure to subscribe and join us for the next episode, where Aaron Parecki (Senior Security Architect) talks about what's new with OAuth2.1.
In the following, we highlight key passages of the interview. We added some background information here and there to make it easier to follow the conversation if you aren't already familiar with the topic.
[4:45] - What is sender constraint and what problem is it trying to solve?
The figure below shows a typical OAuth 2 flow for calling an API- a good example to understand how bearer tokens are meant to work.
In the first leg of the diagram, a client requests an access token - this might be the redemption of an authorization code, of a refresh token, anything that leads to an access token being issued to the client as shown in step 2.
In this particular example, we are issuing tokens that are digitally signed by the
authorization server via its private key (other token encodings are possible).
In step 3, the client calls the API by simply attaching the access token to the request (typically by placing it in the authorization HTTP header).
In step 4, the API validates the incoming access token (in this example, that includes validating the signature with the public key associated with the authorization server), and the call succeeds.
The following diagram shows what happens when a bearer token is leaked.
In step 1, an attacker steals an access token. For the purpose of highlighting the problem with bearer token leaks, it doesn't matter how the leak occurs (the token might be leaked from a cache, a referral header, an HTTPS tunnel termination, erroneously forwarded to a malicious address etc., etc.) as it doesn't change the consequences of the leak. Once an attacker has the token, all they need to do to access the API successfully is to attach it to an API call - the API will perform the same validation we had observed in the former diagram when the call was originating the legitimate sender. This is the key limitation of bearer tokens: everything hinges on not making them fall into the wrong hands, as when that happens, there's no easy recourse.
[11:35] - What are the current standards with sender constraint?
The main specifications extending OAuth 2 with sender constraint capabilities are:
Token binding is mentioned out of exhaustiveness, but it isn't really a viable option as of today. For the other ones, we give more details later on in the interview.
[11:51] - Brian explains mutual TLS profile for OAuth 2.
Here's a super quick primer to explain how MTLS works in OAuth 2. This leaves out lots of details, so if you are interested, you should consider taking a look at the spec itself (RFC8705). Please consider the diagram below.
The client application has access to an X509 certificate, which it uses to perform client TLS authentication (hence proving possession of the corresponding private key) when requesting a token from the authorization server. The authorization server issues a token containing (or referencing, if other token encoding methods are used) a claim cnf carrying the thumbprint of the X.509 certificate used to establish the channel. Important note: the access token is still meant to be signed by the authorization server, and the API is still meant to validate that signature, but we are omitting it in all diagrams from now on for simplicity.
In step 2, the client establishes a channel with the API/resource server, again using its X509 certificate to establish client TLS.
In step 3, the API validates the incoming token as usual, and additionally, it verifies that the certificate thumbprint corresponds to the certificate used to establish the TLS channel. If the two matches, the call is considered valid.
Consider now the case in which an attacker successfully steals a token. The attacker does not have access to the same certificate as the legitimate client; hence it cannot establish a client TLS channel with the API when attempting to use the stolen token - or it has to use a different certificate. However, the API will easily spot that the certificate thumbprint carried by the cnf claim in the incoming access token does NOT correspond to the certificate used in client TLS authentication by the attacker; hence the API will deny access.
[15:32] - Brian explains DPoP.
To help you follow the discussion, here’s a diagram exemplifying how DPoP works.
The idea is simple: the client uses some keys when requesting a token, those keys are referenced by the access token issued by the authorization server, and the client demonstrates knowledge of the same keys to the resource server when using that access token to call an API. Let's break that down in the steps shown in the diagram.
The client generates a private-public keys pair. It then creates a JWT, called DPoP proof, including in it a claim
jwkcarrying the public key from the pair, an
htu claim indicating the intended destination of the call (in this case, something like
https://server.example.com/token), a claim
htm the HTTP method to be used in the call, and (not shown in the diagram) a
jti claim (identifier) and an
iat claim (issuance instant). It then proceeds to digitally sign the JWT with the private key from the generated pair.
In step 1, the client attaches the DPOP proof in a DPOP HTTP header to a token request to the authorization server.
In step 2, the authorization server uses the public key in the
jwk claim from the DPoP proof to validate its signature, verifying that the sender does indeed have access to the corresponding private key. That done; it issues a new access token (once again, the token signature is omitted, but it is there, just like in the first diagram in this post) and embeds in it a
cnf claim containing the public key received from the client.
In step 3, the client calls the API. Using the DPoP, the client creates a new DPoP proof, including the same
jwk with the generated public key, a
htu with the URL of desired API endpoint (for example,
iat claims of the appropriate value. The client then attaches the DPoP proof and the access token to the request to the API.
In step 4, the client validates the incoming access token as it would do in a normal bearer case. In addition to that, it validates the DPoP signature with the
jwk it carries, validates that the content of the DPoP proof accurately describes the targeted API and thatthe thumbprint of the key embedded in the
cnf claim in the incoming access token corresponds to the
jwk in the DPoP proof. If all that checks out, the API has proof that the caller is indeed entitled to use that token and allows the request to succeed.
The failure case here is very similar to the one shown in the MLTS diagram. An attacker stealing a token would not have access to the private key the legitimate client-generated at request time; hence it would not be able to manufacture the appropriate DPoP proof that would convince the API to accept the stolen access token. Bam. Sender constraint!
[18:42] - If DPOP blossoms like some anticipate, is there any reason to continue mutual TLS?
[23:23] - Brian’s opinion on how DPoP can be rolled out.
Identity, Unlocked is the podcast that discusses identity specs and trends from a developer perspective. Identity, Unlocked is powered by Auth0. Vittorio Bertocci is Principal Architect at Auth0 and applies his vast knowledge of the identity industry to Auth0 in all aspects of the company, including internal and external education, product innovation, and customer integration.
Auth0 by Okta takes a modern approach to customer identity and enables organizations to provide secure access to any application, for any user. Auth0 is a highly customizable platform that is as simple as development teams want, and as flexible as they need. Safeguarding billions of login transactions each month, Auth0 delivers convenience, privacy, and security so customers can focus on innovation. For more information, visit https://auth0.com.