export async function generateES256DPoPProof(params: { keyPair: CryptoKeyPair; url: string; accessToken: string | undefined; httpMethod: string | undefined; nonce: string | undefined; getServerDateNow: () => number; }): Promise { const { keyPair, url, accessToken, httpMethod, nonce, getServerDateNow } = params; const payload: Record = { jti: window.crypto.randomUUID(), htm: httpMethod ?? "GET", htu: (() => { const urlObj = new URL(url); return `${urlObj.origin}${urlObj.pathname}`; })(), iat: Math.floor(getServerDateNow() / 1000) }; if (accessToken !== undefined) { const hashedToken = await hash("SHA-256", accessToken); payload.ath = encodeBase64Url(hashedToken); } if (nonce !== undefined) { payload.nonce = nonce; } const publicJwk = await window.crypto.subtle.exportKey("jwk", keyPair.publicKey); const header = { alg: "ES256", typ: "dpop+jwt", jwk: { crv: publicJwk.crv, kty: publicJwk.kty, x: publicJwk.x, y: publicJwk.y } }; return await generateSignedJwt(header, payload, keyPair.privateKey); } async function generateSignedJwt( header: object, payload: object, privateKey: CryptoKey ): Promise { const encodedHeader = encodeBase64Url(new TextEncoder().encode(JSON.stringify(header))); const encodedPayload = encodeBase64Url(new TextEncoder().encode(JSON.stringify(payload))); const encodedToken = `${encodedHeader}.${encodedPayload}`; const signature = await window.crypto.subtle.sign( { name: "ECDSA", hash: { name: "SHA-256" } }, privateKey, new TextEncoder().encode(encodedToken) ); const encodedSignature = encodeBase64Url(new Uint8Array(signature)); return `${encodedToken}.${encodedSignature}`; } const toBase64 = (val: ArrayBuffer): string => btoa([...new Uint8Array(val)].map(chr => String.fromCharCode(chr)).join("")); const encodeBase64Url = (input: Uint8Array) => toBase64(input).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); const hash = async (alg: string, message: string): Promise => { const msgUint8 = new TextEncoder().encode(message); const hashBuffer = await window.crypto.subtle.digest(alg, msgUint8); return new Uint8Array(hashBuffer); };