ts-jose
Version:
Wrap functions of JOSE in steady interface
103 lines • 3.27 kB
JavaScript
import { calculateJwkThumbprint, exportJWK, generateKeyPair, generateSecret, importJWK, } from 'jose';
import { JoseError } from './error.js';
const RSAPrivateProperties = ['d', 'p', 'q', 'dp', 'dq', 'qi', 'oth'];
export class JWK {
constructor(key, metadata) {
this.key = key;
this.metadata = deleteUndefined(metadata);
}
get kid() {
return this.metadata.kid;
}
get alg() {
return this.metadata.alg;
}
get use() {
return this.metadata.use;
}
get kty() {
return this.metadata.kty;
}
get isPrivate() {
switch (this.kty) {
case 'oct':
return true;
case 'EC':
case 'OKP':
return this.metadata.d !== undefined;
case 'RSA':
return Object.entries(this.metadata)
.filter((entry) => RSAPrivateProperties.includes(entry[0]))
.some((entry) => entry[1] !== undefined);
}
}
getThumbprint(config) {
return calculateJwkThumbprint(this.metadata, config?.digestAlgorithm);
}
getKey(options) {
if (options.kid !== undefined && options.kid !== this.kid) {
throw new JoseError('Key', 'kid', options.kid, this.kid);
}
if (options.use !== undefined && this.use !== undefined) {
if (options.use !== this.use) {
throw new JoseError('Key', 'use', options.use, this.use);
}
}
if (options.alg !== undefined && this.alg !== undefined) {
if (options.alg !== this.alg) {
throw new JoseError('Key', 'alg', options.alg, this.alg);
}
}
return this;
}
async toPublic() {
if (!this.isPrivate)
return this;
return JWK.fromObject(this.toObject());
}
toObject(asPrivate = false) {
if (this.isPrivate && !asPrivate) {
const { kid, alg, use, kty, crv, x, y, e, n } = this.metadata;
return deleteUndefined({ kid, alg, use, kty, crv, x, y, e, n });
}
const { ...key } = this.metadata;
return key;
}
static async fromObject(keyObject) {
const key = await importJWK(keyObject, keyObject.alg, {
extractable: true,
});
return new JWK(key, keyObject);
}
static async generate(algorithm, options) {
const key = await generateKey(algorithm, options);
const metadata = (await exportJWK(key));
return new JWK(key, {
kid: options?.kid,
use: options?.use,
alg: algorithm,
...metadata,
});
}
}
function deleteUndefined(data) {
Object.entries(data).forEach((entry) => {
if (entry[1] === undefined) {
delete data[entry[0]];
}
});
return data;
}
async function generateKey(algorithm, options) {
if (algorithm.startsWith('HS') || algorithm.startsWith('A')) {
return generateSecret(algorithm, {
extractable: false,
});
}
const keyPair = await generateKeyPair(algorithm, {
...options,
extractable: true,
});
return keyPair.privateKey;
}
//# sourceMappingURL=jwk.js.map