Demonstrating Proof of Possession (DPoP)

Overview

DPoP is a security feature specified in RFC 9449. It protects you from authorization code injection attacks and access token misuse, by binding your authorization code and access token to a private key that only you know, allowing only you to use them for Token Exchange and Requesting For Userinfo respectively.

With DPoP, you are required to provide a cryptographic proof, called the DPoP proof JWT, when you are requesting for, or using the authorization code or the access token, in order to prove ownership of the private key that the authorization code and access token is bound to.

DPoP works in tandem with client assertions and PKCE in order to prevent authorization code interception attacks, which is a type of attack where attackers gain access to the authorization code (e.g. via malware installed on the user's device) and use it to obtain a user's access token. It also protects against access token leaks, as attackers will not be able to use the access token without the ability to generate a DPoP proof JWT.

Specifically, the DPoP proof JWT must be sent, as the DPoP request header, for the following requests:

Generating the DPoP Proof JWT

The DPoP Proof JWT is a signed JSON Web Token (JWT), also known as a JSON Web Signature (JWS), which acts as a cryptographic proof of your ownership of the private key, without revealing the private key itself.

To reduce complexity, we recommend that you use a JWT library to perform the JWT encoding and signing on your behalf, instead of implementing this on your own. You may refer to this list to look for a suitable library for your programming language. To learn more about JWTs and their structure, you may read this article.

The JWT must have the structure outlined below.

JWT Structure

JWT Header

The JWT header should contain the following parameters:

Parameter
Description
Data Type

alg

The signature algorithm that you are using to sign this JWT

One of the following strings:

  • ES256

  • ES384

  • ES512

typ

The type of this JWT

Must be the string dpop+jwt

jwk

The public key, in JSON Web Key format, that corresponds to the private key used to sign the JWT

Object in JSON Web Key format

Sample JWT header
{
  "typ" : "JWT",
  "alg" : "ES256",
  "jwk" : {
    "kty": "EC",
    "alg": "ES256",
    "use": "sig",
    "kid": "ydGFKJbIoqzSJyMpUiprLpaQz7RxV8C_HLiCW-l0q1k",
    "crv": "P-256",
    "x": "vx_9JwRUaXaj8nE21-mgtjrx2JPkOM_iawIIbIV2huc",
    "y": "ZvqP1ErOvOmMvaaWVA2WzB6eroYsz4I1PML1AiEIvsQ"
  },
}

JWT Payload

The JWT payload should contain the following claims:

Claim
Description
Data type

htm

The HTTP method of the request that you are making

Either the string POST or GET

htu

The URL that you are making the request to.

URL

iat

The unix timestamp, in seconds, at which you generated this JWT.

Number

exp

The unix timestamp, in seconds, on or after which this JWT must not be accepted by us for processing. Note also that this must be less than or equal to 2 minutes after iat.

Number

jti

A unique identifier for this token. This identifier must only be used once. You should generate a new jti value for every request

String

ath

Required only for the userinfo request. This should be the base64url-encoded SHA-256 hash of the access token.

A base64url-encoded string.

Sample JWT payload
{
  "htm": "GET",
  "htu": "https://id.singpass.gov.sg/fapi/userinfo",
  "iat": "1756785377",
  "exp": "1756785493",
  "jti": "52291922-167b-4367-823f-dbf6fc8ca1f3",
  "ath": "3rEbqHhURQcbfV3zM9sl1rsBzAcjT9TKqX3akHLZ9Nc"
}

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.

Last updated

Was this helpful?