UNPKG

jose

Version:

JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes

123 lines (122 loc) • 4.78 kB
import { JOSENotSupported, JWSInvalid } from "../util/errors.js"; import { decode } from "../util/base64url.js"; import { encode, strictDecoder } from "./buffer_utils.js"; function assertUint8Array(input, label) { if (!(input instanceof Uint8Array)) throw new TypeError(`${label} must be an instance of Uint8Array`); } function isObject(input) { if (typeof input != "object" || input === null || Object.prototype.toString.call(input) !== "[object Object]") return !1; const prototype = Object.getPrototypeOf(input); return prototype === null || Object.getPrototypeOf(prototype) === null; } function isJwkSet(input) { return isObject(input) && Array.isArray(input.keys) && Array.from(input.keys).every(isObject); } function isDisjoint(...headers) { const parameters = /* @__PURE__ */ new Set(); for (const header of headers) if (header) for (const parameter of Object.keys(header)) { if (parameters.has(parameter)) return !1; parameters.add(parameter); } return !0; } function assertNotSet(value, name) { if (value !== void 0) throw new TypeError(`${name} can only be called once`); } function decodeBase64url(value, label, ErrorClass) { try { return decode(value); } catch (cause) { throw new ErrorClass(`Failed to base64url decode the ${label}`, { cause }); } } function encodeAsciiMember(value, label, ErrorClass) { try { return encode(value); } catch (cause) { throw new ErrorClass(`The ${label} is not a valid base64url string`, { cause }); } } function parseJoseHeader(encodedProtectedHeader, ErrorClass, message) { let parsed; try { parsed = JSON.parse(strictDecoder.decode(decode(encodedProtectedHeader))); } catch (cause) { throw new ErrorClass(message, { cause }); } if (!isObject(parsed)) throw new ErrorClass(message); return parsed; } const JWS_RECOGNIZED = { __proto__: null, b64: !0 }, JWE_RECOGNIZED = { __proto__: null }; function validateAlgorithms(option, algorithms) { if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s != "string"))) throw new TypeError(`"${option}" option must be an array of strings`); return algorithms === void 0 ? void 0 : new Set(algorithms); } function validateCritDuplicates(Err, protectedHeader) { const { crit } = protectedHeader ?? {}; if (Array.isArray(crit) && new Set(crit).size !== crit.length) throw new Err('"crit" (Critical) Header Parameter MUST NOT contain duplicate values'); } function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) { if (joseHeader.crit !== void 0 && protectedHeader?.crit === void 0) throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected'); if (!protectedHeader || protectedHeader.crit === void 0) return []; if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input != "string" || input.length === 0)) throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present'); const recognized = recognizedOption === void 0 ? recognizedDefault : { __proto__: null, ...recognizedOption, ...recognizedDefault }; for (const parameter of protectedHeader.crit) { if (!(parameter in recognized)) throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`); if (!Object.hasOwn(joseHeader, parameter) || joseHeader[parameter] === void 0) throw new Err(`Extension Header Parameter "${parameter}" is missing`); if (recognized[parameter] && (!Object.hasOwn(protectedHeader, parameter) || protectedHeader[parameter] === void 0)) throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); } return protectedHeader.crit; } function validateB64(protectedHeader, extensions) { if (extensions.includes("b64")) { const b64 = protectedHeader.b64; if (typeof b64 != "boolean") throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean'); return b64; } return !0; } function serializeJoseHeader(Err, header) { let serialized, parsed; try { serialized = JSON.stringify(header), parsed = JSON.parse(serialized); } catch (cause) { throw new Err("JOSE Header is not valid JSON", { cause }); } if (!isObject(parsed)) throw new Err("JOSE Header is not a JSON object"); return [parsed, serialized]; } export { JWE_RECOGNIZED, JWS_RECOGNIZED, assertNotSet, assertUint8Array, decodeBase64url, encodeAsciiMember, isDisjoint, isJwkSet, isObject, parseJoseHeader, serializeJoseHeader, validateAlgorithms, validateB64, validateCrit, validateCritDuplicates };