mirror of
				https://github.com/therootcompany/keyfetch.js.git
				synced 2024-11-16 17:29:02 +00:00 
			
		
		
		
	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user