DPoP for JavaScript Runtimes
dpop is a JavaScript library providing a robust implementation of the OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP), as specified by RFC9449. It facilitates the secure generation of DPoP key pairs and proofs, which are crucial for enhancing API security by binding access tokens to the client's cryptographic key. The library is designed for broad compatibility, supporting various JavaScript runtimes including modern browsers, Node.js (v20.x and higher), Bun, Deno, Cloudflare Workers, Electron, and Vercel's Edge Runtime. The current stable version is 2.1.1, with an active development cycle that includes regular feature additions, bug fixes, and adherence to evolving standards. Its primary differentiators are its comprehensive runtime support and strict compliance with RFC9449, ensuring interoperable and reliable DPoP implementations across different environments.
Common errors
-
TypeError: DPoP.generateKeyPair is not a function
cause Attempting to call functions as properties of a default import, but `dpop` uses named exports since v2.0.0.fixChange your import from `import DPoP from 'dpop';` to `import * as DPoP from 'dpop';` or `import { generateKeyPair } from 'dpop';`. -
ReferenceError: generateKeyPair is not defined
cause Trying to use a named export function without importing it explicitly or via a namespace import.fixEnsure `generateKeyPair` is properly imported: `import { generateKeyPair } from 'dpop';` or accessed via a namespace: `import * as DPoP from 'dpop'; DPoP.generateKeyPair(...)`. -
Error: 'modulusLength' option is not supported for key generation.
cause Using the `modulusLength` option in `generateKeyPair` after it was removed in v2.0.0.fixRemove the `modulusLength` option from your `generateKeyPair` call. The library no longer supports it.
Warnings
- breaking Version 2.0.0 removed default exports in favor of named exports. All functions must now be imported via destructuring (e.g., `import { generateKeyPair } from 'dpop';`) or by importing all as a namespace (e.g., `import * as DPoP from 'dpop';`).
- breaking The `modulusLength` option for key generation has been removed in v2.0.0. This option was specific to RSA keys and its removal streamlines the API, focusing on more modern and recommended algorithms.
- breaking Support for the deprecated EdDSA algorithm was removed in v2.0.0. The library now supports the fully-specified Ed25519 JWS Algorithm.
- gotcha Node.js v20.x or higher is required as a baseline runtime environment for `dpop` v2.x. This is due to the library's reliance on modern Web API globals and standard built-in objects.
Install
-
npm install dpop -
yarn add dpop -
pnpm add dpop
Imports
- DPoP
const DPoP = require('dpop');import * as DPoP from 'dpop';
- generateKeyPair, generateProof
import DPoP, { generateKeyPair, generateProof } from 'dpop';import { generateKeyPair, generateProof } from 'dpop'; - calculateThumbprint
import * as DPoP from 'dpop'; const dpop_jkt = await DPoP.dpop_jkt(publicKey);
import { calculateThumbprint } from 'dpop';
Quickstart
import * as DPoP from 'dpop';
async function runDPoPExample() {
console.log('Starting DPoP example...');
// 1. Generate a DPoP Key Pair (e.g., ES256 algorithm)
// The 'extractable: false' option is good practice for non-exportable keys
const keyPair = await DPoP.generateKeyPair('ES256', { extractable: false });
console.log('DPoP Key Pair generated using ES256.');
// 2. Calculate the dpop_jkt (Key Thumbprint) for authorization code binding
// This identifies the public key associated with the DPoP proof
const dpop_jkt = await DPoP.calculateThumbprint(keyPair.publicKey);
console.log('Calculated dpop_jkt:', dpop_jkt);
// 3. Generate a DPoP proof for an Authorization Server (AS) token request
// This proof is sent with the token request to the AS
const asTokenUrl = 'https://as.example.com/token';
const asProof = await DPoP.generateProof(keyPair, asTokenUrl, 'POST');
console.log('DPoP Proof for AS Token Request:', asProof.slice(0, 100), '...'); // Truncate for display
// 4. Simulate an Access Token from the AS and generate a DPoP proof for a Resource Server (RS) API request
// The access token is bound to the DPoP key when making requests to the RS
const rsApiUrl = 'https://rs.example.com/api/data';
const accessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJhY2Nlc3NfdG9rZW4iOnRydWV9.SflKxwRJSMeKKF2F8DGD_dPOk_W5dZg_qkX-zHjN_W0'; // Example dummy token
const nonceFromRS = undefined; // In a real scenario, this might come from a 'DPoP-Nonce' header
const rsProof = await DPoP.generateProof(
keyPair,
rsApiUrl,
'GET',
nonceFromRS,
accessToken
);
console.log('DPoP Proof for RS API Request:', rsProof.slice(0, 100), '...'); // Truncate for display
}
runDPoPExample().catch(console.error);