Compare commits
9 Commits
523a4f0d1a
...
a84e833571
Author | SHA1 | Date |
---|---|---|
AJ ONeal | a84e833571 | |
AJ ONeal | 076246e4d0 | |
AJ ONeal | 4c85fc4009 | |
AJ ONeal | d5647ea905 | |
AJ ONeal | 2ea44e3a46 | |
AJ ONeal | 5ef53ecb23 | |
AJ ONeal | 604b42c7ef | |
AJ ONeal | c57a08f0cd | |
AJ ONeal | a3539b0941 |
|
@ -0,0 +1,11 @@
|
|||
# v3.0.0
|
||||
|
||||
**Breaking Change**: Standardize error `message`s (now they're more client-friendly).
|
||||
|
||||
# v2.1.0
|
||||
|
||||
Feature: Add `code`, `status`, and `details` to errors.
|
||||
|
||||
# v2.0.0
|
||||
|
||||
**Breaking Change**: require `issuers` array (rather than `["*"]` by default).
|
60
README.md
60
README.md
|
@ -11,7 +11,7 @@ Works great for
|
|||
|
||||
- [x] `jsonwebtoken` (Auth0)
|
||||
- [x] OIDC (OpenID Connect)
|
||||
- [x] .well-known/jwks.json (Auth0)
|
||||
- [x] .well-known/jwks.json (Auth0, Okta)
|
||||
- [x] Other JWKs URLs
|
||||
|
||||
Crypto Support
|
||||
|
@ -19,8 +19,19 @@ Crypto Support
|
|||
- [x] JWT verification
|
||||
- [x] RSA (all variants)
|
||||
- [x] EC / ECDSA (NIST variants P-256, P-384)
|
||||
- [x] Sane error codes
|
||||
- [ ] esoteric variants (excluded to keep the code featherweight and secure)
|
||||
|
||||
# Table of Contents
|
||||
|
||||
- Install
|
||||
- Usage
|
||||
- API
|
||||
- Auth0 / Okta
|
||||
- OIDC
|
||||
- Errors
|
||||
- Change Log
|
||||
|
||||
# Install
|
||||
|
||||
```bash
|
||||
|
@ -248,3 +259,50 @@ keyfetch.init({
|
|||
|
||||
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.
|
||||
|
||||
# Errors
|
||||
|
||||
`JSON.stringify()`d errors look like this:
|
||||
|
||||
```js
|
||||
{
|
||||
code: "INVALID_JWT",
|
||||
status: 401,
|
||||
details: [ "jwt.claims.exp = 1634804500", "DEBUG: helpful message" ]
|
||||
message: "token's 'exp' has passed or could not parsed: 1634804500"
|
||||
}
|
||||
```
|
||||
|
||||
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 from
|
||||
|
||||
| Hint | 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... |
|
||||
|
||||
# Change Log
|
||||
|
||||
Minor Breaking changes (with a major version bump):
|
||||
|
||||
- v3.0.0
|
||||
- reworked error messages (also available in v2.1.0 as `client_message`)
|
||||
- started using `let` and template strings (drops _really_ old node compat)
|
||||
- v2.0.0
|
||||
- changes from the default `issuers = ["*"]` to requiring that an issuer (or public jwk for verification) is specified
|
||||
|
||||
See other changes in [CHANGELOG.md](./CHANGELOG.md).
|
||||
|
|
76
keyfetch.js
76
keyfetch.js
|
@ -19,7 +19,7 @@ async function requestAsync(req) {
|
|||
|
||||
// differentiate potentially temporary server errors from 404
|
||||
if (!resp.ok && (resp.statusCode >= 500 || resp.statusCode < 200)) {
|
||||
throw Errors.BAD_GATEWAY();
|
||||
throw Errors.BAD_GATEWAY({ response: resp });
|
||||
}
|
||||
|
||||
return resp;
|
||||
|
@ -37,6 +37,8 @@ function checkMinDefaultMax(opts, key, n, d, x) {
|
|||
}
|
||||
}
|
||||
|
||||
keyfetch._errors = Errors;
|
||||
|
||||
keyfetch._clear = function () {
|
||||
keyCache = {};
|
||||
};
|
||||
|
@ -46,14 +48,15 @@ keyfetch.init = function (opts) {
|
|||
staletime = checkMinDefaultMax(opts, "staletime", 1 * 60, staletime, 31 * 24 * 60 * 60);
|
||||
};
|
||||
keyfetch._oidc = async function (iss) {
|
||||
var url = normalizeIss(iss) + "/.well-known/openid-configuration";
|
||||
var resp = await requestAsync({
|
||||
url: normalizeIss(iss) + "/.well-known/openid-configuration",
|
||||
url: url,
|
||||
json: true
|
||||
});
|
||||
|
||||
var oidcConf = resp.body;
|
||||
if (!oidcConf.jwks_uri) {
|
||||
throw Errors.OIDC_CONFIG_NOT_FOUND();
|
||||
throw Errors.NO_JWKS_URI(url);
|
||||
}
|
||||
return oidcConf;
|
||||
};
|
||||
|
@ -193,7 +196,7 @@ keyfetch._setCache = function (iss, cacheable) {
|
|||
|
||||
function normalizeIss(iss) {
|
||||
if (!iss) {
|
||||
throw Errors.TOKEN_NO_ISSUER();
|
||||
throw Errors.NO_ISSUER();
|
||||
}
|
||||
|
||||
// We definitely don't want false negatives stemming
|
||||
|
@ -217,7 +220,7 @@ keyfetch.jwt.decode = function (jwt) {
|
|||
obj.claims = JSON.parse(Buffer.from(obj.payload, "base64"));
|
||||
return obj;
|
||||
} catch (e) {
|
||||
var err = Errors.TOKEN_PARSE_ERROR(jwt);
|
||||
var err = Errors.PARSE_ERROR(jwt);
|
||||
err.details = e.message;
|
||||
throw err;
|
||||
}
|
||||
|
@ -227,7 +230,7 @@ keyfetch.jwt.verify = async function (jwt, opts) {
|
|||
opts = {};
|
||||
}
|
||||
|
||||
var decoded;
|
||||
var jws;
|
||||
var exp;
|
||||
var nbf;
|
||||
var active;
|
||||
|
@ -249,60 +252,68 @@ keyfetch.jwt.verify = async function (jwt, opts) {
|
|||
}
|
||||
var claims = opts.claims || {};
|
||||
if (!jwt || "string" === typeof jwt) {
|
||||
decoded = keyfetch.jwt.decode(jwt);
|
||||
jws = keyfetch.jwt.decode(jwt);
|
||||
} else {
|
||||
decoded = jwt;
|
||||
jws = jwt;
|
||||
}
|
||||
|
||||
if (!decoded.claims.iss || !issuers.some(isTrustedIssuer(decoded.claims.iss))) {
|
||||
if (!jws.claims.iss || !issuers.some(isTrustedIssuer(jws.claims.iss))) {
|
||||
if (!(opts.jwk || opts.jwks)) {
|
||||
throw Errors.ISSUER_NOT_TRUSTED(decoded.claims.iss || "");
|
||||
throw Errors.UNKNOWN_ISSUER(jws.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]) {
|
||||
var failedClaims = Object.keys(claims)
|
||||
.filter(function (key) {
|
||||
if (claims[key] !== jws.claims[key]) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
) {
|
||||
throw Errors.CLAIMS_MISMATCH(Object.keys(claims));
|
||||
.map(function (key) {
|
||||
return "jwt.claims." + key + " = " + JSON.stringify(jws.claims[key]);
|
||||
});
|
||||
if (failedClaims.length) {
|
||||
throw Errors.FAILED_CLAIMS(failedClaims, Object.keys(claims));
|
||||
}
|
||||
|
||||
exp = decoded.claims.exp;
|
||||
exp = jws.claims.exp;
|
||||
if (exp && false !== opts.exp) {
|
||||
now = Date.now();
|
||||
// TODO document that opts.exp can be used as leeway? Or introduce opts.leeway?
|
||||
// fair, but not necessary
|
||||
exp = parseInt(exp, 10);
|
||||
if (isNaN(exp)) {
|
||||
throw Errors.MALFORMED_EXP(JSON.stringify(jws.claims.exp));
|
||||
}
|
||||
then = (opts.exp || 0) + parseInt(exp, 10);
|
||||
active = then - now / 1000 > 0;
|
||||
// expiration was on the token or, if not, such a token is not allowed
|
||||
if (!active) {
|
||||
throw Errors.TOKEN_EXPIRED(exp);
|
||||
throw Errors.EXPIRED(exp);
|
||||
}
|
||||
}
|
||||
|
||||
nbf = decoded.claims.nbf;
|
||||
nbf = jws.claims.nbf;
|
||||
if (nbf) {
|
||||
active = parseInt(nbf, 10) - Date.now() / 1000 <= 0;
|
||||
if (!active) {
|
||||
throw Errors.TOKEN_INACTIVE(nbf);
|
||||
throw Errors.INACTIVE(nbf);
|
||||
}
|
||||
}
|
||||
if (opts.jwks || opts.jwk) {
|
||||
return overrideLookup(opts.jwks || [opts.jwk]);
|
||||
}
|
||||
|
||||
var kid = decoded.header.kid;
|
||||
var kid = jws.header.kid;
|
||||
var iss;
|
||||
var fetcher;
|
||||
var fetchOne;
|
||||
if (!opts.strategy || "oidc" === opts.strategy) {
|
||||
iss = decoded.claims.iss;
|
||||
iss = jws.claims.iss;
|
||||
fetcher = keyfetch.oidcJwks;
|
||||
fetchOne = keyfetch.oidcJwk;
|
||||
} else if ("auth0" === opts.strategy || "well-known" === opts.strategy) {
|
||||
iss = decoded.claims.iss;
|
||||
iss = jws.claims.iss;
|
||||
fetcher = keyfetch.wellKnownJwks;
|
||||
fetchOne = keyfetch.wellKnownJwk;
|
||||
} else {
|
||||
|
@ -317,10 +328,10 @@ keyfetch.jwt.verify = async function (jwt, opts) {
|
|||
return fetcher(iss).then(verifyAny);
|
||||
|
||||
function verifyOne(hit) {
|
||||
if (true === keyfetch.jws.verify(decoded, hit)) {
|
||||
return decoded;
|
||||
if (true === keyfetch.jws.verify(jws, hit)) {
|
||||
return jws;
|
||||
}
|
||||
throw Errors.TOKEN_INVALID_SIGNATURE();
|
||||
throw Errors.BAD_SIGNATURE(jws.protected + "." + jws.payload + "." + jws.signature);
|
||||
}
|
||||
|
||||
function verifyAny(hits) {
|
||||
|
@ -330,20 +341,19 @@ keyfetch.jwt.verify = async function (jwt, opts) {
|
|||
if (kid !== hit.jwk.kid && kid !== hit.thumbprint) {
|
||||
return;
|
||||
}
|
||||
if (true === keyfetch.jws.verify(decoded, hit)) {
|
||||
return true;
|
||||
}
|
||||
throw Errors.TOKEN_INVALID_SIGNATURE();
|
||||
} else {
|
||||
if (true === keyfetch.jws.verify(decoded, hit)) {
|
||||
if (true === keyfetch.jws.verify(jws, hit)) {
|
||||
return true;
|
||||
}
|
||||
throw Errors.BAD_SIGNATURE();
|
||||
}
|
||||
if (true === keyfetch.jws.verify(jws, hit)) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
) {
|
||||
return decoded;
|
||||
return jws;
|
||||
}
|
||||
throw Errors.TOKEN_UNKNOWN_SIGNER();
|
||||
throw Errors.JWK_NOT_FOUND_OLD(kid);
|
||||
}
|
||||
|
||||
function overrideLookup(jwks) {
|
||||
|
|
206
lib/errors.js
206
lib/errors.js
|
@ -19,30 +19,47 @@
|
|||
* }} opts
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
function create(msg, { status = 401, code = "", details }) {
|
||||
function create(old, msg, code, status, details) {
|
||||
/** @type AuthError */
|
||||
//@ts-ignore
|
||||
var err = new Error(msg);
|
||||
err.message = err.message;
|
||||
err.status = status;
|
||||
let err = new Error(msg);
|
||||
err.message = msg;
|
||||
err._old_message = old;
|
||||
err.code = code;
|
||||
err.status = status;
|
||||
if (details) {
|
||||
err.details = details;
|
||||
}
|
||||
err.source = "keyfetch";
|
||||
err.toJSON = toJSON;
|
||||
err.toString = toString;
|
||||
return err;
|
||||
}
|
||||
|
||||
function toJSON() {
|
||||
/*jshint validthis:true*/
|
||||
return {
|
||||
message: this.message,
|
||||
status: this.status,
|
||||
code: this.code,
|
||||
details: this.details
|
||||
};
|
||||
}
|
||||
function toString() {
|
||||
/*jshint validthis:true*/
|
||||
return this.stack + "\n" + JSON.stringify(this);
|
||||
}
|
||||
|
||||
// DEVELOPER_ERROR - a good token won't make a difference
|
||||
var E_DEVELOPER = "DEVELOPER_ERROR";
|
||||
|
||||
// BAD_GATEWAY - there may be a temporary error fetching the public or or whatever
|
||||
var E_BAD_GATEWAY = "BAD_GATEWAY";
|
||||
|
||||
// MALFORMED_TOKEN - the token could not be verified - not parsable, missing claims, etc
|
||||
// MALFORMED_JWT - the token could not be verified - not parsable, missing claims, etc
|
||||
var E_MALFORMED = "MALFORMED_JWT";
|
||||
|
||||
// INVALID_TOKEN - the token's properties don't meet requirements - iss, claims, sig, exp
|
||||
// INVALID_JWT - the token's properties don't meet requirements - iss, claims, sig, exp
|
||||
var E_INVALID = "INVALID_JWT";
|
||||
|
||||
module.exports = {
|
||||
|
@ -54,12 +71,20 @@ module.exports = {
|
|||
* @param {string} msg
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
DEVELOPER_ERROR: function (msg) {
|
||||
return create(msg, { status: 500, code: E_DEVELOPER });
|
||||
DEVELOPER_ERROR: function (old, msg, details) {
|
||||
return create(old, msg || old, E_DEVELOPER, 500, details);
|
||||
},
|
||||
BAD_GATEWAY: function (/*err*/) {
|
||||
var msg = "The server encountered a network error or a bad gateway.";
|
||||
return create(msg, { status: 502, code: E_BAD_GATEWAY });
|
||||
BAD_GATEWAY: function (err) {
|
||||
var msg =
|
||||
"The auth token could not be verified because our server encountered a network error (or a bad gateway) when connecting to its issuing server.";
|
||||
var details = [];
|
||||
if (err.message) {
|
||||
details.push("error.message = " + err.message);
|
||||
}
|
||||
if (err.response && err.response.statusCode) {
|
||||
details.push("response.statusCode = " + err.response.statusCode);
|
||||
}
|
||||
return create(msg, msg, E_BAD_GATEWAY, 502, details);
|
||||
},
|
||||
|
||||
//
|
||||
|
@ -71,25 +96,46 @@ module.exports = {
|
|||
* @returns {AuthError}
|
||||
*/
|
||||
INSECURE_ISSUER: function (iss) {
|
||||
var msg =
|
||||
var old =
|
||||
"'" + iss + "' is NOT secure. Set env 'KEYFETCH_ALLOW_INSECURE_HTTP=true' to allow for testing. (iss)";
|
||||
return create(msg, { status: 400, code: E_MALFORMED });
|
||||
var details = [
|
||||
"jwt.claims.iss = " + JSON.stringify(iss),
|
||||
"DEBUG: Set ENV 'KEYFETCH_ALLOW_INSECURE_HTTP=true' to allow insecure issuers (for testing)."
|
||||
];
|
||||
var msg =
|
||||
'The auth token could not be verified because our server could connect to its issuing server ("iss") securely.';
|
||||
return create(old, msg, E_MALFORMED, 400, details);
|
||||
},
|
||||
/**
|
||||
* @param {string} jwt
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
TOKEN_PARSE_ERROR: function (jwt) {
|
||||
var msg = "could not parse jwt: '" + jwt + "'";
|
||||
return create(msg, { status: 400, code: E_MALFORMED });
|
||||
PARSE_ERROR: function (jwt) {
|
||||
var old = "could not parse jwt: '" + jwt + "'";
|
||||
var msg = "The auth token could not be verified because it is malformed.";
|
||||
var details = ["jwt = " + JSON.stringify(jwt)];
|
||||
return create(old, msg, E_MALFORMED, 400, details);
|
||||
},
|
||||
/**
|
||||
* @param {string} iss
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
TOKEN_NO_ISSUER: function (iss) {
|
||||
var msg = "'iss' is not defined";
|
||||
return create(msg, { status: 400, code: E_MALFORMED });
|
||||
NO_ISSUER: function (iss) {
|
||||
var old = "'iss' is not defined";
|
||||
var msg = 'The auth token could not be verified because it doesn\'t specify an issuer ("iss").';
|
||||
var details = ["jwt.claims.iss = " + JSON.stringify(iss)];
|
||||
return create(old, msg, E_MALFORMED, 400, details);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} iss
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
MALFORMED_EXP: function (exp) {
|
||||
var old = "token's 'exp' has passed or could not parsed: '" + exp + "'";
|
||||
var msg = 'The auth token could not be verified because it\'s expiration date ("exp") could not be read';
|
||||
var details = ["jwt.claims.exp = " + JSON.stringify(exp)];
|
||||
return create(old, msg, E_MALFORMED, 400, details);
|
||||
},
|
||||
|
||||
//
|
||||
|
@ -100,77 +146,125 @@ module.exports = {
|
|||
* @param {number} exp
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
TOKEN_EXPIRED: function (exp) {
|
||||
//var msg = "The auth token is expired. (exp='" + exp + "')";
|
||||
var msg = "token's 'exp' has passed or could not parsed: '" + exp + "'";
|
||||
return create(msg, { code: E_INVALID });
|
||||
EXPIRED: function (exp) {
|
||||
var old = "token's 'exp' has passed or could not parsed: '" + exp + "'";
|
||||
// var msg = "The auth token did not pass verification because it is expired.not properly signed.";
|
||||
var msg = "The auth token is expired. To try again, go to the main page and sign in.";
|
||||
var details = ["jwt.claims.exp = " + JSON.stringify(exp)];
|
||||
return create(old, msg, E_INVALID, 401, details);
|
||||
},
|
||||
/**
|
||||
* @param {number} nbf
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
TOKEN_INACTIVE: function (nbf) {
|
||||
//var msg = "The auth token is not active yet. (nbf='" + nbf + "')";
|
||||
var msg = "token's 'nbf' has not been reached or could not parsed: '" + nbf + "'";
|
||||
return create(msg, { code: E_INVALID });
|
||||
INACTIVE: function (nbf) {
|
||||
var old = "token's 'nbf' has not been reached or could not parsed: '" + nbf + "'";
|
||||
var msg = "The auth token isn't valid yet. It's activation date (\"nbf\") is in the future.";
|
||||
var details = ["jwt.claims.nbf = " + JSON.stringify(nbf)];
|
||||
return create(old, msg, E_INVALID, 401, details);
|
||||
},
|
||||
/** @returns {AuthError} */
|
||||
TOKEN_INVALID_SIGNATURE: function () {
|
||||
//var msg = "The auth token is not properly signed and could not be verified.";
|
||||
var msg = "token signature verification was unsuccessful";
|
||||
return create(msg, { code: E_INVALID });
|
||||
BAD_SIGNATURE: function (jwt) {
|
||||
var old = "token signature verification was unsuccessful";
|
||||
var msg = "The auth token did not pass verification because it is not properly signed.";
|
||||
var details = ["jwt = " + JSON.stringify(jwt)];
|
||||
return create(old, msg, E_INVALID, 401, details);
|
||||
},
|
||||
/** @returns {AuthError} */
|
||||
TOKEN_UNKNOWN_SIGNER: function () {
|
||||
var msg = "Retrieved a list of keys, but none of them matched the 'kid' (key id) of the token.";
|
||||
return create(msg, { code: E_INVALID });
|
||||
/**
|
||||
* @param {string} kid
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
JWK_NOT_FOUND_OLD: function (kid) {
|
||||
var old = "Retrieved a list of keys, but none of them matched the 'kid' (key id) of the token.";
|
||||
var msg =
|
||||
'The auth token did not pass verification because our server couldn\'t find a mutually trusted verification key ("jwk").';
|
||||
var details = ["jws.header.kid = " + JSON.stringify(kid)];
|
||||
return create(old, msg, E_INVALID, 401, details);
|
||||
},
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
JWK_NOT_FOUND: function (id) {
|
||||
var msg = "No JWK found by kid or thumbprint '" + id + "'";
|
||||
return create(msg, { code: E_INVALID });
|
||||
// TODO Distinguish between when it's a kid vs thumbprint.
|
||||
var old = "No JWK found by kid or thumbprint '" + id + "'";
|
||||
var msg =
|
||||
'The auth token did not pass verification because our server couldn\'t find a mutually trusted verification key ("jwk").';
|
||||
var details = ["jws.header.kid = " + JSON.stringify(id)];
|
||||
return create(old, msg, E_INVALID, 401, details);
|
||||
},
|
||||
/** @returns {AuthError} */
|
||||
OIDC_CONFIG_NOT_FOUND: function () {
|
||||
//var msg = "Failed to retrieve OpenID configuration for token issuer";
|
||||
var msg = "Failed to retrieve openid configuration";
|
||||
return create(msg, { code: E_INVALID });
|
||||
NO_JWKWS_URI: function (url) {
|
||||
var old = "Failed to retrieve openid configuration";
|
||||
var msg =
|
||||
'The auth token did not pass verification because its issuing server did not list any verification keys ("jwks").';
|
||||
var details = ["OpenID Provider Configuration: " + JSON.stringify(url)];
|
||||
return create(old, msg, E_INVALID, 401, details);
|
||||
},
|
||||
/**
|
||||
* @param {string} iss
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
ISSUER_NOT_TRUSTED: function (iss) {
|
||||
var msg = "token was issued by an untrusted issuer: '" + iss + "'";
|
||||
return create(msg, { code: E_INVALID });
|
||||
UNKNOWN_ISSUER: function (iss) {
|
||||
var old = "token was issued by an untrusted issuer: '" + iss + "'";
|
||||
var msg = "The auth token did not pass verification because it wasn't issued by a server that we trust.";
|
||||
var details = ["jwt.claims.iss = " + JSON.stringify(iss)];
|
||||
return create(old, msg, E_INVALID, 401, details);
|
||||
},
|
||||
/**
|
||||
* @param {Array<string>} claimNames
|
||||
* @param {Array<string>} details
|
||||
* @returns {AuthError}
|
||||
*/
|
||||
CLAIMS_MISMATCH: function (claimNames) {
|
||||
var msg = "token did not match on one or more authorization claims: '" + claimNames + "'";
|
||||
return create(msg, { code: E_INVALID });
|
||||
FAILED_CLAIMS: function (details, claimNames) {
|
||||
var old = "token did not match on one or more authorization claims: '" + claimNames + "'";
|
||||
var msg =
|
||||
'The auth token did not pass verification because it failed some of the verification criteria ("claims").';
|
||||
return create(old, msg, E_INVALID, 401, details);
|
||||
}
|
||||
};
|
||||
var Errors = module.exports;
|
||||
|
||||
// for README
|
||||
if (require.main === module) {
|
||||
console.info("| Name | Status | Message (truncated) |");
|
||||
console.info("| ---- | ------ | ------------------- |");
|
||||
let maxWidth = 54;
|
||||
let header = ["Hint", "Code", "Status", "Message (truncated)"];
|
||||
let widths = header.map(function (v) {
|
||||
return Math.min(maxWidth, String(v).length);
|
||||
});
|
||||
let rows = [];
|
||||
Object.keys(module.exports).forEach(function (k) {
|
||||
//@ts-ignore
|
||||
var E = module.exports[k];
|
||||
var e = E();
|
||||
var e = E("test");
|
||||
var code = e.code;
|
||||
var msg = e.message;
|
||||
if ("E_" + k !== e.code) {
|
||||
code = k;
|
||||
msg = e.details || msg;
|
||||
}
|
||||
console.info(`| ${code} | ${e.status} | ${msg.slice(0, 45)}... |`);
|
||||
var hint = k.toLowerCase().replace(/_/g, " ");
|
||||
widths[0] = Math.max(widths[0], String(hint).length);
|
||||
widths[1] = Math.max(widths[1], String(code).length);
|
||||
widths[2] = Math.max(widths[2], String(e.status).length);
|
||||
widths[3] = Math.min(maxWidth, Math.max(widths[3], String(msg).length));
|
||||
rows.push([hint, code, e.status, msg]);
|
||||
});
|
||||
rows.forEach(function (cols, i) {
|
||||
let cells = cols.map(function (col, i) {
|
||||
if (col.length > maxWidth) {
|
||||
col = col.slice(0, maxWidth - 3);
|
||||
col += "...";
|
||||
}
|
||||
return String(col).padEnd(widths[i], " ");
|
||||
});
|
||||
let out = `| ${cells[0]} | ${cells[1]} | ${cells[2]} | ${cells[3].slice(0, widths[3])} |`;
|
||||
//out = out.replace(/\| /g, " ").replace(/\|/g, "");
|
||||
console.info(out);
|
||||
if (i === 0) {
|
||||
cells = cols.map(function (col, i) {
|
||||
return "-".padEnd(widths[i], "-");
|
||||
});
|
||||
console.info(`| ${cells[0]} | ${cells[1]} | ${cells[2]} | ${cells[3]} |`);
|
||||
}
|
||||
});
|
||||
console.log();
|
||||
console.log(Errors.MALFORMED_EXP());
|
||||
console.log();
|
||||
console.log(JSON.stringify(Errors.MALFORMED_EXP(), null, 2));
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "keyfetch",
|
||||
"version": "2.0.0",
|
||||
"version": "3.0.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "keyfetch",
|
||||
"version": "2.0.0",
|
||||
"version": "3.0.2",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@root/request": "^1.8.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "keyfetch",
|
||||
"version": "2.0.0",
|
||||
"version": "3.0.2",
|
||||
"description": "Lightweight support for fetching JWKs.",
|
||||
"homepage": "https://git.rootprojects.org/root/keyfetch.js",
|
||||
"main": "keyfetch.js",
|
||||
|
|
Loading…
Reference in New Issue