mirror of
				https://github.com/therootcompany/keyfetch.js.git
				synced 2024-11-16 17:29:02 +00:00 
			
		
		
		
	v1.1.8: cleanup and support forced keys for verification
This commit is contained in:
		
							parent
							
								
									448b977963
								
							
						
					
					
						commit
						5fef6a7430
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | node_modules | ||||||
| @ -1,14 +1,14 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
| 
 | 
 | ||||||
| var keyfetch = require('./keyfetch.js'); | var keyfetch = require('./keyfetch.js'); | ||||||
| var testUrl = "https://example.auth0.com"; | var testIss = "https://example.auth0.com"; | ||||||
| 
 | 
 | ||||||
| keyfetch.init({}); | keyfetch.init({}); | ||||||
| keyfetch.oidcJwks().then(function (jwks) { | keyfetch.oidcJwks(testIss).then(function (hits) { | ||||||
|   keyfetch._clear(); |   keyfetch._clear(); | ||||||
|   console.log(jwks); |   console.log(hits); | ||||||
|   return keyfetch.oidcJwk(jwks[0].thumbprint, "https://example.auth0.com").then(function () { |   return keyfetch.oidcJwk(hits[0].thumbprint, testIss).then(function () { | ||||||
|     return keyfetch.oidcJwk(jwks[0].thumbprint, "https://example.auth0.com").then(function (jwk) { |     return keyfetch.oidcJwk(hits[0].thumbprint, testIss).then(function (jwk) { | ||||||
|       console.log(jwk); |       console.log(jwk); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | |||||||
							
								
								
									
										88
									
								
								keyfetch.js
									
									
									
									
									
								
							
							
						
						
									
										88
									
								
								keyfetch.js
									
									
									
									
									
								
							| @ -236,6 +236,22 @@ keyfetch.verify = function (opts) { | |||||||
|         throw new Error("token's 'nbf' has not been reached or could not parsed: '" + nbf + "'"); |         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]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     var kid = decoded.header.kid; |     var kid = decoded.header.kid; | ||||||
|     var iss; |     var iss; | ||||||
|     var fetcher; |     var fetcher; | ||||||
| @ -254,17 +270,48 @@ keyfetch.verify = function (opts) { | |||||||
|       fetchOne = keyfetch.jwk; |       fetchOne = keyfetch.jwk; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function verify(jwk, payload) { |     var payload = jwt.split('.')[1]; // as string, as it was signed
 | ||||||
|  |     if (kid) { | ||||||
|  |       return fetchOne(kid, iss).then(verifyOne); //.catch(fetchAny);
 | ||||||
|  |     } else { | ||||||
|  |       return fetcher(iss).then(verifyAny); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function verify(hit, payload) { | ||||||
|       var alg = 'SHA' + decoded.header.alg.replace(/[^\d]+/i, ''); |       var alg = 'SHA' + decoded.header.alg.replace(/[^\d]+/i, ''); | ||||||
|       var sig = convertIfEcdsa(decoded.header, decoded.signature); |       var sig = ecdsaAsn1SigToJwtSig(decoded.header, decoded.signature); | ||||||
|       return require('crypto') |       return require('crypto') | ||||||
|         .createVerify(alg) |         .createVerify(alg) | ||||||
|         .update(jwt.split('.')[0] + '.' + payload) |         .update(jwt.split('.')[0] + '.' + payload) | ||||||
|         .verify(jwk.pem, sig, 'base64') |         .verify(hit.pem, sig, 'base64') | ||||||
|       ; |       ; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     function convertIfEcdsa(header, b64sig) { |     function verifyOne(hit) { | ||||||
|  |       if (true === verify(hit, payload)) { | ||||||
|  |         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 === verify(hit, payload)) { return true; } | ||||||
|  |           throw new Error('token signature verification was unsuccessful'); | ||||||
|  |         } else { | ||||||
|  |           if (true === verify(hit, payload)) { 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 ecdsaAsn1SigToJwtSig(header, b64sig) { | ||||||
|   // ECDSA JWT signatures differ from "normal" ECDSA signatures
 |   // ECDSA JWT signatures differ from "normal" ECDSA signatures
 | ||||||
|   // https://tools.ietf.org/html/rfc7518#section-3.4
 |   // https://tools.ietf.org/html/rfc7518#section-3.4
 | ||||||
|   if (!/^ES/i.test(header.alg)) { return b64sig; } |   if (!/^ES/i.test(header.alg)) { return b64sig; } | ||||||
| @ -299,36 +346,3 @@ keyfetch.verify = function (opts) { | |||||||
|     .replace(/=/g, '') |     .replace(/=/g, '') | ||||||
|   ; |   ; | ||||||
| } | } | ||||||
| 
 |  | ||||||
|     var payload = jwt.split('.')[1]; // as string, as it was signed
 |  | ||||||
|     if (kid) { |  | ||||||
|       return fetchOne(kid, iss).then(verifyOne); //.catch(fetchAny);
 |  | ||||||
|     } else { |  | ||||||
|       return fetchAny(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     function verifyOne(jwk) { |  | ||||||
|       if (true === verify(jwk, payload)) { |  | ||||||
|         return decoded; |  | ||||||
|       } |  | ||||||
|       throw new Error('token signature verification was unsuccessful'); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     function fetchAny() { |  | ||||||
|       return fetcher(iss).then(function (jwks) { |  | ||||||
|         if (jwks.some(function (jwk) { |  | ||||||
|           if (kid) { |  | ||||||
|             if (kid !== jwk.kid && kid !== jwk.thumbprint) { return; } |  | ||||||
|             if (true === verify(jwk, payload)) { return true; } |  | ||||||
|             throw new Error('token signature verification was unsuccessful'); |  | ||||||
|           } else { |  | ||||||
|             if (true === verify(jwk, payload)) { return true; } |  | ||||||
|           } |  | ||||||
|         })) { |  | ||||||
|           return decoded; |  | ||||||
|         } |  | ||||||
|         throw new Error("Retrieved a list of keys, but none of them matched the 'kid' (key id) of the token."); |  | ||||||
|       }); |  | ||||||
|     } |  | ||||||
|   }); |  | ||||||
| }; |  | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "keyfetch", |   "name": "keyfetch", | ||||||
|   "version": "1.1.0", |   "version": "1.1.8", | ||||||
|   "lockfileVersion": 1, |   "lockfileVersion": 1, | ||||||
|   "requires": true, |   "requires": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| { "author": { | { | ||||||
|  |   "author": { | ||||||
|     "name": "AJ ONeal", |     "name": "AJ ONeal", | ||||||
|     "email": "solderjs@gmail.com" |     "email": "solderjs@gmail.com" | ||||||
|   }, |   }, | ||||||
| @ -29,5 +30,5 @@ | |||||||
|   "scripts": { |   "scripts": { | ||||||
|     "test": "echo \"Error: no test specified\" && exit 1" |     "test": "echo \"Error: no test specified\" && exit 1" | ||||||
|   }, |   }, | ||||||
|   "version": "1.1.7" |   "version": "1.1.8" | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user