UNPKG

@story-health/fhirclient

Version:

JavaScript client for Fast Healthcare Interoperability Resources

101 lines (81 loc) 3.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.signCompactJws = exports.importJWK = exports.generatePKCEChallenge = exports.digestSha256 = exports.randomBytes = void 0; const js_base64_1 = require("js-base64"); const crypto = (() => { if (typeof globalThis === "object" && globalThis.crypto) { return globalThis.crypto; } throw new Error("window.crypto does not exist in this browser"); })(); const subtle = crypto.subtle; const ALGS = { ES384: { name: "ECDSA", namedCurve: "P-384" }, RS384: { name: "RSASSA-PKCS1-v1_5", modulusLength: 4096, publicExponent: new Uint8Array([1, 0, 1]), hash: { name: "SHA-384" } } }; function randomBytes(count) { return crypto.getRandomValues(new Uint8Array(count)); } exports.randomBytes = randomBytes; async function digestSha256(payload) { const prepared = new TextEncoder().encode(payload); const hash = await subtle.digest("SHA-256", prepared); return new Uint8Array(hash); } exports.digestSha256 = digestSha256; const generatePKCEChallenge = async (entropy = 96) => { const inputBytes = randomBytes(entropy); const codeVerifier = (0, js_base64_1.fromUint8Array)(inputBytes, true); const codeChallenge = (0, js_base64_1.fromUint8Array)(await digestSha256(codeVerifier), true); return { codeChallenge, codeVerifier }; }; exports.generatePKCEChallenge = generatePKCEChallenge; async function importJWK(jwk) { // alg is optional in JWK but we need it here! if (!jwk.alg) { throw new Error('The "alg" property of the JWK must be set to "ES384" or "RS384"'); } // Use of the "key_ops" member is OPTIONAL, unless the application requires its presence. // https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3 // // In our case the app will only import private keys so we can assume "sign" if (!Array.isArray(jwk.key_ops)) { jwk.key_ops = ["sign"]; } // In this case the JWK has a "key_ops" array and "sign" is not listed if (!jwk.key_ops.includes("sign")) { throw new Error('The "key_ops" property of the JWK does not contain "sign"'); } try { return await subtle.importKey("jwk", jwk, ALGS[jwk.alg], jwk.ext === true, jwk.key_ops // || ['sign'] ); } catch (e) { throw new Error(`The ${jwk.alg} is not supported by this browser: ${e}`); } } exports.importJWK = importJWK; async function signCompactJws(alg, privateKey, header, payload) { const jwtHeader = JSON.stringify(Object.assign(Object.assign({}, header), { alg })); const jwtPayload = JSON.stringify(payload); const jwtAuthenticatedContent = `${(0, js_base64_1.encodeURL)(jwtHeader)}.${(0, js_base64_1.encodeURL)(jwtPayload)}`; const signature = await subtle.sign(Object.assign(Object.assign({}, privateKey.algorithm), { hash: "SHA-384" }), privateKey, new TextEncoder().encode(jwtAuthenticatedContent)); return `${jwtAuthenticatedContent}.${(0, js_base64_1.fromUint8Array)(new Uint8Array(signature), true)}`; } exports.signCompactJws = signCompactJws;