this repo has no description
1/** 2 * DPoP proof generation for e2e tests 3 */ 4 5import { base64UrlEncode, computeJwkThumbprint } from '../../src/pds.js'; 6 7/** 8 * Generate an ES256 key pair for DPoP 9 * @returns {Promise<{privateKey: CryptoKey, publicKey: CryptoKey, jwk: object}>} 10 */ 11export async function generateKeyPair() { 12 const keyPair = await crypto.subtle.generateKey( 13 { name: 'ECDSA', namedCurve: 'P-256' }, 14 true, 15 ['sign', 'verify'], 16 ); 17 18 const jwk = await crypto.subtle.exportKey('jwk', keyPair.publicKey); 19 const publicJwk = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y }; 20 21 return { 22 privateKey: keyPair.privateKey, 23 publicKey: keyPair.publicKey, 24 jwk: publicJwk, 25 }; 26} 27 28/** 29 * Create a DPoP proof JWT 30 * @param {object} params 31 * @param {CryptoKey} params.privateKey 32 * @param {object} params.jwk 33 * @param {string} params.method 34 * @param {string} params.url 35 * @param {string} [params.accessToken] 36 * @returns {Promise<string>} 37 */ 38export async function createDpopProof({ 39 privateKey, 40 jwk, 41 method, 42 url, 43 accessToken, 44}) { 45 const header = { typ: 'dpop+jwt', alg: 'ES256', jwk }; 46 const payload = { 47 jti: base64UrlEncode(crypto.getRandomValues(new Uint8Array(16))), 48 htm: method, 49 htu: url, 50 iat: Math.floor(Date.now() / 1000), 51 }; 52 53 if (accessToken) { 54 const hash = await crypto.subtle.digest( 55 'SHA-256', 56 new TextEncoder().encode(accessToken), 57 ); 58 payload.ath = base64UrlEncode(new Uint8Array(hash)); 59 } 60 61 const headerB64 = base64UrlEncode( 62 new TextEncoder().encode(JSON.stringify(header)), 63 ); 64 const payloadB64 = base64UrlEncode( 65 new TextEncoder().encode(JSON.stringify(payload)), 66 ); 67 const signingInput = `${headerB64}.${payloadB64}`; 68 69 const signature = await crypto.subtle.sign( 70 { name: 'ECDSA', hash: 'SHA-256' }, 71 privateKey, 72 new TextEncoder().encode(signingInput), 73 ); 74 75 return `${signingInput}.${base64UrlEncode(new Uint8Array(signature))}`; 76} 77 78/** 79 * DPoP client helper 80 */ 81export class DpopClient { 82 #privateKey; 83 #jwk; 84 #jkt = null; 85 86 constructor(privateKey, jwk) { 87 this.#privateKey = privateKey; 88 this.#jwk = jwk; 89 } 90 91 static async create() { 92 const { privateKey, jwk } = await generateKeyPair(); 93 return new DpopClient(privateKey, jwk); 94 } 95 96 async getJkt() { 97 if (!this.#jkt) this.#jkt = await computeJwkThumbprint(this.#jwk); 98 return this.#jkt; 99 } 100 101 getJwk() { 102 return this.#jwk; 103 } 104 105 async createProof(method, url, accessToken) { 106 return createDpopProof({ 107 privateKey: this.#privateKey, 108 jwk: this.#jwk, 109 method, 110 url, 111 accessToken, 112 }); 113 } 114}