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
JavaScript
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
};