Compare commits
No commits in common. "523a4f0d1a70b6c56ac248044be1553cf2a4a022" and "79e6758f9f6169b1c2a61f594198b5a0bbb8d09a" have entirely different histories.
523a4f0d1a
...
79e6758f9f
22
.jshintrc
22
.jshintrc
|
@ -1,22 +0,0 @@
|
||||||
{
|
|
||||||
"browser": true,
|
|
||||||
"node": true,
|
|
||||||
"esversion": 11,
|
|
||||||
"curly": true,
|
|
||||||
"sub": true,
|
|
||||||
"bitwise": true,
|
|
||||||
"eqeqeq": true,
|
|
||||||
"forin": true,
|
|
||||||
"freeze": true,
|
|
||||||
"immed": true,
|
|
||||||
"latedef": "nofunc",
|
|
||||||
"nonbsp": true,
|
|
||||||
"nonew": true,
|
|
||||||
"plusplus": true,
|
|
||||||
"undef": true,
|
|
||||||
"unused": "vars",
|
|
||||||
"strict": true,
|
|
||||||
"maxdepth": 3,
|
|
||||||
"maxstatements": 100,
|
|
||||||
"maxcomplexity": 10
|
|
||||||
}
|
|
86
keyfetch.js
86
keyfetch.js
|
@ -2,9 +2,8 @@
|
||||||
|
|
||||||
var keyfetch = module.exports;
|
var keyfetch = module.exports;
|
||||||
|
|
||||||
var request = require("@root/request").defaults({
|
var promisify = require("util").promisify;
|
||||||
userAgent: "keyfetch/v2.1.0"
|
var requestAsync = promisify(require("@root/request"));
|
||||||
});
|
|
||||||
var Rasha = require("rasha");
|
var Rasha = require("rasha");
|
||||||
var Eckles = require("eckles");
|
var Eckles = require("eckles");
|
||||||
var mincache = 1 * 60 * 60;
|
var mincache = 1 * 60 * 60;
|
||||||
|
@ -12,19 +11,6 @@ var maxcache = 3 * 24 * 60 * 60;
|
||||||
var staletime = 15 * 60;
|
var staletime = 15 * 60;
|
||||||
var keyCache = {};
|
var keyCache = {};
|
||||||
|
|
||||||
var Errors = require("./lib/errors.js");
|
|
||||||
|
|
||||||
async function requestAsync(req) {
|
|
||||||
var resp = await request(req).catch(Errors.BAD_GATEWAY);
|
|
||||||
|
|
||||||
// differentiate potentially temporary server errors from 404
|
|
||||||
if (!resp.ok && (resp.statusCode >= 500 || resp.statusCode < 200)) {
|
|
||||||
throw Errors.BAD_GATEWAY();
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkMinDefaultMax(opts, key, n, d, x) {
|
function checkMinDefaultMax(opts, key, n, d, x) {
|
||||||
var i = opts[key];
|
var i = opts[key];
|
||||||
if (!i && 0 !== i) {
|
if (!i && 0 !== i) {
|
||||||
|
@ -33,7 +19,7 @@ function checkMinDefaultMax(opts, key, n, d, x) {
|
||||||
if (i >= n && i >= x) {
|
if (i >= n && i >= x) {
|
||||||
return parseInt(i, 10);
|
return parseInt(i, 10);
|
||||||
} else {
|
} else {
|
||||||
throw Errors.DEVELOPER_ERROR("opts." + key + " should be at least " + n + " and at most " + x + ", not " + i);
|
throw new Error("opts." + key + " should be at least " + n + " and at most " + x + ", not " + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,10 +36,9 @@ keyfetch._oidc = async function (iss) {
|
||||||
url: normalizeIss(iss) + "/.well-known/openid-configuration",
|
url: normalizeIss(iss) + "/.well-known/openid-configuration",
|
||||||
json: true
|
json: true
|
||||||
});
|
});
|
||||||
|
|
||||||
var oidcConf = resp.body;
|
var oidcConf = resp.body;
|
||||||
if (!oidcConf.jwks_uri) {
|
if (!oidcConf.jwks_uri) {
|
||||||
throw Errors.OIDC_CONFIG_NOT_FOUND();
|
throw new Error("Failed to retrieve openid configuration");
|
||||||
}
|
}
|
||||||
return oidcConf;
|
return oidcConf;
|
||||||
};
|
};
|
||||||
|
@ -62,7 +47,6 @@ keyfetch._wellKnownJwks = async function (iss) {
|
||||||
};
|
};
|
||||||
keyfetch._jwks = async function (iss) {
|
keyfetch._jwks = async function (iss) {
|
||||||
var resp = await requestAsync({ url: iss, json: true });
|
var resp = await requestAsync({ url: iss, json: true });
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
resp.body.keys.map(async function (jwk) {
|
resp.body.keys.map(async function (jwk) {
|
||||||
// EC keys have an x values, whereas RSA keys do not
|
// EC keys have an x values, whereas RSA keys do not
|
||||||
|
@ -120,7 +104,7 @@ function checkId(id) {
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw Errors.JWK_NOT_FOUND(id);
|
throw new Error("No JWK found by kid or thumbprint '" + id + "'");
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@ -193,7 +177,7 @@ keyfetch._setCache = function (iss, cacheable) {
|
||||||
|
|
||||||
function normalizeIss(iss) {
|
function normalizeIss(iss) {
|
||||||
if (!iss) {
|
if (!iss) {
|
||||||
throw Errors.TOKEN_NO_ISSUER();
|
throw new Error("'iss' is not defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
// We definitely don't want false negatives stemming
|
// We definitely don't want false negatives stemming
|
||||||
|
@ -201,26 +185,26 @@ function normalizeIss(iss) {
|
||||||
// We also don't want to allow insecure issuers
|
// We also don't want to allow insecure issuers
|
||||||
if (/^http:/.test(iss) && !process.env.KEYFETCH_ALLOW_INSECURE_HTTP) {
|
if (/^http:/.test(iss) && !process.env.KEYFETCH_ALLOW_INSECURE_HTTP) {
|
||||||
// note, we wrap some things in promises just so we can throw here
|
// note, we wrap some things in promises just so we can throw here
|
||||||
throw Errors.INSECURE_ISSUER(iss);
|
throw new Error(
|
||||||
|
"'" + iss + "' is NOT secure. Set env 'KEYFETCH_ALLOW_INSECURE_HTTP=true' to allow for testing."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return iss.replace(/\/$/, "");
|
return iss.replace(/\/$/, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
keyfetch.jwt = {};
|
keyfetch.jwt = {};
|
||||||
keyfetch.jwt.decode = function (jwt) {
|
keyfetch.jwt.decode = function (jwt) {
|
||||||
try {
|
|
||||||
var parts = jwt.split(".");
|
var parts = jwt.split(".");
|
||||||
// JWS
|
// JWS
|
||||||
var obj = { protected: parts[0], payload: parts[1], signature: parts[2] };
|
var obj = {
|
||||||
|
protected: parts[0],
|
||||||
|
payload: parts[1],
|
||||||
|
signature: parts[2]
|
||||||
|
};
|
||||||
// JWT
|
// JWT
|
||||||
obj.header = JSON.parse(Buffer.from(obj.protected, "base64"));
|
obj.header = JSON.parse(Buffer.from(obj.protected, "base64"));
|
||||||
obj.claims = JSON.parse(Buffer.from(obj.payload, "base64"));
|
obj.claims = JSON.parse(Buffer.from(obj.payload, "base64"));
|
||||||
return obj;
|
return obj;
|
||||||
} catch (e) {
|
|
||||||
var err = Errors.TOKEN_PARSE_ERROR(jwt);
|
|
||||||
err.details = e.message;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
keyfetch.jwt.verify = async function (jwt, opts) {
|
keyfetch.jwt.verify = async function (jwt, opts) {
|
||||||
if (!opts) {
|
if (!opts) {
|
||||||
|
@ -231,8 +215,6 @@ keyfetch.jwt.verify = async function (jwt, opts) {
|
||||||
var exp;
|
var exp;
|
||||||
var nbf;
|
var nbf;
|
||||||
var active;
|
var active;
|
||||||
var now;
|
|
||||||
var then;
|
|
||||||
var issuers = opts.issuers || [];
|
var issuers = opts.issuers || [];
|
||||||
if (opts.iss) {
|
if (opts.iss) {
|
||||||
issuers.push(opts.iss);
|
issuers.push(opts.iss);
|
||||||
|
@ -241,23 +223,25 @@ keyfetch.jwt.verify = async function (jwt, opts) {
|
||||||
issuers.push(opts.claims.iss);
|
issuers.push(opts.claims.iss);
|
||||||
}
|
}
|
||||||
if (!issuers.length) {
|
if (!issuers.length) {
|
||||||
if (!(opts.jwk || opts.jwks)) {
|
throw new Error(
|
||||||
throw Errors.DEVELOPER_ERROR(
|
|
||||||
"[keyfetch.js] Security Error: Neither of opts.issuers nor opts.iss were provided. If you would like to bypass issuer verification (i.e. for federated authn) you must explicitly set opts.issuers = ['*']. Otherwise set a value such as https://accounts.google.com/"
|
"[keyfetch.js] Security Error: Neither of opts.issuers nor opts.iss were provided. If you would like to bypass issuer verification (i.e. for federated authn) you must explicitly set opts.issuers = ['*']. Otherwise set a value such as https://accounts.google.com/"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
var claims = opts.claims || {};
|
var claims = opts.claims || {};
|
||||||
if (!jwt || "string" === typeof jwt) {
|
if (!jwt || "string" === typeof jwt) {
|
||||||
|
try {
|
||||||
decoded = keyfetch.jwt.decode(jwt);
|
decoded = keyfetch.jwt.decode(jwt);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("could not parse jwt: '" + jwt + "'");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
decoded = jwt;
|
decoded = jwt;
|
||||||
}
|
}
|
||||||
|
exp = decoded.claims.exp;
|
||||||
|
nbf = decoded.claims.nbf;
|
||||||
|
|
||||||
if (!decoded.claims.iss || !issuers.some(isTrustedIssuer(decoded.claims.iss))) {
|
if (!issuers.some(isTrustedIssuer(decoded.claims.iss))) {
|
||||||
if (!(opts.jwk || opts.jwks)) {
|
throw new Error("token was issued by an untrusted issuer: '" + decoded.claims.iss + "'");
|
||||||
throw Errors.ISSUER_NOT_TRUSTED(decoded.claims.iss || "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Note claims.iss validates more strictly than opts.issuers (requires exact match)
|
// Note claims.iss validates more strictly than opts.issuers (requires exact match)
|
||||||
if (
|
if (
|
||||||
|
@ -267,26 +251,20 @@ keyfetch.jwt.verify = async function (jwt, opts) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
throw Errors.CLAIMS_MISMATCH(Object.keys(claims));
|
throw new Error("token did not match on one or more authorization claims: '" + Object.keys(claims) + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
exp = decoded.claims.exp;
|
active = (opts.exp || 0) + parseInt(exp, 10) - Date.now() / 1000 > 0;
|
||||||
if (exp && false !== opts.exp) {
|
|
||||||
now = Date.now();
|
|
||||||
// TODO document that opts.exp can be used as leeway? Or introduce opts.leeway?
|
|
||||||
then = (opts.exp || 0) + parseInt(exp, 10);
|
|
||||||
active = then - now / 1000 > 0;
|
|
||||||
// expiration was on the token or, if not, such a token is not allowed
|
|
||||||
if (!active) {
|
if (!active) {
|
||||||
throw Errors.TOKEN_EXPIRED(exp);
|
// expiration was on the token or, if not, such a token is not allowed
|
||||||
|
if (exp || false !== opts.exp) {
|
||||||
|
throw new Error("token's 'exp' has passed or could not parsed: '" + exp + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nbf = decoded.claims.nbf;
|
|
||||||
if (nbf) {
|
if (nbf) {
|
||||||
active = parseInt(nbf, 10) - Date.now() / 1000 <= 0;
|
active = parseInt(nbf, 10) - Date.now() / 1000 <= 0;
|
||||||
if (!active) {
|
if (!active) {
|
||||||
throw Errors.TOKEN_INACTIVE(nbf);
|
throw new Error("token's 'nbf' has not been reached or could not parsed: '" + nbf + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (opts.jwks || opts.jwk) {
|
if (opts.jwks || opts.jwk) {
|
||||||
|
@ -320,7 +298,7 @@ keyfetch.jwt.verify = async function (jwt, opts) {
|
||||||
if (true === keyfetch.jws.verify(decoded, hit)) {
|
if (true === keyfetch.jws.verify(decoded, hit)) {
|
||||||
return decoded;
|
return decoded;
|
||||||
}
|
}
|
||||||
throw Errors.TOKEN_INVALID_SIGNATURE();
|
throw new Error("token signature verification was unsuccessful");
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyAny(hits) {
|
function verifyAny(hits) {
|
||||||
|
@ -333,7 +311,7 @@ keyfetch.jwt.verify = async function (jwt, opts) {
|
||||||
if (true === keyfetch.jws.verify(decoded, hit)) {
|
if (true === keyfetch.jws.verify(decoded, hit)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
throw Errors.TOKEN_INVALID_SIGNATURE();
|
throw new Error("token signature verification was unsuccessful");
|
||||||
} else {
|
} else {
|
||||||
if (true === keyfetch.jws.verify(decoded, hit)) {
|
if (true === keyfetch.jws.verify(decoded, hit)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -343,7 +321,7 @@ keyfetch.jwt.verify = async function (jwt, opts) {
|
||||||
) {
|
) {
|
||||||
return decoded;
|
return decoded;
|
||||||
}
|
}
|
||||||
throw Errors.TOKEN_UNKNOWN_SIGNER();
|
throw new Error("Retrieved a list of keys, but none of them matched the 'kid' (key id) of the token.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function overrideLookup(jwks) {
|
function overrideLookup(jwks) {
|
||||||
|
|
176
lib/errors.js
176
lib/errors.js
|
@ -1,176 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// Possible User Errors
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef AuthError
|
|
||||||
* @property {string} message
|
|
||||||
* @property {number} status
|
|
||||||
* @property {string} code
|
|
||||||
* @property {any} [details]
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} msg
|
|
||||||
* @param {{
|
|
||||||
* status: number,
|
|
||||||
* code: string,
|
|
||||||
* details?: any,
|
|
||||||
* }} opts
|
|
||||||
* @returns {AuthError}
|
|
||||||
*/
|
|
||||||
function create(msg, { status = 401, code = "", details }) {
|
|
||||||
/** @type AuthError */
|
|
||||||
//@ts-ignore
|
|
||||||
var err = new Error(msg);
|
|
||||||
err.message = err.message;
|
|
||||||
err.status = status;
|
|
||||||
err.code = code;
|
|
||||||
if (details) {
|
|
||||||
err.details = details;
|
|
||||||
}
|
|
||||||
err.source = "keyfetch";
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEVELOPER_ERROR - a good token won't make a difference
|
|
||||||
var E_DEVELOPER = "DEVELOPER_ERROR";
|
|
||||||
|
|
||||||
// BAD_GATEWAY - there may be a temporary error fetching the public or or whatever
|
|
||||||
var E_BAD_GATEWAY = "BAD_GATEWAY";
|
|
||||||
|
|
||||||
// MALFORMED_TOKEN - the token could not be verified - not parsable, missing claims, etc
|
|
||||||
var E_MALFORMED = "MALFORMED_JWT";
|
|
||||||
|
|
||||||
// INVALID_TOKEN - the token's properties don't meet requirements - iss, claims, sig, exp
|
|
||||||
var E_INVALID = "INVALID_JWT";
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
//
|
|
||||||
// DEVELOPER_ERROR (dev / server)
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} msg
|
|
||||||
* @returns {AuthError}
|
|
||||||
*/
|
|
||||||
DEVELOPER_ERROR: function (msg) {
|
|
||||||
return create(msg, { status: 500, code: E_DEVELOPER });
|
|
||||||
},
|
|
||||||
BAD_GATEWAY: function (/*err*/) {
|
|
||||||
var msg = "The server encountered a network error or a bad gateway.";
|
|
||||||
return create(msg, { status: 502, code: E_BAD_GATEWAY });
|
|
||||||
},
|
|
||||||
|
|
||||||
//
|
|
||||||
// MALFORMED_TOKEN (dev / client)
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} iss
|
|
||||||
* @returns {AuthError}
|
|
||||||
*/
|
|
||||||
INSECURE_ISSUER: function (iss) {
|
|
||||||
var msg =
|
|
||||||
"'" + iss + "' is NOT secure. Set env 'KEYFETCH_ALLOW_INSECURE_HTTP=true' to allow for testing. (iss)";
|
|
||||||
return create(msg, { status: 400, code: E_MALFORMED });
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @param {string} jwt
|
|
||||||
* @returns {AuthError}
|
|
||||||
*/
|
|
||||||
TOKEN_PARSE_ERROR: function (jwt) {
|
|
||||||
var msg = "could not parse jwt: '" + jwt + "'";
|
|
||||||
return create(msg, { status: 400, code: E_MALFORMED });
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @param {string} iss
|
|
||||||
* @returns {AuthError}
|
|
||||||
*/
|
|
||||||
TOKEN_NO_ISSUER: function (iss) {
|
|
||||||
var msg = "'iss' is not defined";
|
|
||||||
return create(msg, { status: 400, code: E_MALFORMED });
|
|
||||||
},
|
|
||||||
|
|
||||||
//
|
|
||||||
// INVALID_TOKEN (dev / client)
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} exp
|
|
||||||
* @returns {AuthError}
|
|
||||||
*/
|
|
||||||
TOKEN_EXPIRED: function (exp) {
|
|
||||||
//var msg = "The auth token is expired. (exp='" + exp + "')";
|
|
||||||
var msg = "token's 'exp' has passed or could not parsed: '" + exp + "'";
|
|
||||||
return create(msg, { code: E_INVALID });
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @param {number} nbf
|
|
||||||
* @returns {AuthError}
|
|
||||||
*/
|
|
||||||
TOKEN_INACTIVE: function (nbf) {
|
|
||||||
//var msg = "The auth token is not active yet. (nbf='" + nbf + "')";
|
|
||||||
var msg = "token's 'nbf' has not been reached or could not parsed: '" + nbf + "'";
|
|
||||||
return create(msg, { code: E_INVALID });
|
|
||||||
},
|
|
||||||
/** @returns {AuthError} */
|
|
||||||
TOKEN_INVALID_SIGNATURE: function () {
|
|
||||||
//var msg = "The auth token is not properly signed and could not be verified.";
|
|
||||||
var msg = "token signature verification was unsuccessful";
|
|
||||||
return create(msg, { code: E_INVALID });
|
|
||||||
},
|
|
||||||
/** @returns {AuthError} */
|
|
||||||
TOKEN_UNKNOWN_SIGNER: function () {
|
|
||||||
var msg = "Retrieved a list of keys, but none of them matched the 'kid' (key id) of the token.";
|
|
||||||
return create(msg, { code: E_INVALID });
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @param {string} id
|
|
||||||
* @returns {AuthError}
|
|
||||||
*/
|
|
||||||
JWK_NOT_FOUND: function (id) {
|
|
||||||
var msg = "No JWK found by kid or thumbprint '" + id + "'";
|
|
||||||
return create(msg, { code: E_INVALID });
|
|
||||||
},
|
|
||||||
/** @returns {AuthError} */
|
|
||||||
OIDC_CONFIG_NOT_FOUND: function () {
|
|
||||||
//var msg = "Failed to retrieve OpenID configuration for token issuer";
|
|
||||||
var msg = "Failed to retrieve openid configuration";
|
|
||||||
return create(msg, { code: E_INVALID });
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @param {string} iss
|
|
||||||
* @returns {AuthError}
|
|
||||||
*/
|
|
||||||
ISSUER_NOT_TRUSTED: function (iss) {
|
|
||||||
var msg = "token was issued by an untrusted issuer: '" + iss + "'";
|
|
||||||
return create(msg, { code: E_INVALID });
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* @param {Array<string>} claimNames
|
|
||||||
* @returns {AuthError}
|
|
||||||
*/
|
|
||||||
CLAIMS_MISMATCH: function (claimNames) {
|
|
||||||
var msg = "token did not match on one or more authorization claims: '" + claimNames + "'";
|
|
||||||
return create(msg, { code: E_INVALID });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// for README
|
|
||||||
if (require.main === module) {
|
|
||||||
console.info("| Name | Status | Message (truncated) |");
|
|
||||||
console.info("| ---- | ------ | ------------------- |");
|
|
||||||
Object.keys(module.exports).forEach(function (k) {
|
|
||||||
//@ts-ignore
|
|
||||||
var E = module.exports[k];
|
|
||||||
var e = E();
|
|
||||||
var code = e.code;
|
|
||||||
var msg = e.message;
|
|
||||||
if ("E_" + k !== e.code) {
|
|
||||||
code = k;
|
|
||||||
msg = e.details || msg;
|
|
||||||
}
|
|
||||||
console.info(`| ${code} | ${e.status} | ${msg.slice(0, 45)}... |`);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,62 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "keyfetch",
|
"name": "keyfetch",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "keyfetch",
|
|
||||||
"version": "2.0.0",
|
|
||||||
"license": "MPL-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@root/request": "^1.8.0",
|
|
||||||
"eckles": "^1.4.1",
|
|
||||||
"rasha": "^1.2.4"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"keypairs": "^1.2.14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@root/request": {
|
|
||||||
"version": "1.8.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.8.0.tgz",
|
|
||||||
"integrity": "sha512-HufCvoTwqR30OyKSjwg28W5QCUpypSJZpOYcJbC9PME5kI6cOYsccYs/6bXfsuEoarz8+YwBDrsuM1UdBMxMLw=="
|
|
||||||
},
|
|
||||||
"node_modules/eckles": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/eckles/-/eckles-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-auWyk/k8oSkVHaD4RxkPadKsLUcIwKgr/h8F7UZEueFDBO7BsE4y+H6IMUDbfqKIFPg/9MxV6KcBdJCmVVcxSA==",
|
|
||||||
"bin": {
|
|
||||||
"eckles": "bin/eckles.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/keypairs": {
|
|
||||||
"version": "1.2.14",
|
|
||||||
"resolved": "https://registry.npmjs.org/keypairs/-/keypairs-1.2.14.tgz",
|
|
||||||
"integrity": "sha512-ZoZfZMygyB0QcjSlz7Rh6wT2CJasYEHBPETtmHZEfxuJd7bnsOG5AdtPZqHZBT+hoHvuWCp/4y8VmvTvH0Y9uA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"eckles": "^1.4.1",
|
|
||||||
"rasha": "^1.2.4"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"keypairs-install": "bin/keypairs.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rasha": {
|
|
||||||
"version": "1.2.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/rasha/-/rasha-1.2.4.tgz",
|
|
||||||
"integrity": "sha512-GsIwKv+hYSumJyK9wkTDaERLwvWaGYh1WuI7JMTBISfYt13TkKFU/HFzlY4n72p8VfXZRUYm0AqaYhkZVxOC3Q==",
|
|
||||||
"bin": {
|
|
||||||
"rasha": "bin/rasha.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@root/request": {
|
"@root/request": {
|
||||||
"version": "1.8.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.7.0.tgz",
|
||||||
"integrity": "sha512-HufCvoTwqR30OyKSjwg28W5QCUpypSJZpOYcJbC9PME5kI6cOYsccYs/6bXfsuEoarz8+YwBDrsuM1UdBMxMLw=="
|
"integrity": "sha512-lre7XVeEwszgyrayWWb/kRn5fuJfa+n0Nh+rflM9E+EpC28yIYA+FPm/OL1uhzp3TxhQM0HFN4FE2RDIPGlnmg=="
|
||||||
},
|
},
|
||||||
"eckles": {
|
"eckles": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
|
|
|
@ -4,11 +4,9 @@
|
||||||
"description": "Lightweight support for fetching JWKs.",
|
"description": "Lightweight support for fetching JWKs.",
|
||||||
"homepage": "https://git.rootprojects.org/root/keyfetch.js",
|
"homepage": "https://git.rootprojects.org/root/keyfetch.js",
|
||||||
"main": "keyfetch.js",
|
"main": "keyfetch.js",
|
||||||
"files": [
|
"files": [],
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@root/request": "^1.8.0",
|
"@root/request": "^1.7.0",
|
||||||
"eckles": "^1.4.1",
|
"eckles": "^1.4.1",
|
||||||
"rasha": "^1.2.4"
|
"rasha": "^1.2.4"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue