UNPKG

@gaonengwww/jose

Version:

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

1,168 lines (1,144 loc) 33.7 kB
// src/lib/buffer_utils.ts var encoder = new TextEncoder(); var decoder = new TextDecoder(); var MAX_INT32 = 2 ** 32; function concat(...buffers) { const size = buffers.reduce((acc, { length }) => acc + length, 0); const buf = new Uint8Array(size); let i = 0; for (const buffer of buffers) { buf.set(buffer, i); i += buffer.length; } return buf; } // src/lib/base64.ts function encodeBase64(input) { if (Uint8Array.prototype.toBase64) { return input.toBase64(); } const CHUNK_SIZE = 32768; const arr = []; for (let i = 0; i < input.length; i += CHUNK_SIZE) { arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE))); } return btoa(arr.join("")); } function decodeBase64(encoded) { if (Uint8Array.fromBase64) { return Uint8Array.fromBase64(encoded); } const binary = atob(encoded); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } return bytes; } // src/util/base64url.ts function decode(input) { if (Uint8Array.fromBase64) { return Uint8Array.fromBase64(typeof input === "string" ? input : decoder.decode(input), { alphabet: "base64url" }); } let encoded = input; if (encoded instanceof Uint8Array) { encoded = decoder.decode(encoded); } encoded = encoded.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, ""); try { return decodeBase64(encoded); } catch { throw new TypeError("The input to be decoded is not correctly encoded."); } } function encode(input) { let unencoded = input; if (typeof unencoded === "string") { unencoded = encoder.encode(unencoded); } if (Uint8Array.prototype.toBase64) { return unencoded.toBase64({ alphabet: "base64url", omitPadding: true }); } return encodeBase64(unencoded).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); } // src/util/errors.ts var JOSEError = class extends Error { /** * A unique error code for the particular error subclass. * * @ignore */ static code = "ERR_JOSE_GENERIC"; /** A unique error code for {@link JOSEError}. */ code = "ERR_JOSE_GENERIC"; /** @ignore */ constructor(message2, options) { super(message2, options); this.name = this.constructor.name; Error.captureStackTrace?.(this, this.constructor); } }; var JOSENotSupported = class extends JOSEError { /** @ignore */ static code = "ERR_JOSE_NOT_SUPPORTED"; /** A unique error code for {@link JOSENotSupported}. */ code = "ERR_JOSE_NOT_SUPPORTED"; }; var JWSInvalid = class extends JOSEError { /** @ignore */ static code = "ERR_JWS_INVALID"; /** A unique error code for {@link JWSInvalid}. */ code = "ERR_JWS_INVALID"; }; var JWTInvalid = class extends JOSEError { /** @ignore */ static code = "ERR_JWT_INVALID"; /** A unique error code for {@link JWTInvalid}. */ code = "ERR_JWT_INVALID"; }; // src/lib/subtle_dsa.ts var subtle_dsa_default = (alg, algorithm) => { const hash = `SHA-${alg.slice(-3)}`; switch (alg) { case "HS256": case "HS384": case "HS512": return { hash, name: "HMAC" }; case "PS256": case "PS384": case "PS512": return { hash, name: "RSA-PSS", saltLength: parseInt(alg.slice(-3), 10) >> 3 }; case "RS256": case "RS384": case "RS512": return { hash, name: "RSASSA-PKCS1-v1_5" }; case "ES256": case "ES384": case "ES512": return { hash, name: "ECDSA", namedCurve: algorithm.namedCurve }; case "Ed25519": // Fall through case "EdDSA": return { name: "Ed25519" }; default: throw new JOSENotSupported( `alg ${alg} is not supported either by JOSE or your javascript runtime` ); } }; // src/lib/check_key_length.ts var check_key_length_default = (alg, key) => { if (alg.startsWith("RS") || alg.startsWith("PS")) { const { modulusLength } = key.algorithm; if (typeof modulusLength !== "number" || modulusLength < 2048) { throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`); } } }; // src/lib/crypto_key.ts function unusable(name, prop = "algorithm.name") { return new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`); } function isAlgorithm(algorithm, name) { return algorithm.name === name; } function getHashLength(hash) { return parseInt(hash.name.slice(4), 10); } function getNamedCurve(alg) { switch (alg) { case "ES256": return "P-256"; case "ES384": return "P-384"; case "ES512": return "P-521"; default: throw new Error("unreachable"); } } function checkUsage(key, usage) { if (usage && !key.usages.includes(usage)) { throw new TypeError( `CryptoKey does not support this operation, its usages must include ${usage}.` ); } } function checkSigCryptoKey(key, alg, usage) { switch (alg) { case "HS256": case "HS384": case "HS512": { if (!isAlgorithm(key.algorithm, "HMAC")) throw unusable("HMAC"); const expected = parseInt(alg.slice(2), 10); const actual = getHashLength(key.algorithm.hash); if (actual !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash"); break; } case "RS256": case "RS384": case "RS512": { if (!isAlgorithm(key.algorithm, "RSASSA-PKCS1-v1_5")) throw unusable("RSASSA-PKCS1-v1_5"); const expected = parseInt(alg.slice(2), 10); const actual = getHashLength(key.algorithm.hash); if (actual !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash"); break; } case "PS256": case "PS384": case "PS512": { if (!isAlgorithm(key.algorithm, "RSA-PSS")) throw unusable("RSA-PSS"); const expected = parseInt(alg.slice(2), 10); const actual = getHashLength(key.algorithm.hash); if (actual !== expected) throw unusable(`SHA-${expected}`, "algorithm.hash"); break; } case "Ed25519": // Fall through case "EdDSA": { if (!isAlgorithm(key.algorithm, "Ed25519")) throw unusable("Ed25519"); break; } case "ES256": case "ES384": case "ES512": { if (!isAlgorithm(key.algorithm, "ECDSA")) throw unusable("ECDSA"); const expected = getNamedCurve(alg); const actual = key.algorithm.namedCurve; if (actual !== expected) throw unusable(expected, "algorithm.namedCurve"); break; } default: throw new TypeError("CryptoKey does not support this operation"); } checkUsage(key, usage); } // src/lib/invalid_key_input.ts function message(msg, actual, ...types) { types = types.filter(Boolean); if (types.length > 2) { const last = types.pop(); msg += `one of type ${types.join(", ")}, or ${last}.`; } else if (types.length === 2) { msg += `one of type ${types[0]} or ${types[1]}.`; } else { msg += `of type ${types[0]}.`; } if (actual == null) { msg += ` Received ${actual}`; } else if (typeof actual === "function" && actual.name) { msg += ` Received function ${actual.name}`; } else if (typeof actual === "object" && actual != null) { if (actual.constructor?.name) { msg += ` Received an instance of ${actual.constructor.name}`; } } return msg; } var invalid_key_input_default = (actual, ...types) => { return message("Key must be ", actual, ...types); }; function withAlg(alg, actual, ...types) { return message(`Key for the ${alg} algorithm must be `, actual, ...types); } // src/lib/get_sign_verify_key.ts var get_sign_verify_key_default = async (alg, key, usage) => { if (key instanceof Uint8Array) { if (!alg.startsWith("HS")) { throw new TypeError(invalid_key_input_default(key, "CryptoKey", "KeyObject", "JSON Web Key")); } return crypto.subtle.importKey( "raw", key, { hash: `SHA-${alg.slice(-3)}`, name: "HMAC" }, false, [usage] ); } checkSigCryptoKey(key, alg, usage); return key; }; // src/lib/sign.ts var sign_default = async (alg, key, data) => { const cryptoKey = await get_sign_verify_key_default(alg, key, "sign"); check_key_length_default(alg, cryptoKey); const signature = await crypto.subtle.sign( subtle_dsa_default(alg, cryptoKey.algorithm), cryptoKey, data ); return new Uint8Array(signature); }; // src/lib/is_disjoint.ts var is_disjoint_default = (...headers) => { const sources = headers.filter(Boolean); if (sources.length === 0 || sources.length === 1) { return true; } let acc; for (const header of sources) { const parameters = Object.keys(header); if (!acc || acc.size === 0) { acc = new Set(parameters); continue; } for (const parameter of parameters) { if (acc.has(parameter)) { return false; } acc.add(parameter); } } return true; }; // src/lib/is_key_like.ts function isCryptoKey(key) { return key?.[Symbol.toStringTag] === "CryptoKey"; } function isKeyObject(key) { return key?.[Symbol.toStringTag] === "KeyObject"; } var is_key_like_default = (key) => { return isCryptoKey(key) || isKeyObject(key); }; // src/lib/is_object.ts function isObjectLike(value) { return typeof value === "object" && value !== null; } var is_object_default = (input) => { if (!isObjectLike(input) || Object.prototype.toString.call(input) !== "[object Object]") { return false; } if (Object.getPrototypeOf(input) === null) { return true; } let proto = input; while (Object.getPrototypeOf(proto) !== null) { proto = Object.getPrototypeOf(proto); } return Object.getPrototypeOf(input) === proto; }; // src/lib/is_jwk.ts function isJWK(key) { return is_object_default(key) && typeof key.kty === "string"; } function isPrivateJWK(key) { return key.kty !== "oct" && typeof key.d === "string"; } function isPublicJWK(key) { return key.kty !== "oct" && typeof key.d === "undefined"; } function isSecretJWK(key) { return key.kty === "oct" && typeof key.k === "string"; } // src/lib/check_key_type.ts var tag = (key) => key?.[Symbol.toStringTag]; var jwkMatchesOp = (alg, key, usage) => { if (key.use !== void 0) { let expected; switch (usage) { case "sign": case "verify": expected = "sig"; break; case "encrypt": case "decrypt": expected = "enc"; break; } if (key.use !== expected) { throw new TypeError( `Invalid key for this operation, its "use" must be "${expected}" when present` ); } } if (key.alg !== void 0 && key.alg !== alg) { throw new TypeError(`Invalid key for this operation, its "alg" must be "${alg}" when present`); } if (Array.isArray(key.key_ops)) { let expectedKeyOp; switch (true) { case (usage === "sign" || usage === "verify"): // Fall through case alg === "dir": // Fall through case alg.includes("CBC-HS"): expectedKeyOp = usage; break; case alg.startsWith("PBES2"): expectedKeyOp = "deriveBits"; break; case /^A\d{3}(?:GCM)?(?:KW)?$/.test(alg): if (!alg.includes("GCM") && alg.endsWith("KW")) { expectedKeyOp = usage === "encrypt" ? "wrapKey" : "unwrapKey"; } else { expectedKeyOp = usage; } break; case (usage === "encrypt" && alg.startsWith("RSA")): expectedKeyOp = "wrapKey"; break; case usage === "decrypt": expectedKeyOp = alg.startsWith("RSA") ? "unwrapKey" : "deriveBits"; break; } if (expectedKeyOp && key.key_ops?.includes?.(expectedKeyOp) === false) { throw new TypeError( `Invalid key for this operation, its "key_ops" must include "${expectedKeyOp}" when present` ); } } return true; }; var symmetricTypeCheck = (alg, key, usage) => { if (key instanceof Uint8Array) return; if (isJWK(key)) { if (isSecretJWK(key) && jwkMatchesOp(alg, key, usage)) return; throw new TypeError( `JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present` ); } if (!is_key_like_default(key)) { throw new TypeError( withAlg(alg, key, "CryptoKey", "KeyObject", "JSON Web Key", "Uint8Array") ); } if (key.type !== "secret") { throw new TypeError(`${tag(key)} instances for symmetric algorithms must be of type "secret"`); } }; var asymmetricTypeCheck = (alg, key, usage) => { if (isJWK(key)) { switch (usage) { case "decrypt": case "sign": if (isPrivateJWK(key) && jwkMatchesOp(alg, key, usage)) return; throw new TypeError(`JSON Web Key for this operation be a private JWK`); case "encrypt": case "verify": if (isPublicJWK(key) && jwkMatchesOp(alg, key, usage)) return; throw new TypeError(`JSON Web Key for this operation be a public JWK`); } } if (!is_key_like_default(key)) { throw new TypeError(withAlg(alg, key, "CryptoKey", "KeyObject", "JSON Web Key")); } if (key.type === "secret") { throw new TypeError( `${tag(key)} instances for asymmetric algorithms must not be of type "secret"` ); } if (key.type === "public") { switch (usage) { case "sign": throw new TypeError( `${tag(key)} instances for asymmetric algorithm signing must be of type "private"` ); case "decrypt": throw new TypeError( `${tag(key)} instances for asymmetric algorithm decryption must be of type "private"` ); default: break; } } if (key.type === "private") { switch (usage) { case "verify": throw new TypeError( `${tag(key)} instances for asymmetric algorithm verifying must be of type "public"` ); case "encrypt": throw new TypeError( `${tag(key)} instances for asymmetric algorithm encryption must be of type "public"` ); default: break; } } }; var check_key_type_default = (alg, key, usage) => { const symmetric = alg.startsWith("HS") || alg === "dir" || alg.startsWith("PBES2") || /^A(?:128|192|256)(?:GCM)?(?:KW)?$/.test(alg) || /^A(?:128|192|256)CBC-HS(?:256|384|512)$/.test(alg); if (symmetric) { symmetricTypeCheck(alg, key, usage); } else { asymmetricTypeCheck(alg, key, usage); } }; // src/lib/validate_crit.ts var validate_crit_default = (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 /* @__PURE__ */ new Set(); } 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' ); } let recognized; if (recognizedOption !== void 0) { recognized = new Map([...Object.entries(recognizedOption), ...recognizedDefault.entries()]); } else { recognized = recognizedDefault; } for (const parameter of protectedHeader.crit) { if (!recognized.has(parameter)) { throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`); } if (joseHeader[parameter] === void 0) { throw new Err(`Extension Header Parameter "${parameter}" is missing`); } if (recognized.get(parameter) && protectedHeader[parameter] === void 0) { throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`); } } return new Set(protectedHeader.crit); }; // src/lib/jwk_to_key.ts function subtleMapping(jwk) { let algorithm; let keyUsages; switch (jwk.kty) { case "RSA": { switch (jwk.alg) { case "PS256": case "PS384": case "PS512": algorithm = { name: "RSA-PSS", hash: `SHA-${jwk.alg.slice(-3)}` }; keyUsages = jwk.d ? ["sign"] : ["verify"]; break; case "RS256": case "RS384": case "RS512": algorithm = { name: "RSASSA-PKCS1-v1_5", hash: `SHA-${jwk.alg.slice(-3)}` }; keyUsages = jwk.d ? ["sign"] : ["verify"]; break; case "RSA-OAEP": case "RSA-OAEP-256": case "RSA-OAEP-384": case "RSA-OAEP-512": algorithm = { name: "RSA-OAEP", hash: `SHA-${parseInt(jwk.alg.slice(-3), 10) || 1}` }; keyUsages = jwk.d ? ["decrypt", "unwrapKey"] : ["encrypt", "wrapKey"]; break; default: throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); } break; } case "EC": { switch (jwk.alg) { case "ES256": algorithm = { name: "ECDSA", namedCurve: "P-256" }; keyUsages = jwk.d ? ["sign"] : ["verify"]; break; case "ES384": algorithm = { name: "ECDSA", namedCurve: "P-384" }; keyUsages = jwk.d ? ["sign"] : ["verify"]; break; case "ES512": algorithm = { name: "ECDSA", namedCurve: "P-521" }; keyUsages = jwk.d ? ["sign"] : ["verify"]; break; case "ECDH-ES": case "ECDH-ES+A128KW": case "ECDH-ES+A192KW": case "ECDH-ES+A256KW": algorithm = { name: "ECDH", namedCurve: jwk.crv }; keyUsages = jwk.d ? ["deriveBits"] : []; break; default: throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); } break; } case "OKP": { switch (jwk.alg) { case "Ed25519": // Fall through case "EdDSA": algorithm = { name: "Ed25519" }; keyUsages = jwk.d ? ["sign"] : ["verify"]; break; case "ECDH-ES": case "ECDH-ES+A128KW": case "ECDH-ES+A192KW": case "ECDH-ES+A256KW": algorithm = { name: jwk.crv }; keyUsages = jwk.d ? ["deriveBits"] : []; break; default: throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value'); } break; } default: throw new JOSENotSupported('Invalid or unsupported JWK "kty" (Key Type) Parameter value'); } return { algorithm, keyUsages }; } var jwk_to_key_default = async (jwk) => { if (!jwk.alg) { throw new TypeError('"alg" argument is required when "jwk.alg" is not present'); } const { algorithm, keyUsages } = subtleMapping(jwk); const keyData = { ...jwk }; delete keyData.alg; delete keyData.use; return crypto.subtle.importKey( "jwk", keyData, algorithm, jwk.ext ?? (jwk.d ? false : true), jwk.key_ops ?? keyUsages ); }; // src/lib/normalize_key.ts var cache; var handleJWK = async (key, jwk, alg, freeze = false) => { cache ||= /* @__PURE__ */ new WeakMap(); let cached = cache.get(key); if (cached?.[alg]) { return cached[alg]; } const cryptoKey = await jwk_to_key_default({ ...jwk, alg }); if (freeze) Object.freeze(key); if (!cached) { cache.set(key, { [alg]: cryptoKey }); } else { cached[alg] = cryptoKey; } return cryptoKey; }; var handleKeyObject = (keyObject, alg) => { cache ||= /* @__PURE__ */ new WeakMap(); let cached = cache.get(keyObject); if (cached?.[alg]) { return cached[alg]; } const isPublic = keyObject.type === "public"; const extractable = isPublic ? true : false; let cryptoKey; if (keyObject.asymmetricKeyType === "x25519") { switch (alg) { case "ECDH-ES": case "ECDH-ES+A128KW": case "ECDH-ES+A192KW": case "ECDH-ES+A256KW": break; default: throw new TypeError("given KeyObject instance cannot be used for this algorithm"); } cryptoKey = keyObject.toCryptoKey( keyObject.asymmetricKeyType, extractable, isPublic ? [] : ["deriveBits"] ); } if (keyObject.asymmetricKeyType === "ed25519") { if (alg !== "EdDSA" && alg !== "Ed25519") { throw new TypeError("given KeyObject instance cannot be used for this algorithm"); } cryptoKey = keyObject.toCryptoKey(keyObject.asymmetricKeyType, extractable, [ isPublic ? "verify" : "sign" ]); } if (keyObject.asymmetricKeyType === "rsa") { let hash; switch (alg) { case "RSA-OAEP": hash = "SHA-1"; break; case "RS256": case "PS256": case "RSA-OAEP-256": hash = "SHA-256"; break; case "RS384": case "PS384": case "RSA-OAEP-384": hash = "SHA-384"; break; case "RS512": case "PS512": case "RSA-OAEP-512": hash = "SHA-512"; break; default: throw new TypeError("given KeyObject instance cannot be used for this algorithm"); } if (alg.startsWith("RSA-OAEP")) { return keyObject.toCryptoKey( { name: "RSA-OAEP", hash }, extractable, isPublic ? ["encrypt"] : ["decrypt"] ); } cryptoKey = keyObject.toCryptoKey( { name: alg.startsWith("PS") ? "RSA-PSS" : "RSASSA-PKCS1-v1_5", hash }, extractable, [isPublic ? "verify" : "sign"] ); } if (keyObject.asymmetricKeyType === "ec") { const nist = /* @__PURE__ */ new Map([ ["prime256v1", "P-256"], ["secp384r1", "P-384"], ["secp521r1", "P-521"] ]); const namedCurve = nist.get(keyObject.asymmetricKeyDetails?.namedCurve); if (!namedCurve) { throw new TypeError("given KeyObject instance cannot be used for this algorithm"); } if (alg === "ES256" && namedCurve === "P-256") { cryptoKey = keyObject.toCryptoKey( { name: "ECDSA", namedCurve }, extractable, [isPublic ? "verify" : "sign"] ); } if (alg === "ES384" && namedCurve === "P-384") { cryptoKey = keyObject.toCryptoKey( { name: "ECDSA", namedCurve }, extractable, [isPublic ? "verify" : "sign"] ); } if (alg === "ES512" && namedCurve === "P-521") { cryptoKey = keyObject.toCryptoKey( { name: "ECDSA", namedCurve }, extractable, [isPublic ? "verify" : "sign"] ); } if (alg.startsWith("ECDH-ES")) { cryptoKey = keyObject.toCryptoKey( { name: "ECDH", namedCurve }, extractable, isPublic ? [] : ["deriveBits"] ); } } if (!cryptoKey) { throw new TypeError("given KeyObject instance cannot be used for this algorithm"); } if (!cached) { cache.set(keyObject, { [alg]: cryptoKey }); } else { cached[alg] = cryptoKey; } return cryptoKey; }; var normalize_key_default = async (key, alg) => { if (key instanceof Uint8Array) { return key; } if (isCryptoKey(key)) { return key; } if (isKeyObject(key)) { if (key.type === "secret") { return key.export(); } if ("toCryptoKey" in key && typeof key.toCryptoKey === "function") { try { return handleKeyObject(key, alg); } catch (err) { if (err instanceof TypeError) { throw err; } } } let jwk = key.export({ format: "jwk" }); return handleJWK(key, jwk, alg); } if (isJWK(key)) { if (key.k) { return decode(key.k); } return handleJWK(key, key, alg, true); } throw new Error("unreachable"); }; // src/jws/flattened/sign.ts var FlattenedSign = class { #payload; #protectedHeader; #unprotectedHeader; /** * {@link FlattenedSign} constructor * * @param payload Binary representation of the payload to sign. */ constructor(payload) { if (!(payload instanceof Uint8Array)) { throw new TypeError("payload must be an instance of Uint8Array"); } this.#payload = payload; } /** * Sets the JWS Protected Header on the FlattenedSign object. * * @param protectedHeader JWS Protected Header. */ setProtectedHeader(protectedHeader) { if (this.#protectedHeader) { throw new TypeError("setProtectedHeader can only be called once"); } this.#protectedHeader = protectedHeader; return this; } /** * Sets the JWS Unprotected Header on the FlattenedSign object. * * @param unprotectedHeader JWS Unprotected Header. */ setUnprotectedHeader(unprotectedHeader) { if (this.#unprotectedHeader) { throw new TypeError("setUnprotectedHeader can only be called once"); } this.#unprotectedHeader = unprotectedHeader; return this; } /** * Signs and resolves the value of the Flattened JWS object. * * @param key Private Key or Secret to sign the JWS with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Sign options. */ async sign(key, options) { if (!this.#protectedHeader && !this.#unprotectedHeader) { throw new JWSInvalid( "either setProtectedHeader or setUnprotectedHeader must be called before #sign()" ); } if (!is_disjoint_default(this.#protectedHeader, this.#unprotectedHeader)) { throw new JWSInvalid( "JWS Protected and JWS Unprotected Header Parameter names must be disjoint" ); } const joseHeader = { ...this.#protectedHeader, ...this.#unprotectedHeader }; const extensions = validate_crit_default( JWSInvalid, /* @__PURE__ */ new Map([["b64", true]]), options?.crit, this.#protectedHeader, joseHeader ); let b64 = true; if (extensions.has("b64")) { b64 = this.#protectedHeader.b64; if (typeof b64 !== "boolean") { throw new JWSInvalid( 'The "b64" (base64url-encode payload) Header Parameter must be a boolean' ); } } const { alg } = joseHeader; if (typeof alg !== "string" || !alg) { throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid'); } check_key_type_default(alg, key, "sign"); let payload = this.#payload; if (b64) { payload = encoder.encode(encode(payload)); } let protectedHeader; if (this.#protectedHeader) { protectedHeader = encoder.encode(encode(JSON.stringify(this.#protectedHeader))); } else { protectedHeader = encoder.encode(""); } const data = concat(protectedHeader, encoder.encode("."), payload); const k = await normalize_key_default(key, alg); const signature = await sign_default(alg, k, data); const jws = { signature: encode(signature), payload: "" }; if (b64) { jws.payload = decoder.decode(payload); } if (this.#unprotectedHeader) { jws.header = this.#unprotectedHeader; } if (this.#protectedHeader) { jws.protected = decoder.decode(protectedHeader); } return jws; } }; // src/jws/compact/sign.ts var CompactSign = class { #flattened; /** * {@link CompactSign} constructor * * @param payload Binary representation of the payload to sign. */ constructor(payload) { this.#flattened = new FlattenedSign(payload); } /** * Sets the JWS Protected Header on the CompactSign object. * * @param protectedHeader JWS Protected Header. */ setProtectedHeader(protectedHeader) { this.#flattened.setProtectedHeader(protectedHeader); return this; } /** * Signs and resolves the value of the Compact JWS string. * * @param key Private Key or Secret to sign the JWS with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWS Sign options. */ async sign(key, options) { const jws = await this.#flattened.sign(key, options); if (jws.payload === void 0) { throw new TypeError("use the flattened module for creating JWS with b64: false"); } return `${jws.protected}.${jws.payload}.${jws.signature}`; } }; // src/lib/epoch.ts var epoch_default = (date) => Math.floor(date.getTime() / 1e3); // src/lib/secs.ts var minute = 60; var hour = minute * 60; var day = hour * 24; var week = day * 7; var year = day * 365.25; var REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i; var secs_default = (str) => { const matched = REGEX.exec(str); if (!matched || matched[4] && matched[1]) { throw new TypeError("Invalid time period format"); } const value = parseFloat(matched[2]); const unit = matched[3].toLowerCase(); let numericDate; switch (unit) { case "sec": case "secs": case "second": case "seconds": case "s": numericDate = Math.round(value); break; case "minute": case "minutes": case "min": case "mins": case "m": numericDate = Math.round(value * minute); break; case "hour": case "hours": case "hr": case "hrs": case "h": numericDate = Math.round(value * hour); break; case "day": case "days": case "d": numericDate = Math.round(value * day); break; case "week": case "weeks": case "w": numericDate = Math.round(value * week); break; // years matched default: numericDate = Math.round(value * year); break; } if (matched[1] === "-" || matched[4] === "ago") { return -numericDate; } return numericDate; }; // src/lib/jwt_claims_set.ts function validateInput(label, input) { if (!Number.isFinite(input)) { throw new TypeError(`Invalid ${label} input`); } return input; } var JWTClaimsBuilder = class { #payload; constructor(payload) { if (!is_object_default(payload)) { throw new TypeError("JWT Claims Set MUST be an object"); } this.#payload = structuredClone(payload); } data() { return encoder.encode(JSON.stringify(this.#payload)); } get iss() { return this.#payload.iss; } set iss(value) { this.#payload.iss = value; } get sub() { return this.#payload.sub; } set sub(value) { this.#payload.sub = value; } get aud() { return this.#payload.aud; } set aud(value) { this.#payload.aud = value; } get jti() { return this.#payload.jti; } set jti(value) { this.#payload.jti = value; } get nbf() { return this.#payload.nbf; } set nbf(value) { if (typeof value === "number") { this.#payload.nbf = validateInput("setNotBefore", value); } else if (value instanceof Date) { this.#payload.nbf = validateInput("setNotBefore", epoch_default(value)); } else { this.#payload.nbf = epoch_default(/* @__PURE__ */ new Date()) + secs_default(value); } } get exp() { return this.#payload.exp; } set exp(value) { if (typeof value === "number") { this.#payload.exp = validateInput("setExpirationTime", value); } else if (value instanceof Date) { this.#payload.exp = validateInput("setExpirationTime", epoch_default(value)); } else { this.#payload.exp = epoch_default(/* @__PURE__ */ new Date()) + secs_default(value); } } get iat() { return this.#payload.iat; } set iat(value) { if (typeof value === "undefined") { this.#payload.iat = epoch_default(/* @__PURE__ */ new Date()); } else if (value instanceof Date) { this.#payload.iat = validateInput("setIssuedAt", epoch_default(value)); } else if (typeof value === "string") { this.#payload.iat = validateInput("setIssuedAt", epoch_default(/* @__PURE__ */ new Date()) + secs_default(value)); } else { this.#payload.iat = validateInput("setIssuedAt", value); } } }; // src/jwt/sign.ts var SignJWT = class { #protectedHeader; #jwt; /** * {@link SignJWT} constructor * * @param payload The JWT Claims Set object. Defaults to an empty object. */ constructor(payload = {}) { this.#jwt = new JWTClaimsBuilder(payload); } setIssuer(issuer) { this.#jwt.iss = issuer; return this; } setSubject(subject) { this.#jwt.sub = subject; return this; } setAudience(audience) { this.#jwt.aud = audience; return this; } setJti(jwtId) { this.#jwt.jti = jwtId; return this; } setNotBefore(input) { this.#jwt.nbf = input; return this; } setExpirationTime(input) { this.#jwt.exp = input; return this; } setIssuedAt(input) { this.#jwt.iat = input; return this; } /** * Sets the JWS Protected Header on the SignJWT object. * * @param protectedHeader JWS Protected Header. Must contain an "alg" (JWS Algorithm) property. */ setProtectedHeader(protectedHeader) { this.#protectedHeader = protectedHeader; return this; } /** * Signs and returns the JWT. * * @param key Private Key or Secret to sign the JWT with. See * {@link https://github.com/panva/jose/issues/210#jws-alg Algorithm Key Requirements}. * @param options JWT Sign options. */ async sign(key, options) { const sig = new CompactSign(this.#jwt.data()); sig.setProtectedHeader(this.#protectedHeader); if (Array.isArray(this.#protectedHeader?.crit) && this.#protectedHeader.crit.includes("b64") && // @ts-expect-error this.#protectedHeader.b64 === false) { throw new JWTInvalid("JWTs MUST NOT use unencoded payload"); } return sig.sign(key, options); } }; export { SignJWT };