Gathering detailed insights and metrics for keyfetch
Gathering detailed insights and metrics for keyfetch
Gathering detailed insights and metrics for keyfetch
Gathering detailed insights and metrics for keyfetch
npm install keyfetch
Typescript
Module System
Node Version
NPM Version
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
3
1
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).
Works great for
jsonwebtoken
(Auth0)Crypto Support
1npm install --save keyfetch
Retrieve a key list of keys:
1var keyfetch = require("keyfetch"); 2 3keyfetch.oidcJwks("https://example.com/").then(function (results) { 4 results.forEach(function (result) { 5 console.log(result.jwk); 6 console.log(result.thumprint); 7 console.log(result.pem); 8 }); 9});
Quick JWT verification (for authentication):
1var keyfetch = require("keyfetch"); 2var jwt = "..."; 3 4keyfetch.jwt.verify(jwt).then(function (decoded) { 5 console.log(decoded); 6});
JWT verification (for authorization):
1var options = { issuers: ["https://example.com/"], claims: { role: "admin" } }; 2keyfetch.jwt.verify(jwt, options).then(function (decoded) { 3 console.log(decoded); 4});
Verify a JWT with jsonwebtoken
:
1var keyfetch = require("keyfetch"); 2var jwt = require("jsonwebtoken"); 3var auth = "..."; // some JWT 4var token = jwt.decode(auth, { json: true, complete: true }); 5 6if (!isTrustedIssuer(token.payload.iss)) { 7 throw new Error("untrusted issuer"); 8} 9 10keyfetch.oidcJwk(token.header.kid, token.payload.iss).then(function (result) { 11 console.log(result.jwk); 12 console.log(result.thumprint); 13 console.log(result.pem); 14 15 jwt.jwt.verify(jwt, { jwk: result.jwk }); 16});
Note: You might implement isTrustedIssuer
one of these:
1function isTrustedIssuer(iss) { 2 return -1 !== ["https://partner.com/", "https://auth0.com/"].indexOf(iss); 3}
1function isTrustedIssuer(iss) { 2 return ( 3 /^https:/.test(iss) && /(\.|^)example\.com$/.test(iss) // must be a secure domain 4 ); // can be example.com or any subdomain 5}
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
).
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).
1keyfetch.jwks(jwksUrl); 2// Promises [ { jwk, thumbprint, pem } ] or fails
1keyfetch.jwk(id, jwksUrl); 2// Promises { jwk, thumbprint, pem } or fails
If https://example.com/
is used as issuerUrl
it will resolve to
https://example.com/.well-known/jwks.json
and return the keys.
1keyfetch.wellKnownJwks(issuerUrl); 2// Promises [ { jwk, thumbprint, pem } ] or fails
1keyfetch.wellKnownJwk(id, issuerUrl);
2// Promises { jwk, thumbprint, pem } or fails
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.
1keyfetch.oidcJwks(issuerUrl); 2// Promises [ { jwk, thumbprint, pem } ] or fails
1keyfetch.oidcJwk(id, issuerUrl);
2// Promises { jwk, thumbprint, pem } or fails
This can accept a JWT string (compact JWS) or a decoded JWT object (JWS).
This can be used purely for verifying pure authentication tokens, as well as authorization tokens.
1keyfetch.jwt.verify(jwt, { strategy: "oidc" }).then(function (verified) { 2 /* 3 { protected: '...' // base64 header 4 , payload: '...' // base64 payload 5 , signature: '...' // base64 signature 6 , header: {...} // decoded header 7 , claims: {...} // decoded payload 8 } 9 */ 10});
When used for authorization, it's important to specify a limited set of trusted issuers
.
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.
1keyfetch.jwt.verify(jwt, { 2 strategy: 'oidc', 3 issuers: [ 'https://example.com/' ], 4 //iss: 'https://example.com/', 5 claims: { role: 'admin', sub: 'abc', group: 'xyz' } 6}).then(function (verified) {
strategy
may be oidc
(default) , auth0
, or a direct JWKs url.issuers
must be a list of https urls (though http is allowed for things like Docker swarm), or '*'iss
is like issuers
, but only oneclaims
is an object with arbitrary keys (i.e. everything except for the standard iat
, exp
, jti
, etc)exp
may be set to false
if you're validating on your own (i.e. allowing time drift leeway)jwks
can be used to specify a list of allowed public key rather than fetching them (i.e. for offline unit tests)jwk
same as above, but a single key rather than a list1try { 2 console.log( keyfetch.jwt.decode(jwt) ); 3} catch(e) { 4 console.error(e); 5}
1{ protected: '...' // base64 header 2, payload: '...' // base64 payload 3, signature: '...' // base64 signature 4, header: {...} // decoded header 5, claims: {...} // decoded payload
It's easier just to show the code than to explain the example.
1keyfetch.jwt.decode = function (jwt) { 2 // Unpack JWS from "compact" form 3 var parts = jwt.split("."); 4 var obj = { 5 protected: parts[0], 6 payload: parts[1], 7 signature: parts[2] 8 }; 9 10 // Decode JWT properties from JWS as unordered objects 11 obj.header = JSON.parse(Buffer.from(obj.protected, "base64")); 12 obj.claims = JSON.parse(Buffer.from(obj.payload, "base64")); 13 14 return obj; 15};
1keyfetch.init({ 2 // set all keys at least 1 hour (regardless of jwk.exp) 3 mincache: 1 * 60 * 60, 4 5 // expire each key after 3 days (regardless of jwk.exp) 6 maxcache: 3 * 24 * 60 * 60, 7 8 // re-fetch a key up to 15 minutes before it expires (only if used) 9 staletime: 15 * 60 10});
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.
JSON.stringify()
d errors look like this:
1{ 2 code: "INVALID_JWT", 3 status: 401, 4 details: [ "jwt.claims.exp = 1634804500", "DEBUG: helpful message" ] 5 message: "token's 'exp' has passed or could not parsed: 1634804500" 6}
SemVer Compatibility:
code
& status
will remain the same.message
is NOT included in the semver compatibility guarantee (we intend to make them more client-friendly), neither is detail
at this time (but it will be once we decide on what it should be).details
may be added to, but not subtracted fromHint | Code | Status | Message (truncated) |
---|---|---|---|
bad gateway | BAD_GATEWAY | 502 | The auth token could not be verified because our se... |
insecure issuer | MALFORMED_JWT | 400 | The auth token could not be verified because our se... |
parse error | MALFORMED_JWT | 400 | The auth token could not be verified because it is ... |
no issuer | MALFORMED_JWT | 400 | The auth token could not be verified because it doe... |
malformed exp | MALFORMED_JWT | 400 | The auth token could not be verified because it's e... |
expired | INVALID_JWT | 401 | The auth token is expired. To try again, go to the ... |
inactive | INVALID_JWT | 401 | The auth token isn't valid yet. It's activation dat... |
bad signature | INVALID_JWT | 401 | The auth token did not pass verification because it... |
jwk not found old | INVALID_JWT | 401 | The auth token did not pass verification because ou... |
jwk not found | INVALID_JWT | 401 | The auth token did not pass verification because ou... |
no jwkws uri | INVALID_JWT | 401 | The auth token did not pass verification because it... |
unknown issuer | INVALID_JWT | 401 | The auth token did not pass verification because it... |
failed claims | INVALID_JWT | 401 | The auth token did not pass verification because it... |
Minor Breaking changes (with a major version bump):
client_message
)let
and template strings (drops really old node compat)issuers = ["*"]
to requiring that an issuer (or public jwk for verification) is specifiedSee other changes in CHANGELOG.md.
No vulnerabilities found.
No security vulnerabilities found.