Singpass Developer Docs
Legacy Myinfo v3/v4
Legacy Myinfo v3/v4
  • Legacy Myinfo v3/v4
  • Data Catalog
  • Key Principles
  • Technical Specifications
    • Myinfo v4
      • Difference between v3 and v4
      • Technical Guidelines
      • Technical Concepts
        • OAuth 2.1 Concepts
        • Proof of Key Code Exchange (PKCE)
        • JSON Web Token (JWT)
        • Client Assertions
        • JSON Web Key Store (JWKS)
        • Demonstration of Proof-of-Possession (DPoP)
      • API Specifications
      • Tutorials
        • Tutorial 1: Myinfo Person sample Data
        • Tutorial 2: End-to-end Integration with Myinfo v4 APIs
      • Resources
        • Myinfo Connectors
        • Error Codes
      • FAQ
    • Myinfo v3
      • Technical Guidelines
      • API Specifications
      • Latest X.509 Public Key Certificate
      • Tutorials
        • Tutorial 1: Basic Person API
        • Tutorial 2: Using OAuth2
        • Tutorial 3: Implementing PKI Digital Signature
      • Resources
        • Myinfo Connectors
        • Error Codes
      • FAQ
Powered by GitBook
On this page
  • Introduction
  • Generate Ephemeral keys
  • Generate DPoP Proof JWT

Was this helpful?

  1. Technical Specifications
  2. Myinfo v4
  3. Technical Concepts

Demonstration of Proof-of-Possession (DPoP)

PreviousJSON Web Key Store (JWKS)NextAPI Specifications

Last updated 1 month ago

Was this helpful?

Introduction

The Demonstration of Proof-of-Possession(DPoP) is a mechanism to tie an access_token to the client who gets it. The client will have to provide a proof of possession before the access_token can be used.

1. Client application generates an ephemeral (One time) keypair on each API request.

Please ensure a new ephemeral keypair is generated on each request to ensure DPoP Proof is always unique per transaction.

  • Ephemeral public key (JWK)

  • Ephemeral private key

With the following information:

  • alg: ES256

  • use: sig


2. Client application generates the DPoP Proof JWT(for /token API)

  • Embed the DPoP Proof JWTephemeral public key (JWK) into the header of the DPoP Proof JWT DPoP Proof JWT

  • Sign the DPoP Proof JWT using the DPoP Proof JWT ephemeral private key


3. Client application appends ephemeral public key (JWK) thumbprint into the payload of the client_assertion (cnf.jkt)

This step ensures that the identical client application generates the DPoP Proof (for Token API) and client_assertion.


4. Client application calls the /token API to request for an access_token with the following information:

  • DPoP Proof (for Token API)

  • client_assertion

  • authcode


5. AuthZ server validates and embeds the ephemeral public key (JWK) thumbprint of the DPoP Proof JWT (for /token API) into the access_token (cnf.jkt).

This step ensures that the access_token can only be used by the client application.


6. AuthZ server returns access_token to the client application with token_type as DPoP.


7. Client application generates another DPoP Proof JWT (for /person API):

This step ensures that the access_token can only be used by the client application.


8. Client application calls the /person API to request for the person data (Resource) with the following information:

  • DPoP Proof (for Person API)

  • access_token


9. Resource server validates the payload (ath) and ensures the ephemeral public key (JWK) thumbprint of the DPoP Proof JWT (for /person API) matches the access_token hash and cnf.jkt.

This step ensures that the access_token belongs to the client application that generates the DPoP Proof (Person API).


10. Resource server response with the person data.


Generate Ephemeral keys

Sample Code:

async function generateEphemeralKeys() {
  let options = {
    namedCurve: "P-256",
    publicKeyEncoding: {
      type: "spki",
      format: "pem",
    },
    privateKeyEncoding: {
      type: "sec1",
      format: "pem",
    },
  };
     
  let ephemeralKeyPair = crypto.generateKeyPairSync("ec", options);
 
  return ephemeralKeyPair;
}


Generate DPoP Proof JWT

PARAMETER
DESCRIPTION

