mirror of
				https://github.com/therootcompany/keyfetch.js.git
				synced 2024-11-16 17:29:02 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			174 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # keyfetch
 | |
| 
 | |
| Lightweight support for fetching JWKs.
 | |
| 
 | |
| Fetches JSON native JWKs and exposes them as PEMs that can be consumed by the `jsonwebtoken` package
 | |
| (and node's native RSA and ECDSA crypto APIs).
 | |
| 
 | |
| ## Features
 | |
| 
 | |
| Works great for
 | |
| 
 | |
| * [x] `jsonwebtoken` (Auth0)
 | |
| * [x] OIDC (OpenID Connect)
 | |
| * [x] .well-known/jwks.json (Auth0)
 | |
| * [x] Other JWKs URLs
 | |
| 
 | |
| Crypto Support
 | |
| 
 | |
| * [x] JWT verification
 | |
| * [x] RSA (all variants)
 | |
| * [x] EC / ECDSA (NIST variants P-256, P-384)
 | |
| * [ ] esoteric variants (excluded to keep the code featherweight and secure)
 | |
| 
 | |
| # Install
 | |
| 
 | |
| ```bash
 | |
| npm install --save keyfetch
 | |
| ```
 | |
| 
 | |
| # Usage
 | |
| 
 | |
| Retrieve a key list of keys:
 | |
| 
 | |
| ```js
 | |
| var keyfetch = require('keyfetch');
 | |
| 
 | |
| keyfetch.oidcJwks("https://example.com/").then(function (results) {
 | |
|   results.forEach(function (result) {
 | |
|     console.log(result.jwk);
 | |
|     console.log(result.thumprint);
 | |
|     console.log(result.pem);
 | |
|   });
 | |
| });
 | |
| ```
 | |
| 
 | |
| Quick JWT verification:
 | |
| 
 | |
| ```js
 | |
| var keyfetch = require('keyfetch');
 | |
| var jwt = '...';
 | |
| 
 | |
| keyfetch.verify({ jwt: jwt }).then(function (decoded) {
 | |
|   console.log(decoded);
 | |
| });
 | |
| ```
 | |
| 
 | |
| Verify a JWT with `jsonwebtoken`:
 | |
| 
 | |
| ```js
 | |
| var keyfetch = require('keyfetch');
 | |
| var jwt = require('jsonwebtoken');
 | |
| var auth = "..."; // some JWT
 | |
| var token = jwt.decode(auth, { json: true, complete: true })
 | |
| 
 | |
| if (!isTrustedIssuer(token.payload.iss)) {
 | |
|   throw new Error("untrusted issuer");
 | |
| }
 | |
| 
 | |
| keyfetch.oidcJwk(
 | |
|   token.header.kid
 | |
| , token.payload.iss
 | |
| ).then(function (result) {
 | |
|   console.log(result.jwk);
 | |
|   console.log(result.thumprint);
 | |
|   console.log(result.pem);
 | |
| 
 | |
|   jwt.verify(jwt, pem);
 | |
| });
 | |
| ```
 | |
| 
 | |
| *Note*: You might implement `isTrustedIssuer` one of these:
 | |
| 
 | |
| ```js
 | |
| function isTrustedIssuer(iss) {
 | |
|   return -1 !== [ 'https://partner.com/', 'https://auth0.com/'].indexOf(iss);
 | |
| }
 | |
| ```
 | |
| 
 | |
| ```js
 | |
| function isTrustedIssuer(iss) {
 | |
|   return /^https:/.test(iss) &&         // must be a secure domain
 | |
|     /(\.|^)example\.com$/.test(iss);    // can be example.com or any subdomain
 | |
| }
 | |
| ```
 | |
| 
 | |
| # API
 | |
| 
 | |
| All API calls will return the RFC standard JWK SHA256 thumbprint as well as a PEM version of the key.
 | |
| 
 | |
| Note: When specifying `id`, it may be either `kid` (as in `token.header.kid`)
 | |
| or `thumbprint` (as in `result.thumbprint`).
 | |
| 
 | |
| ### JWKs URLs
 | |
| 
 | |
| Retrieves keys from a URL such as `https://example.com/jwks/` with the format `{ keys: [ { kid, kty, exp, ... } ] }`
 | |
| and returns the array of keys (as well as thumbprint and jwk-to-pem).
 | |
| 
 | |
| ```js
 | |
| keyfetch.jwks(jwksUrl)
 | |
| // Promises [ { jwk, thumbprint, pem } ] or fails
 | |
| ```
 | |
| 
 | |
| ```js
 | |
| keyfetch.jwk(id, jwksUrl)
 | |
| // Promises { jwk, thumbprint, pem } or fails
 | |
| ```
 | |
| 
 | |
| ### Auth0
 | |
| 
 | |
| If `https://example.com/` is used as `issuerUrl` it will resolve to
 | |
| `https://example.com/.well-known/jwks.json` and return the keys.
 | |
| 
 | |
| ```js
 | |
| keyfetch.wellKnownJwks(issuerUrl)
 | |
| // Promises [ { jwk, thumbprint, pem } ] or fails
 | |
| ```
 | |
| 
 | |
| ```js
 | |
| keyfetch.wellKnownJwk(id, issuerUrl)
 | |
| // Promises { jwk, thumbprint, pem } or fails
 | |
| ```
 | |
| 
 | |
| ### OIDC
 | |
| 
 | |
| If `https://example.com/` is used as `issuerUrl` then it will first resolve to
 | |
| `https://example.com/.well-known/openid-configuration` and then follow `jwks_uri` to return the keys.
 | |
| 
 | |
| ```js
 | |
| keyfetch.oidcJwks(issuerUrl)
 | |
| // Promises [ { jwk, thumbprint, pem } ] or fails
 | |
| ```
 | |
| 
 | |
| ```js
 | |
| keyfetch.oidcJwk(id, issuerUrl)
 | |
| // Promises { jwk, thumbprint, pem } or fails
 | |
| ```
 | |
| 
 | |
| ### Verify JWT
 | |
| 
 | |
| ```js
 | |
| keyfetch.verify({ jwt: jwk, strategy: 'oidc' })
 | |
| // Promises a decoded JWT { headers, payload, signature } or fails
 | |
| ```
 | |
| 
 | |
| * `strategy` may be `oidc` (default) , `auth0`, or a direct JWKs url.
 | |
| 
 | |
| ### Cache Settings
 | |
| 
 | |
| ```js
 | |
| keyfetch.init({
 | |
|   // set all keys at least 1 hour (regardless of jwk.exp)
 | |
|   mincache: 1 * 60 * 60
 | |
| 
 | |
|   // expire each key after 3 days (regardless of jwk.exp)
 | |
| , maxcache: 3 * 24 * 60 * 60
 | |
| 
 | |
|   // re-fetch a key up to 15 minutes before it expires (only if used)
 | |
| , staletime: 15 * 60
 | |
| })
 | |
| ```
 | |
| 
 | |
| There is no background task to cleanup expired keys as of yet.
 | |
| For now you can limit the number of keys fetched by having a simple whitelist.
 |