# Demonstrating Proof of Possession (DPoP)

## Overview

DPoP ([RFC 9449](https://www.rfc-editor.org/rfc/rfc9449.html)) protects your integration from:

1. Authorization code inteception
2. Access token misuse or leakage&#x20;

It works by binding the *authorization code* and *access token* to a private key that only your system controls. allowing only you to use them for [Token Exchange](https://docs.developer.singpass.gov.sg/docs/technical-specifications/integration-guide/3.-token-exchange) and [Requesting For Userinfo](https://docs.developer.singpass.gov.sg/docs/technical-specifications/integration-guide/5.-requesting-for-userinfo) respectively.

#### **When is DPoP Required?**

You must include a **DPoP proof JWT** in the DPoP request header for the following API calls:

* [Pushed Authorization Request](https://docs.developer.singpass.gov.sg/docs/integration-guide/1.-authorization-request#id-1.-sending-the-pushed-authorization-request)
* [Token Exchange](https://docs.developer.singpass.gov.sg/docs/technical-specifications/integration-guide/3.-token-exchange)
* [Requesting for Userinfo](https://docs.developer.singpass.gov.sg/docs/technical-specifications/integration-guide/5.-requesting-for-userinfo)

If DPoP is missing or invalid the request will be rejected

#### **Key Requirements**

{% hint style="warning" %}
You **must** use the same DPoP key pair across all three requests for a single authentication. You should generate a different ephemeral key for each authentication.
{% endhint %}

* Generate a **new** ephemeral DPoP key pair for each authentication session
* Use the **same key pair across all three requests** (PAR → Token → Userinfo) for that session
* Do not reuse the key pair across different login sessions

#### What Is the DPoP Proof JWT?

The DPoP Proof JWT is a signed JWT (JWS) that proves you own the private key bound to the token — without exposing the private key.

To implement:

* We recommend using a standard JWT library&#x20;
* You may refer to [this list](https://www.jwt.io/libraries?support=sign\&algorithm=es256) to look for a suitable library for your programming language. To learn more about JWTs and their structure, you may read [this article](https://www.jwt.io/introduction).

### DPoP Proof JWT Structure and Requirements

#### JWT Header

The JWT header should contain the following parameters:

<table><thead><tr><th width="122.17578125">Parameter</th><th>Description</th><th>Data Type</th></tr></thead><tbody><tr><td><code>alg</code></td><td>The signature algorithm that you are using to sign this JWT</td><td><p>One of the following strings:</p><ul><li><code>ES256</code></li><li><code>ES384</code></li><li><code>ES512</code></li></ul></td></tr><tr><td><code>typ</code></td><td>The type of this JWT</td><td>Must be the string <code>dpop+jwt</code></td></tr><tr><td><code>jwk</code></td><td>The public key, in JSON Web Key format, that corresponds to the private key used to sign the JWT</td><td>Object in JSON Web Key format</td></tr></tbody></table>

<details>

<summary>Sample JWT header</summary>

```json
{
  "alg": "ES256",
  "typ": "dpop+jwt",
  "jwk": {
    "crv": "P-256",
    "kty": "EC",
    "x": "T59MDXABUkdP7DAvAnuZ3l-iLUylObl1v6DBqAp8LU0",
    "y": "D7NSWZW9_r2wZC10yobEiOgjoqDppoMdYSG3YV16QjU"
  }
}
```

</details>

#### JWT Payload

The JWT payload should contain the following claims:

<table><thead><tr><th width="88.7265625">Claim</th><th width="440.61328125">Description</th><th>Data type</th></tr></thead><tbody><tr><td><code>htm</code></td><td>The HTTP method of the request that you are making</td><td>Either the string <code>POST</code> or <code>GET</code></td></tr><tr><td><code>htu</code></td><td>The URL that you are making the request to.</td><td>URL</td></tr><tr><td><code>iat</code></td><td>The unix timestamp, in seconds, at which you generated this JWT.</td><td>This value must be a <a href="https://datatracker.ietf.org/doc/html/rfc7519#section-2"><code>NumericDate</code></a> type, representing seconds.</td></tr><tr><td><code>exp</code></td><td>The unix timestamp, in seconds, on or after which this JWT <strong>must not</strong> be accepted by us for processing.<br><br>Note also that this must be less than or equal to 2 minutes after <code>iat</code>.</td><td>This value must be a <a href="https://datatracker.ietf.org/doc/html/rfc7519#section-2"><code>NumericDate</code></a> type, representing seconds.</td></tr><tr><td><code>jti</code></td><td>A unique identifier for this token. This identifier must only be used once. You should generate a new <code>jti</code> value for every request</td><td>String</td></tr><tr><td><code>ath</code></td><td>Required only for the userinfo request. This should be the base64url-encoded SHA-256 hash of the access token.</td><td>A base64url-encoded string.</td></tr></tbody></table>

<details>

<summary>Sample JWT payload</summary>

```json
{
  "jti": "50ac59b2-a1e7-4bac-909b-68b692207b6c",
  "htm": "POST",
  "htu": "https://stg-id.singpass.gov.sg/fapi/par",
  "iat": 1771819829,
  "exp": 1771819949
}
```

</details>

### Signing and using the JWT

The JWT should be signed using your private DPoP key to form a JSON Web Signature (JWS) in compact serialisation format. This is a format where the three parts of the JWS (the header, the payload, and the signature), are base64url encoded and concatenated together, using a dot (`.`) as the separator.

The private key used to sign the JWT must correspond to the public key indicated in the `jwk` parameter in the JWT header.

This JWS should then be included in the `DPoP` request header when you are making requests that require DPoP.

```
DPoP: <your_signed_jwt>
```

### Validate Your DPoP Implementation&#x20;

&#x20;you can validate your DPoP proof JWT using this online validator tool:

[DPoP & Client Assertion Validator](https://client-assertion-dpop-validator.vercel.app/)

This tool helps you:

* Verify JWT structure and compact format
* Check required header parameters (alg, typ, jwk)
* Validate required payload claims
* Confirm signature validity
* Detect common implementation errors
