@gaonengwww/jose
Version:
JWA, JWS, JWE, JWT, JWK, JWKS for Node.js, Browser, Cloudflare Workers, Deno, Bun, and other Web-interoperable runtimes
213 lines (201 loc) • 6.38 kB
JavaScript
// src/lib/digest.ts
var digest_default = async (algorithm, data) => {
const subtleDigest = `SHA-${algorithm.slice(-3)}`;
return new Uint8Array(await crypto.subtle.digest(subtleDigest, data));
};
// src/lib/buffer_utils.ts
var encoder = new TextEncoder();
var decoder = new TextDecoder();
var MAX_INT32 = 2 ** 32;
// 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(""));
}
// src/util/base64url.ts
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 JWKInvalid = class extends JOSEError {
/** @ignore */
static code = "ERR_JWK_INVALID";
/** A unique error code for {@link JWKInvalid}. */
code = "ERR_JWK_INVALID";
};
// 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";
}
// 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);
};
// src/lib/key_to_jwk.ts
async function keyToJWK(key) {
if (isKeyObject(key)) {
if (key.type === "secret") {
key = key.export();
} else {
return key.export({ format: "jwk" });
}
}
if (key instanceof Uint8Array) {
return {
kty: "oct",
k: encode(key)
};
}
if (!isCryptoKey(key)) {
throw new TypeError(invalid_key_input_default(key, "CryptoKey", "KeyObject", "Uint8Array"));
}
if (!key.extractable) {
throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK");
}
const { ext, key_ops, alg, use, ...jwk } = await crypto.subtle.exportKey("jwk", key);
return jwk;
}
// src/key/export.ts
async function exportJWK(key) {
return keyToJWK(key);
}
// src/jwk/thumbprint.ts
var check = (value, description) => {
if (typeof value !== "string" || !value) {
throw new JWKInvalid(`${description} missing or invalid`);
}
};
async function calculateJwkThumbprint(key, digestAlgorithm) {
let jwk;
if (isJWK(key)) {
jwk = key;
} else if (is_key_like_default(key)) {
jwk = await exportJWK(key);
} else {
throw new TypeError(invalid_key_input_default(key, "CryptoKey", "KeyObject", "JSON Web Key"));
}
digestAlgorithm ??= "sha256";
if (digestAlgorithm !== "sha256" && digestAlgorithm !== "sha384" && digestAlgorithm !== "sha512") {
throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');
}
let components;
switch (jwk.kty) {
case "EC":
check(jwk.crv, '"crv" (Curve) Parameter');
check(jwk.x, '"x" (X Coordinate) Parameter');
check(jwk.y, '"y" (Y Coordinate) Parameter');
components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x, y: jwk.y };
break;
case "OKP":
check(jwk.crv, '"crv" (Subtype of Key Pair) Parameter');
check(jwk.x, '"x" (Public Key) Parameter');
components = { crv: jwk.crv, kty: jwk.kty, x: jwk.x };
break;
case "RSA":
check(jwk.e, '"e" (Exponent) Parameter');
check(jwk.n, '"n" (Modulus) Parameter');
components = { e: jwk.e, kty: jwk.kty, n: jwk.n };
break;
case "oct":
check(jwk.k, '"k" (Key Value) Parameter');
components = { k: jwk.k, kty: jwk.kty };
break;
default:
throw new JOSENotSupported('"kty" (Key Type) Parameter missing or unsupported');
}
const data = encoder.encode(JSON.stringify(components));
return encode(await digest_default(digestAlgorithm, data));
}
async function calculateJwkThumbprintUri(key, digestAlgorithm) {
digestAlgorithm ??= "sha256";
const thumbprint = await calculateJwkThumbprint(key, digestAlgorithm);
return `urn:ietf:params:oauth:jwk-thumbprint:sha-${digestAlgorithm.slice(-3)}:${thumbprint}`;
}
export {
calculateJwkThumbprint,
calculateJwkThumbprintUri
};