From 083cc6d73e16970613e9ed0595ac4d44a922ea95 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 8 Mar 2019 12:57:50 -0700 Subject: [PATCH] v1.2.8: allow non-stringified jwk in parse, ignore undefineds when neutering --- README.md | 18 +++++++++++++++-- keypairs.js | 57 ++++++++++++++++++++++++++++++++++------------------ package.json | 2 +- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 60a3953..fd8c773 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ return Keypairs.export({ jwk: pair.private, format: 'pkcs8' }).then(function (pe ``` // PEM to JWK return Keypairs.import({ pem: pem }).then(function (jwk) { + console.log(jwk); }); ``` @@ -164,9 +165,22 @@ Options } ``` -#### Keypairs.publish({ jwk: jwk }) +#### Keypairs.publish({ jwk: jwk, exp: '3d', use: 'sig' }) -**Synchronously** strips a key of its private parts and returns the public version. +Promises a public key that adheres to the OIDC and Auth0 spec (plus expiry), suitable to be published to a JWKs URL: + +``` +{ "kty": "EC" +, "crv": "P-256" +, "x": "..." +, "y": "..." +, "kid": "..." +, "use": "sig" +, "exp": 1552074208 +} +``` + +In particular this adds "use" and "exp". #### Keypairs.thumbprint({ jwk: jwk }) diff --git a/keypairs.js b/keypairs.js index 0a290b1..bc90541 100644 --- a/keypairs.js +++ b/keypairs.js @@ -10,10 +10,19 @@ var Keypairs = module.exports; Keypairs.generate = function (opts) { opts = opts || {}; var kty = opts.kty || opts.type; + var p; if ('RSA' === kty) { - return Rasha.generate(opts); + p = Rasha.generate(opts); + } else { + p = Eckles.generate(opts); } - return Eckles.generate(opts); + return p.then(function (pair) { + return Keypairs.thumbprint({ jwk: pair.public }).then(function (thumb) { + pair.private.kid = thumb; // maybe not the same id on the private key? + pair.public.kid = thumb; + return pair; + }); + }); }; Keypairs.parse = function (opts) { @@ -24,22 +33,26 @@ Keypairs.parse = function (opts) { var pem; var p; - try { - jwk = JSON.parse(opts.key); - p = Keypairs.export({ jwk: jwk }).catch(function (e) { - pem = opts.key; - err = new Error("Not a valid jwk '" + JSON.stringify(jwk) + "':" + e.message); - err.code = "EINVALID"; - return Promise.reject(err); - }).then(function () { - return jwk; - }); - } catch(e) { - p = Keypairs.import({ pem: opts.key }).catch(function (e) { - err = new Error("Could not parse key (type " + typeof opts.key + ") '" + opts.key + "': " + e.message); - err.code = "EPARSE"; - return Promise.reject(err); - }); + if (!opts.key || !opts.key.kty) { + try { + jwk = JSON.parse(opts.key); + p = Keypairs.export({ jwk: jwk }).catch(function (e) { + pem = opts.key; + err = new Error("Not a valid jwk '" + JSON.stringify(jwk) + "':" + e.message); + err.code = "EINVALID"; + return Promise.reject(err); + }).then(function () { + return jwk; + }); + } catch(e) { + p = Keypairs.import({ pem: opts.key }).catch(function (e) { + err = new Error("Could not parse key (type " + typeof opts.key + ") '" + opts.key + "': " + e.message); + err.code = "EPARSE"; + return Promise.reject(err); + }); + } + } else { + p = Promise.resolve(opts.key); } return p.then(function (jwk) { @@ -74,6 +87,11 @@ Keypairs.parseOrGenerate = function (opts) { Keypairs.import = function (opts) { return Eckles.import(opts).catch(function () { return Rasha.import(opts); + }).then(function (jwk) { + return Keypairs.thumbprint({ jwk: jwk }).then(function (thumb) { + jwk.kid = thumb; + return jwk; + }); }); }; @@ -93,6 +111,7 @@ Keypairs.neuter = Keypairs._neuter = function (opts) { // trying to find the best balance of an immutable copy with custom attributes var jwk = {}; Object.keys(opts.jwk).forEach(function (k) { + if ('undefined' === typeof opts.jwk[k]) { return; } // ignore RSA and EC private parts if (-1 !== ['d', 'p', 'q', 'dp', 'dq', 'qi'].indexOf(k)) { return; } jwk[k] = JSON.parse(JSON.stringify(opts.jwk[k])); @@ -111,7 +130,7 @@ Keypairs.publish = function (opts) { } else { if (opts.exp) { jwk.exp = setTime(opts.exp); } else if (opts.expiresIn) { jwk.exp = Math.round(Date.now()/1000) + opts.expiresIn; } - else { jwk.exp = opts.expiresAt; } + else if (opts.expiresAt) { jwk.exp = opts.expiresAt; } } if (!jwk.use && false !== jwk.use) { jwk.use = "sig"; } diff --git a/package.json b/package.json index ad455e1..312f107 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keypairs", - "version": "1.2.7", + "version": "1.2.8", "description": "Lightweight RSA/ECDSA keypair generation and JWK <-> PEM", "main": "keypairs.js", "files": [