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'; | ||||
| 
 | ||||
| var keyfetch = require('./keyfetch.js'); | ||||
| var testUrl = "https://example.auth0.com"; | ||||
| var testIss = "https://example.auth0.com"; | ||||
| 
 | ||||
| keyfetch.init({}); | ||||
| keyfetch.oidcJwks().then(function (jwks) { | ||||
| keyfetch.oidcJwks(testIss).then(function (hits) { | ||||
|   keyfetch._clear(); | ||||
|   console.log(jwks); | ||||
|   return keyfetch.oidcJwk(jwks[0].thumbprint, "https://example.auth0.com").then(function () { | ||||
|     return keyfetch.oidcJwk(jwks[0].thumbprint, "https://example.auth0.com").then(function (jwk) { | ||||
|   console.log(hits); | ||||
|   return keyfetch.oidcJwk(hits[0].thumbprint, testIss).then(function () { | ||||
|     return keyfetch.oidcJwk(hits[0].thumbprint, testIss).then(function (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 + "'"); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     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 iss; | ||||
|     var fetcher; | ||||
| @ -254,17 +270,48 @@ keyfetch.verify = function (opts) { | ||||
|       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 sig = convertIfEcdsa(decoded.header, decoded.signature); | ||||
|       var sig = ecdsaAsn1SigToJwtSig(decoded.header, decoded.signature); | ||||
|       return require('crypto') | ||||
|         .createVerify(alg) | ||||
|         .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
 | ||||
|   // https://tools.ietf.org/html/rfc7518#section-3.4
 | ||||
|   if (!/^ES/i.test(header.alg)) { return b64sig; } | ||||
| @ -299,36 +346,3 @@ keyfetch.verify = function (opts) { | ||||
|     .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", | ||||
|   "version": "1.1.0", | ||||
|   "version": "1.1.8", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| { "author": { | ||||
| { | ||||
|   "author": { | ||||
|     "name": "AJ ONeal", | ||||
|     "email": "solderjs@gmail.com" | ||||
|   }, | ||||
| @ -29,5 +30,5 @@ | ||||
|   "scripts": { | ||||
|     "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