security warning -> issuer error, async/await
This commit is contained in:
parent
a648de58d8
commit
842807f92b
|
@ -171,8 +171,8 @@ keyfetch.jwt.verify(jwt, { strategy: "oidc" }).then(function (verified) {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
When used for authorization, it's important to specify which `issuers` are allowed
|
When used for authorization, it's important to specify a limited set of trusted `issuers`. \
|
||||||
(otherwise anyone can create a valid token with whatever any claims they want).
|
When using for federated authentication you may set `issuers = ["*"]` - but **DO NOT** trust claims such as `email` and `email_verified`.
|
||||||
|
|
||||||
If your authorization `claims` can be expressed as exact string matches, you can specify those too.
|
If your authorization `claims` can be expressed as exact string matches, you can specify those too.
|
||||||
|
|
||||||
|
|
|
@ -120,17 +120,12 @@ keypairs.generate().then(function (pair) {
|
||||||
exp: "1h"
|
exp: "1h"
|
||||||
})
|
})
|
||||||
.then(function (jwt) {
|
.then(function (jwt) {
|
||||||
var warned = false;
|
|
||||||
console.warn = function () {
|
|
||||||
warned = true;
|
|
||||||
};
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
// test that the old behavior of defaulting to '*' still works
|
// test that the old behavior of defaulting to '*' still works
|
||||||
keyfetch.jwt.verify(jwt, { jwk: pair.public }).then(function () {
|
keyfetch.jwt
|
||||||
if (!warned) {
|
.verify(jwt, { jwk: pair.public })
|
||||||
throw e("should have issued security warning about allow all by default");
|
.then(e("should have issued security warning about allow all by default"))
|
||||||
}
|
.catch(throwIfNotExpected),
|
||||||
}),
|
|
||||||
keyfetch.jwt.verify(jwt, { jwk: pair.public, issuers: ["*"] }),
|
keyfetch.jwt.verify(jwt, { jwk: pair.public, issuers: ["*"] }),
|
||||||
keyfetch.jwt.verify(jwt).then(e("should have an issuer")).catch(throwIfNotExpected),
|
keyfetch.jwt.verify(jwt).then(e("should have an issuer")).catch(throwIfNotExpected),
|
||||||
keyfetch.jwt
|
keyfetch.jwt
|
||||||
|
|
461
keyfetch.js
461
keyfetch.js
|
@ -11,7 +11,6 @@ var maxcache = 3 * 24 * 60 * 60;
|
||||||
var staletime = 15 * 60;
|
var staletime = 15 * 60;
|
||||||
var keyCache = {};
|
var keyCache = {};
|
||||||
|
|
||||||
/*global Promise*/
|
|
||||||
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) {
|
||||||
|
@ -32,85 +31,70 @@ keyfetch.init = function (opts) {
|
||||||
maxcache = checkMinDefaultMax(opts, "maxcache", 1 * 60 * 60, maxcache, 31 * 24 * 60 * 60);
|
maxcache = checkMinDefaultMax(opts, "maxcache", 1 * 60 * 60, maxcache, 31 * 24 * 60 * 60);
|
||||||
staletime = checkMinDefaultMax(opts, "staletime", 1 * 60, staletime, 31 * 24 * 60 * 60);
|
staletime = checkMinDefaultMax(opts, "staletime", 1 * 60, staletime, 31 * 24 * 60 * 60);
|
||||||
};
|
};
|
||||||
keyfetch._oidc = function (iss) {
|
keyfetch._oidc = async function (iss) {
|
||||||
return Promise.resolve().then(function () {
|
var resp = await requestAsync({
|
||||||
return requestAsync({
|
url: normalizeIss(iss) + "/.well-known/openid-configuration",
|
||||||
url: normalizeIss(iss) + "/.well-known/openid-configuration",
|
json: true
|
||||||
json: true
|
|
||||||
}).then(function (resp) {
|
|
||||||
var oidcConf = resp.body;
|
|
||||||
if (!oidcConf.jwks_uri) {
|
|
||||||
throw new Error("Failed to retrieve openid configuration");
|
|
||||||
}
|
|
||||||
return oidcConf;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
var oidcConf = resp.body;
|
||||||
|
if (!oidcConf.jwks_uri) {
|
||||||
|
throw new Error("Failed to retrieve openid configuration");
|
||||||
|
}
|
||||||
|
return oidcConf;
|
||||||
};
|
};
|
||||||
keyfetch._wellKnownJwks = function (iss) {
|
keyfetch._wellKnownJwks = async function (iss) {
|
||||||
return Promise.resolve().then(function () {
|
return keyfetch._jwks(normalizeIss(iss) + "/.well-known/jwks.json");
|
||||||
return keyfetch._jwks(normalizeIss(iss) + "/.well-known/jwks.json");
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
keyfetch._jwks = function (iss) {
|
keyfetch._jwks = async function (iss) {
|
||||||
return requestAsync({ url: iss, json: true }).then(function (resp) {
|
var resp = await requestAsync({ url: iss, json: true });
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
resp.body.keys.map(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
|
||||||
var Keypairs = jwk.x ? Eckles : Rasha;
|
var Keypairs = jwk.x ? Eckles : Rasha;
|
||||||
return Keypairs.thumbprint({ jwk: jwk }).then(function (thumbprint) {
|
var thumbprint = await Keypairs.thumbprint({ jwk: jwk });
|
||||||
return Keypairs.export({ jwk: jwk }).then(function (pem) {
|
var pem = await Keypairs.export({ jwk: jwk });
|
||||||
var cacheable = {
|
var cacheable = {
|
||||||
jwk: jwk,
|
jwk: jwk,
|
||||||
thumbprint: thumbprint,
|
thumbprint: thumbprint,
|
||||||
pem: pem
|
pem: pem
|
||||||
};
|
};
|
||||||
return cacheable;
|
return cacheable;
|
||||||
});
|
})
|
||||||
});
|
);
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
keyfetch.jwks = function (jwkUrl) {
|
keyfetch.jwks = async function (jwkUrl) {
|
||||||
// TODO DRY up a bit
|
// TODO DRY up a bit
|
||||||
return keyfetch._jwks(jwkUrl).then(function (results) {
|
var results = await keyfetch._jwks(jwkUrl);
|
||||||
return Promise.all(
|
await Promise.all(
|
||||||
results.map(function (result) {
|
results.map(async function (result) {
|
||||||
return keyfetch._setCache(result.jwk.iss || jwkUrl, result);
|
return keyfetch._setCache(result.jwk.iss || jwkUrl, result);
|
||||||
})
|
})
|
||||||
).then(function () {
|
);
|
||||||
// cacheable -> hit (keep original externally immutable)
|
// cacheable -> hit (keep original externally immutable)
|
||||||
return JSON.parse(JSON.stringify(results));
|
return JSON.parse(JSON.stringify(results));
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
keyfetch.wellKnownJwks = function (iss) {
|
keyfetch.wellKnownJwks = async function (iss) {
|
||||||
// TODO DRY up a bit
|
// TODO DRY up a bit
|
||||||
return keyfetch._wellKnownJwks(iss).then(function (results) {
|
var results = await keyfetch._wellKnownJwks(iss);
|
||||||
return Promise.all(
|
await Promise.all(
|
||||||
results.map(function (result) {
|
results.map(async function (result) {
|
||||||
return keyfetch._setCache(result.jwk.iss || iss, result);
|
return keyfetch._setCache(result.jwk.iss || iss, result);
|
||||||
})
|
})
|
||||||
).then(function () {
|
);
|
||||||
// result -> hit (keep original externally immutable)
|
// result -> hit (keep original externally immutable)
|
||||||
return JSON.parse(JSON.stringify(results));
|
return JSON.parse(JSON.stringify(results));
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
keyfetch.oidcJwks = function (iss) {
|
keyfetch.oidcJwks = async function (iss) {
|
||||||
return keyfetch._oidc(iss).then(function (oidcConf) {
|
var oidcConf = await keyfetch._oidc(iss);
|
||||||
// TODO DRY up a bit
|
// TODO DRY up a bit
|
||||||
return keyfetch._jwks(oidcConf.jwks_uri).then(function (results) {
|
var results = await keyfetch._jwks(oidcConf.jwks_uri);
|
||||||
return Promise.all(
|
await Promise.all(
|
||||||
results.map(function (result) {
|
results.map(async function (result) {
|
||||||
return keyfetch._setCache(result.jwk.iss || iss, result);
|
return keyfetch._setCache(result.jwk.iss || iss, result);
|
||||||
})
|
})
|
||||||
).then(function () {
|
);
|
||||||
// result -> hit (keep original externally immutable)
|
// result -> hit (keep original externally immutable)
|
||||||
return JSON.parse(JSON.stringify(results));
|
return JSON.parse(JSON.stringify(results));
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
function checkId(id) {
|
function checkId(id) {
|
||||||
return function (results) {
|
return function (results) {
|
||||||
|
@ -125,54 +109,49 @@ function checkId(id) {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
keyfetch.oidcJwk = function (id, iss) {
|
keyfetch.oidcJwk = async function (id, iss) {
|
||||||
return keyfetch._checkCache(id, iss).then(function (hit) {
|
var hit = await keyfetch._checkCache(id, iss);
|
||||||
if (hit) {
|
if (hit) {
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
return keyfetch.oidcJwks(iss).then(checkId(id));
|
return keyfetch.oidcJwks(iss).then(checkId(id));
|
||||||
});
|
|
||||||
};
|
};
|
||||||
keyfetch.wellKnownJwk = function (id, iss) {
|
keyfetch.wellKnownJwk = async function (id, iss) {
|
||||||
return keyfetch._checkCache(id, iss).then(function (hit) {
|
var hit = await keyfetch._checkCache(id, iss);
|
||||||
if (hit) {
|
if (hit) {
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
return keyfetch.wellKnownJwks(iss).then(checkId(id));
|
return keyfetch.wellKnownJwks(iss).then(checkId(id));
|
||||||
});
|
|
||||||
};
|
};
|
||||||
keyfetch.jwk = function (id, jwksUrl) {
|
keyfetch.jwk = async function (id, jwksUrl) {
|
||||||
return keyfetch._checkCache(id, jwksUrl).then(function (hit) {
|
var hit = await keyfetch._checkCache(id, jwksUrl);
|
||||||
if (hit) {
|
if (hit) {
|
||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
return keyfetch.jwks(jwksUrl).then(checkId(id));
|
return keyfetch.jwks(jwksUrl).then(checkId(id));
|
||||||
});
|
|
||||||
};
|
};
|
||||||
keyfetch._checkCache = function (id, iss) {
|
keyfetch._checkCache = async function (id, iss) {
|
||||||
return Promise.resolve().then(function () {
|
// We cache by thumbprint and (kid + '@' + iss),
|
||||||
// We cache by thumbprint and (kid + '@' + iss),
|
// so it's safe to check without appending the issuer
|
||||||
// so it's safe to check without appending the issuer
|
var hit = keyCache[id];
|
||||||
var hit = keyCache[id];
|
if (!hit) {
|
||||||
if (!hit) {
|
hit = keyCache[id + "@" + normalizeIss(iss)];
|
||||||
hit = keyCache[id + "@" + normalizeIss(iss)];
|
}
|
||||||
}
|
if (!hit) {
|
||||||
if (!hit) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var now = Math.round(Date.now() / 1000);
|
|
||||||
var left = hit.expiresAt - now;
|
|
||||||
// not guarding number checks since we know that we
|
|
||||||
// set 'now' and 'expiresAt' correctly elsewhere
|
|
||||||
if (left > staletime) {
|
|
||||||
return JSON.parse(JSON.stringify(hit));
|
|
||||||
}
|
|
||||||
if (left > 0) {
|
|
||||||
return JSON.parse(JSON.stringify(hit));
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
var now = Math.round(Date.now() / 1000);
|
||||||
|
var left = hit.expiresAt - now;
|
||||||
|
// not guarding number checks since we know that we
|
||||||
|
// set 'now' and 'expiresAt' correctly elsewhere
|
||||||
|
if (left > staletime) {
|
||||||
|
return JSON.parse(JSON.stringify(hit));
|
||||||
|
}
|
||||||
|
if (left > 0) {
|
||||||
|
return JSON.parse(JSON.stringify(hit));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
keyfetch._setCache = function (iss, cacheable) {
|
keyfetch._setCache = function (iss, cacheable) {
|
||||||
// force into a number
|
// force into a number
|
||||||
|
@ -227,143 +206,134 @@ keyfetch.jwt.decode = function (jwt) {
|
||||||
obj.claims = JSON.parse(Buffer.from(obj.payload, "base64"));
|
obj.claims = JSON.parse(Buffer.from(obj.payload, "base64"));
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
keyfetch.jwt.verify = function (jwt, opts) {
|
keyfetch.jwt.verify = async function (jwt, opts) {
|
||||||
if (!opts) {
|
if (!opts) {
|
||||||
opts = {};
|
opts = {};
|
||||||
}
|
}
|
||||||
return Promise.resolve().then(function () {
|
|
||||||
var decoded;
|
|
||||||
var exp;
|
|
||||||
var nbf;
|
|
||||||
var active;
|
|
||||||
var issuers = opts.issuers || [];
|
|
||||||
var err;
|
|
||||||
if (opts.iss) {
|
|
||||||
issuers.push(opts.iss);
|
|
||||||
}
|
|
||||||
if (opts.claims && opts.claims.iss) {
|
|
||||||
issuers.push(opts.claims.iss);
|
|
||||||
}
|
|
||||||
if (!issuers.length) {
|
|
||||||
err = new Error(
|
|
||||||
"\n[keyfetch.js] Security Warning:\nNeither of opts.issuers nor opts.iss were provided. The default behavior of setting opts.issuers = ['*'] by default - which allows any or no issuers - is deprecated and will throw an error instead in the next major version.\n"
|
|
||||||
);
|
|
||||||
console.warn(err);
|
|
||||||
issuers.push("*");
|
|
||||||
}
|
|
||||||
var claims = opts.claims || {};
|
|
||||||
if (!jwt || "string" === typeof jwt) {
|
|
||||||
try {
|
|
||||||
decoded = keyfetch.jwt.decode(jwt);
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error("could not parse jwt: '" + jwt + "'");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
decoded = jwt;
|
|
||||||
}
|
|
||||||
exp = decoded.claims.exp;
|
|
||||||
nbf = decoded.claims.nbf;
|
|
||||||
|
|
||||||
if (!issuers.some(isTrustedIssuer(decoded.claims.iss))) {
|
var decoded;
|
||||||
throw new Error("token was issued by an untrusted issuer: '" + decoded.claims.iss + "'");
|
var exp;
|
||||||
|
var nbf;
|
||||||
|
var active;
|
||||||
|
var issuers = opts.issuers || [];
|
||||||
|
if (opts.iss) {
|
||||||
|
issuers.push(opts.iss);
|
||||||
|
}
|
||||||
|
if (opts.claims && opts.claims.iss) {
|
||||||
|
issuers.push(opts.claims.iss);
|
||||||
|
}
|
||||||
|
if (!issuers.length) {
|
||||||
|
throw new 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/"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
var claims = opts.claims || {};
|
||||||
|
if (!jwt || "string" === typeof jwt) {
|
||||||
|
try {
|
||||||
|
decoded = keyfetch.jwt.decode(jwt);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("could not parse jwt: '" + jwt + "'");
|
||||||
}
|
}
|
||||||
// Note claims.iss validates more strictly than opts.issuers (requires exact match)
|
} else {
|
||||||
|
decoded = jwt;
|
||||||
|
}
|
||||||
|
exp = decoded.claims.exp;
|
||||||
|
nbf = decoded.claims.nbf;
|
||||||
|
|
||||||
|
if (!issuers.some(isTrustedIssuer(decoded.claims.iss))) {
|
||||||
|
throw new Error("token was issued by an untrusted issuer: '" + decoded.claims.iss + "'");
|
||||||
|
}
|
||||||
|
// Note claims.iss validates more strictly than opts.issuers (requires exact match)
|
||||||
|
if (
|
||||||
|
!Object.keys(claims).every(function (key) {
|
||||||
|
if (claims[key] === decoded.claims[key]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
throw new Error("token did not match on one or more authorization claims: '" + Object.keys(claims) + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
active = (opts.exp || 0) + parseInt(exp, 10) - Date.now() / 1000 > 0;
|
||||||
|
if (!active) {
|
||||||
|
// 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 + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nbf) {
|
||||||
|
active = parseInt(nbf, 10) - Date.now() / 1000 <= 0;
|
||||||
|
if (!active) {
|
||||||
|
throw new Error("token's 'nbf' has not been reached or could not parsed: '" + nbf + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opts.jwks || opts.jwk) {
|
||||||
|
return overrideLookup(opts.jwks || [opts.jwk]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var kid = decoded.header.kid;
|
||||||
|
var iss;
|
||||||
|
var fetcher;
|
||||||
|
var fetchOne;
|
||||||
|
if (!opts.strategy || "oidc" === opts.strategy) {
|
||||||
|
iss = decoded.claims.iss;
|
||||||
|
fetcher = keyfetch.oidcJwks;
|
||||||
|
fetchOne = keyfetch.oidcJwk;
|
||||||
|
} else if ("auth0" === opts.strategy || "well-known" === opts.strategy) {
|
||||||
|
iss = decoded.claims.iss;
|
||||||
|
fetcher = keyfetch.wellKnownJwks;
|
||||||
|
fetchOne = keyfetch.wellKnownJwk;
|
||||||
|
} else {
|
||||||
|
iss = opts.strategy;
|
||||||
|
fetcher = keyfetch.jwks;
|
||||||
|
fetchOne = keyfetch.jwk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kid) {
|
||||||
|
return fetchOne(kid, iss).then(verifyOne); //.catch(fetchAny);
|
||||||
|
}
|
||||||
|
return fetcher(iss).then(verifyAny);
|
||||||
|
|
||||||
|
function verifyOne(hit) {
|
||||||
|
if (true === keyfetch.jws.verify(decoded, hit)) {
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
throw new Error("token signature verification was unsuccessful");
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyAny(hits) {
|
||||||
if (
|
if (
|
||||||
!Object.keys(claims).every(function (key) {
|
hits.some(function (hit) {
|
||||||
if (claims[key] === decoded.claims[key]) {
|
if (kid) {
|
||||||
return true;
|
if (kid !== hit.jwk.kid && kid !== hit.thumbprint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (true === keyfetch.jws.verify(decoded, hit)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
throw new Error("token signature verification was unsuccessful");
|
||||||
|
} else {
|
||||||
|
if (true === keyfetch.jws.verify(decoded, hit)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
throw new Error("token did not match on one or more authorization claims: '" + Object.keys(claims) + "'");
|
return decoded;
|
||||||
}
|
}
|
||||||
|
throw new Error("Retrieved a list of keys, but none of them matched the 'kid' (key id) of the token.");
|
||||||
|
}
|
||||||
|
|
||||||
active = (opts.exp || 0) + parseInt(exp, 10) - Date.now() / 1000 > 0;
|
function overrideLookup(jwks) {
|
||||||
if (!active) {
|
return Promise.all(
|
||||||
// expiration was on the token or, if not, such a token is not allowed
|
jwks.map(async function (jwk) {
|
||||||
if (exp || false !== opts.exp) {
|
var Keypairs = jwk.x ? Eckles : Rasha;
|
||||||
throw new Error("token's 'exp' has passed or could not parsed: '" + exp + "'");
|
var pem = await Keypairs.export({ jwk: jwk });
|
||||||
}
|
var thumb = await Keypairs.thumbprint({ jwk: jwk });
|
||||||
}
|
return { jwk: jwk, pem: pem, thumbprint: thumb };
|
||||||
if (nbf) {
|
})
|
||||||
active = parseInt(nbf, 10) - Date.now() / 1000 <= 0;
|
).then(verifyAny);
|
||||||
if (!active) {
|
}
|
||||||
throw new Error("token's 'nbf' has not been reached or could not parsed: '" + nbf + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (opts.jwks || opts.jwk) {
|
|
||||||
return overrideLookup(opts.jwks || [opts.jwk]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var kid = decoded.header.kid;
|
|
||||||
var iss;
|
|
||||||
var fetcher;
|
|
||||||
var fetchOne;
|
|
||||||
if (!opts.strategy || "oidc" === opts.strategy) {
|
|
||||||
iss = decoded.claims.iss;
|
|
||||||
fetcher = keyfetch.oidcJwks;
|
|
||||||
fetchOne = keyfetch.oidcJwk;
|
|
||||||
} else if ("auth0" === opts.strategy || "well-known" === opts.strategy) {
|
|
||||||
iss = decoded.claims.iss;
|
|
||||||
fetcher = keyfetch.wellKnownJwks;
|
|
||||||
fetchOne = keyfetch.wellKnownJwk;
|
|
||||||
} else {
|
|
||||||
iss = opts.strategy;
|
|
||||||
fetcher = keyfetch.jwks;
|
|
||||||
fetchOne = keyfetch.jwk;
|
|
||||||
}
|
|
||||||
|
|
||||||
var p;
|
|
||||||
if (kid) {
|
|
||||||
p = fetchOne(kid, iss).then(verifyOne); //.catch(fetchAny);
|
|
||||||
} else {
|
|
||||||
p = fetcher(iss).then(verifyAny);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
|
|
||||||
function verifyOne(hit) {
|
|
||||||
if (true === keyfetch.jws.verify(decoded, hit)) {
|
|
||||||
return decoded;
|
|
||||||
}
|
|
||||||
throw new Error("token signature verification was unsuccessful");
|
|
||||||
}
|
|
||||||
|
|
||||||
function verifyAny(hits) {
|
|
||||||
if (
|
|
||||||
hits.some(function (hit) {
|
|
||||||
if (kid) {
|
|
||||||
if (kid !== hit.jwk.kid && kid !== hit.thumbprint) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (true === keyfetch.jws.verify(decoded, hit)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
throw new Error("token signature verification was unsuccessful");
|
|
||||||
} else {
|
|
||||||
if (true === keyfetch.jws.verify(decoded, hit)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
return decoded;
|
|
||||||
}
|
|
||||||
throw new Error("Retrieved a list of keys, but none of them matched the 'kid' (key id) of the token.");
|
|
||||||
}
|
|
||||||
|
|
||||||
function overrideLookup(jwks) {
|
|
||||||
return Promise.all(
|
|
||||||
jwks.map(function (jwk) {
|
|
||||||
var Keypairs = jwk.x ? Eckles : Rasha;
|
|
||||||
return Keypairs.export({ jwk: jwk }).then(function (pem) {
|
|
||||||
return Keypairs.thumbprint({ jwk: jwk }).then(function (thumb) {
|
|
||||||
return { jwk: jwk, pem: pem, thumbprint: thumb };
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
).then(verifyAny);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
keyfetch.jws = {};
|
keyfetch.jws = {};
|
||||||
keyfetch.jws.verify = function (jws, pub) {
|
keyfetch.jws.verify = function (jws, pub) {
|
||||||
|
@ -380,11 +350,10 @@ keyfetch._decode = function (jwt) {
|
||||||
var obj = keyfetch.jwt.decode(jwt);
|
var obj = keyfetch.jwt.decode(jwt);
|
||||||
return { header: obj.header, payload: obj.claims, signature: obj.signature };
|
return { header: obj.header, payload: obj.claims, signature: obj.signature };
|
||||||
};
|
};
|
||||||
keyfetch.verify = function (opts) {
|
keyfetch.verify = async function (opts) {
|
||||||
var jwt = opts.jwt;
|
var jwt = opts.jwt;
|
||||||
return keyfetch.jwt.verify(jwt, opts).then(function (obj) {
|
var obj = await keyfetch.jwt.verify(jwt, opts);
|
||||||
return { header: obj.header, payload: obj.claims, signature: obj.signature };
|
return { header: obj.header, payload: obj.claims, signature: obj.signature };
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function ecdsaJoseSigToAsn1Sig(header, b64sig) {
|
function ecdsaJoseSigToAsn1Sig(header, b64sig) {
|
||||||
|
|
Loading…
Reference in New Issue