Engineering
July 15, 2024
8 min read

Demonstrating Proof-of-Possession (DPoP): Preventing Illegal Access of APIs

Veena Rajarathna
Staff Product Manager, Kong
Xumin Zhou
Software Engineer 2, Kong

Background

In a previous blog post, we discussed the prevalence of bearer tokens (or access tokens) to restrict access to protected resources, the challenges the sheer nature of bearer tokens present, and available mitigations. To recap, presenting a bearer token is proof enough of an authorization grant to avail the service and access resources protected by the token. This poses many security risks such as using stolen or leaked tokens to gain unauthorized access. The solution to overcome this weakness is to use proof-of-possession(PoP) tokens or sender-constrained tokens. These are still access tokens but are limited to only being used by the client/entity that originally obtained it. This is achieved in two-fold: 

  1. Constrain the tokens to the client/entity and limit the usage of the token only to that client/entity.
  2. Seek proof of possession of the token at the point of enforcement.

With this approach, the primary security vulnerability of standard bearer tokens is remediated. 

Constraining bearer tokens

Bearer tokens can be constrained in such a way that only the client/entity that received it can actually be used. When such tokens are presented as an authorization grant,  the receiving service/API can evaluate the legitimacy of the token by asking for  proof of possession. Access is granted only if the presenter of the token also produces the proof. Since only the client/entity to whom the token was originally issued can present the proof, requests with stolen or leaded tokens are rejected. This approach renders the stolen tokens unusable. 

There are two methods to constrain bearer tokens and demand proof of possession:

  1. mTLS based proof of possession : OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens.

  2. Asymmetric cryptography and JWT based proof of possession : OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer. 

In this blog post, we’ll briefly discuss the two methods. 

mTLS based proof of possession

Tokens constrained with certificates are called certificate-bound access tokens. With this approach, the access tokens are bound to the client certificate used in establishing a mutual TLS connection to the authorization server. Issued tokens contain a thumbprint of the certificate. When a client makes a request to a protected resource, the client is expected to present the same mTLS certificate as proof along with the token obtained from the authorization server. The receiving party can grant access only after validating that the thumbprint of the certificate in the token matches the presented proof (mTLS certificate). Requests with missing certificates or mismatched certificates and tokens are rejected. Stolen or leaked tokens cannot be used as an additional proof (in the form of certificate) needs to be presented along with the token.

DPOP: Asymmetric cryptography and JWT-based proof of possession

DPoP is a OAuth security extension to constrain bearer tokens to clients. It is designed to work at the application layer. Unlike mTLS, it does not rely on PKI infrastructure. This approach leverages asymmetric cryptography and JSON Web Tokens. With DPoP, bearer tokens are constrained to the client using the client’s private key.

Although DPoP is relatively new compared to the mTLS-based approach, which is widely adopted, DPoP is better suited for a public client such as a single-page application running in a browser. Since it does not rely on PKI, it is less cumbersome to deploy and much easier to implement.

Comparison of the two methods


With both methods, access tokens are sender-constrained, requiring clients to present proof of possession of tokens. The choice between the two methods depends on factors such as existing infrastructure, security requirements, and tooling ecosystem. 


In Kong Gateway Enterprise 3.5, we introduced constraining tokens using mTLS. Kong authorizes the request only after validating that the thumbprint of the client certificate in the token matches the client certificate used to establish the mTLS connection. With Kong Gateway Enterprise 3.7, we offer support for the second method DPoP.

DPoP at the application layer

RFC 9449 defines another mechanism to sender-constrain the access tokens. This approach leverages asymmetric keys and JWTS. The authorization server constrains the tokens by binding the tokens to the client’s public key. Clients have to prove the possession of a private key when using the tokens. Recipients of such tokens are then able to verify the binding of the token to the key pair that the client has demonstrated that it holds, thereby providing some assurance that the client presenting the token also possesses the private key. In other words, the legitimate presenter of the token is constrained to be the sender that holds and proves possession of the private part of the key pair.

Starting with the 3.7 release, Kong offers support for application-level PoP mechanisms.

 Let’s examine the flow in depth:

  1. Step 1: The client generates a key pair consisting of public and private keys.
  2. Step 2: The client then prepares a JWT. Includes the public key in the JWT header. The JWT payload consists of claims such as htm and htu. Note htu is the requested resource (token endpoint url) and htm is the method. The client signs the JWT with the private key.
  3. Step 3: The client then makes a request to the authorization server’s token endpoint. It includes the client credentials, the grant type, and the JWT as a DPoP header.
  4. Step 4: The authorization server authenticates the client, generates an access token and binds the public key to the token. The server responds back with the access token
  5. Step 5: Clients must generate a ‘proof JWT’ for each request. (Note,  validity of the proof JWT is configurable. Its possible to retry a request within the time period without having to generate new proof JWT). The client then prepares another JWT. This is the DPoP proof JWT. It includes the public key in the header. The hash of access token is included as ath claim in the payload along with htm and htu claims. Note the htu is the requested resource and htm is the method. The client then signs the JWT with the private key. 
  6. Step 6: The client then makes a request for the protected resource by including the access token in the Authorization: DPoP <> header and the proof JWT as DPoP header.
  7. Step 7: Kong extracts the DPoP proof JWT and the access token from the request. The signature is verified using the public key included in the Dpop proof. This proves that the client has the private key corresponding to the public key. Next, it checks the public key in the proof JWT matches the public key bound to the access token. This proves that the client is the valid owner of the token.

Requests with just a token or requests that have mismatched proofs or missing proofs altogether are rejected.

Let’s see a step-by-step on how to achieve this in Kong.

This example uses Cloundentity as IDP and a client credentials grant type and uses an API service available on the web(ergast.com). Kong is placed in front to authorize the requests and protect the API service. With DPoP, the client must be capable of generating JWTs and signing them with the private key. This example highlights two separate tools. One tool is used to communicate between the client and IDP and another between the client and Kong. You have to ensure to use the same set of keys between the two tools.

Pre-Requisites

  1. Kong Gateway Enterprise 3.7
  2. IDP - OIDC and OAuth 2.0 compliant provider supporting DPoP such as Okta, Keycloak, Cloudentity etc
  3. OAuth2c command line tool to interact with IDP
  4. Python Program (mentioned here) to generate proof JWTs 

(Note it is not necessary to use separate tools. I include it here just for convenience and to showcase the options. #4 can be used to generate proofs for both legs of communication -  client to IDP and client to Kong. Alternatively, JWT.io can be used along with a combination of other command line tools such as curl, openssl. ssh-keygen etc )

IDP Configuration

  1. Create a Client with the following with following configuration

    a. Grant Type: Client_Credentials

    b. Response Types : Token

    c. Client Authentication/Token endpoint authentication : Client_Secret Basic

    d. Sender Constrained Tokens : DPoP

Kong Configuration

1. Create a Service

a. Name : F1Results

b. Host : ergast.com

c. Path : /api/f1

2. Create a Route for service F!Results

a. Name : drivers

b. Path : /drivers

c. Strip Path : false

3. Add the OIDC plugin with the following settings

a. Issuer : <IDP’s issuer URL>

b. Proof Of Possession DPoP : strict

c. Dpop Proof Lifetime: default is 5 min


In this flow, the client must first obtain the access token from IDP. Since Dpop is enabled, the IDP expects a JWT. We will use OAuth2c to generate proof and interact with IDP. 

Steps

  1. Generate a key pair in the JWKS format
  2. Using OAuth2c, make a request to IDP to obtain a token. Use the keys generated above 


The tool generates a JWT and signs it with the private key as explained in step 2 of the in-depth flow above

3. If all good, IDP returns an access token

4. Now the client must generate a Dpop proof JWT for each request/route. The client must use the same pair of keys and include the access token in this DPoP proof JWT. We will use the python tool to generate the Dpop proof JWT. Pass in the route, method and the access token obtained above to the python program. Use the same set of keys. (The tool expects the keys to be made available in a directory). The tool outputs a JWT.

5. Next, make a request to Kong using the JWT from above and the access token obtained earlier

6. Kong extracts the DPoP proof JWT and the access token from the request. Kong validates the signature using the public key included in the proof. This proves that the client has the private key corresponding to the public key. Next, it checks the public key in the proof JWT matches the public key bound to the access token. This proves that the client is the valid owner of the token


The Proof JWT is valid for 5 min on the same route/method combination. Other routes will require new proof jwt. Requests with just a token or requests with missing or mismatched proofs are rejected.

Summary

The primary aim of DPoP is to prevent unauthorized or illegitimate parties from using leaked or stolen access tokens. This is achieved by binding a token to a public key upon issuance and requiring the client to prove the possession of the corresponding private key when using the token. This mechanism constrains the token to be used only by the legitimate owner of the token and provides added assurance that the sender of the token is legitimately authorized to use it.

Sender-constrained tokens are suitable implementations for environments with high security requirements such as financial-grade APIs or Open Banking. The DPoP mechanism offers a new way to implement sender-constrained tokens and is designed to work at the application layer.

With support for DPoP in Kong Gateway Enterprise 3.7, Kong enforces proof-of-possession checks for both methods of sender-constrained tokens.