typ

Type - Type (Header), value "dpop+jwt"

alg

jwk

JSON Web Key (Header) - public key chosen by the client. MUST NOT contain the private key.

jti

JWT ID (Payload) - unique identifier

htu

HTTP URL (Payload) - HTTP URI used for the request, without query (?) and fragment parts (#)

htm

HTTP Method (Payload) - HTTP method for the request to which the JWT is attached

iat

Issued At (Payload) - current timestamp

exp

Expiry (Payload) - expiry timestamp

ath

Access token hash (Payload) - The base64url encoded SHA-256 hash of the ASCII encoding of the associated access token's value (Required only for /person call after DPoP-bound access token is issued)

DPoP Proof Sample

Token API

{
  "typ": "dpop+jwt",
  "jwk": {
    "kty": "EC",
    "kid": "M2OJVTdfiUw9C1HQkO1vTijLXGOypcFuT5wd4-5WdzE",
    "crv": "P-256",
    "x": "n92tLt2RNoBgB3jbOkq61wl_33USS08tS-GR7de0Mf0",
    "y": "3riG1jcEY8BLZdR8fseAkXA-RoIov_x9C_bG3JXVUq4",
    "use": "sig",
    "alg": "ES256"
  },
  "alg": "ES256"
}.
{
  "htu": "https://sit.api.myinfo.gov.sg/com/v4/token",
  "htm": "POST",
  "jti": "bWDY6aTCnAGKWtCUjYLVIVmoEmB4XarepsBD85GC",
  "iat": 1662714998,
  "exp": 1662715118
}.
[signature]

Person API

{
  "typ": "dpop+jwt",
  "jwk": {
    "kty": "EC",
    "kid": "M2OJVTdfiUw9C1HQkO1vTijLXGOypcFuT5wd4-5WdzE",
    "crv": "P-256",
    "x": "n92tLt2RNoBgB3jbOkq61wl_33USS08tS-GR7de0Mf0",
    "y": "3riG1jcEY8BLZdR8fseAkXA-RoIov_x9C_bG3JXVUq4",
    "use": "sig",
    "alg": "ES256"
  },
  "alg": "ES256"
}.
{
  "htu": "https://sit.api.myinfo.gov.sg/com/v4/person/915267f0-5939-0230-78e7-b8cdbaab8518",
  "htm": "GET",
  "jti": "XFj7vBWCOlpI2Qyuxi4R7l2BTwFWDwZu1lWDT47p",
  "iat": 1662715005,
  "exp": 1662715125,
  "ath": "Eh9xM2CDQyrI3OMLJJJDhtmQeakdYYTDckNtklRQOVw"
}.
[signature]

Sample Code:

// ath: Base64urlencoded Hash of the access_token
 
async function generateDpop(url, ath, method, ephemeralKeyPair) {
  let now = Math.floor(Date.now() / 1000);
  let payload = {
    htu: url,
    htm: method,
    jti: generateRandomString(40),
    iat: now,
    exp: now + 120,
  };
     
  if (ath) {
    payload.ath = ath;
  }
 
  let privateKey = await jose.JWK.asKey(ephemeralKeyPair.privateKey, "pem");
  let jwk = (await jose.JWK.asKey(ephemeralKeyPair.publicKey, "pem")).toJSON(true);
  jwk.use = "sig";
  jwk.alg = "ES256";
  let DPoP = await jose.JWS.createSign(
    { format: "compact", fields: { typ: "dpop+jwt", jwk: jwk } },
    { key: privateKey, reference: false }
  )
    .update(JSON.stringify(payload))
    .final();
  return DPoP;
}

Refer to Generate

The claims (Refer to ) expected in the DPoP Proof JWT are:

Algorithm (Header) - digital signature algorithm identifier as per RFC7518 (Refer to ). MUST NOT be none or an identifier for a symmetric algorithm (MAC), value "ES256"

Client Assertion
https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop#name-dpop-proof-jwt-syntax
https://datatracker.ietf.org/doc/html/rfc7518
DPoP Mechanism