diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 522866ee..00000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -docs/_site diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index d22000de..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "env": { - "commonjs": true, - "es6": true, - "node": true - }, - "extends": [ - "google" - ], - "globals": { - "Atomics": "readonly", - "SharedArrayBuffer": "readonly" - }, - "parserOptions": { - "ecmaVersion": 2018 - }, - "rules": { - "indent": ["error", 2], - "comma-dangle": ["error", "always-multiline"] - } -} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..1de66086 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,16 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import jest from "eslint-plugin-jest"; + +export default [ + {files: ["**/*.js"], languageOptions: {sourceType: "commonjs"}}, + {languageOptions: { globals: globals.node }}, + { + files: ["test/**"], + ...jest.configs['flat/recommended'], + rules: { + ...jest.configs['flat/recommended'].rules, + }, + }, + pluginJs.configs.recommended, +]; \ No newline at end of file diff --git a/lib/index.js b/lib/index.js index b3c304f6..18fee4dd 100755 --- a/lib/index.js +++ b/lib/index.js @@ -8,160 +8,106 @@ const async = require('async'); const errors = require('restify-errors'); const jwt = require('jsonwebtoken'); -const {unless} = require('express-unless'); +const { unless } = require('express-unless'); -const DEFAULT_REVOKED_FUNCTION = (_, __, cb) => { - return cb(null, false); -}; +const DEFAULT_REVOKED_FUNCTION = (_, __, cb) => cb(null, false); -/** - * @param {any} object - * @return {boolean} - */ -function isFunction(object) { - return Object.prototype.toString.call(object) === '[object Function]'; -} +const isFunction = (object) => typeof object === 'function'; -/** - * @param {string} secret - * @return {function(*, *, *): *} - */ -function wrapStaticSecretInCallback(secret) { - return (_, __, cb) => { - return cb(null, secret); - }; -} +const wrapStaticSecretInCallback = (secret) => (_, __, cb) => cb(null, secret); module.exports = (options) => { - if (!options || !options.secret) throw new Error('secret should be set'); - - let secretCallback = options.secret; - - if (!isFunction(secretCallback)) { - secretCallback = wrapStaticSecretInCallback(secretCallback); + if (!options || !options.secret) { + throw new Error('secret should be set'); } - const isRevokedCallback = options.isRevoked || DEFAULT_REVOKED_FUNCTION; + const secretCallback = isFunction(options.secret) + ? options.secret + : wrapStaticSecretInCallback(options.secret); - const _requestProperty = options.userProperty || - options.requestProperty || - 'user'; - - // eslint-disable-next-line max-len - const credentialsRequired = typeof options.credentialsRequired === 'undefined' ? - true : - options.credentialsRequired; + const isRevokedCallback = options.isRevoked || DEFAULT_REVOKED_FUNCTION; + const requestProperty = options.userProperty || options.requestProperty || 'user'; + const credentialsRequired = options.credentialsRequired !== false; const middleware = (req, res, next) => { let token; - if ( - req.method === 'OPTIONS' && - req.headers.hasOwnProperty('access-control-request-headers') - ) { - const hasAuthInAccessControl = !!~req.headers[ - 'access-control-request-headers' - ] + // Handle CORS preflight requests + if (req.method === 'OPTIONS' && req.headers['access-control-request-headers']) { + const hasAuthInAccessControl = req.headers['access-control-request-headers'] .split(',') - .map((header) => { - return header.trim(); - }) - .indexOf('authorization'); + .map(header => header.trim()) + .includes('authorization'); - if (hasAuthInAccessControl) { - return next(); - } + if (hasAuthInAccessControl) return next(); } - if (options.getToken && typeof options.getToken === 'function') { - try { - token = options.getToken(req); - } catch (e) { - return next(e); - } - } else if (req.headers && req.headers.authorization) { - const parts = req.headers.authorization.split(' '); - if (parts.length === 2) { - const scheme = parts[0]; - const credentials = parts[1]; - - if (/^(?:Bearer|JWT)$/i.test(scheme)) { - token = credentials; - } else { - return next( - new errors.InvalidCredentialsError( - 'Format is Authorization: Bearer [token] or Jwt [token]', - ), - ); - } - } else { - return next( - new errors.InvalidCredentialsError( - 'Format is Authorization: Bearer [token] or Jwt [token]', - ), - ); - } + // Get the token from the request + try { + token = options.getToken ? options.getToken(req) : extractToken(req); + } catch (e) { + return next(e); } + // Check if token is required if (!token) { if (credentialsRequired) { - return next( - new errors.InvalidCredentialsError( - 'No authorization token was found', - ), - ); - } else { - return next(); + return next(new errors.InvalidCredentialsError('No authorization token was found')); } + return next(); } - let dToken; - + let decodedToken; try { - dToken = jwt.decode(token, {complete: true}) || {}; - } catch (e) { + decodedToken = jwt.decode(token, { complete: true }) || {}; + } catch { return next(new errors.InvalidCredentialsError('The token is corrupted')); } async.parallel([ - (callback) => { // getSecret - const arity = secretCallback.length; - if (arity === 4) { - secretCallback(req, dToken.header, dToken.payload, callback); - } else { // arity == 3 - secretCallback(req, dToken.payload, callback); - } - }, - (callback) => { // checkRevoked - isRevokedCallback(req, dToken.payload, callback); - }, - ], (err, results) => { - if (err) { - return next(err); - } - const revoked = results[1]; + (callback) => getSecret(secretCallback, req, decodedToken, callback), + (callback) => checkRevoked(isRevokedCallback, req, decodedToken, callback), + ], (err, [secret, revoked]) => { + if (err) return next(err); + if (revoked) { - return next( - new errors.UnauthorizedError( - 'The token has been revoked.', - ), - ); + return next(new errors.UnauthorizedError('The token has been revoked.')); } - const secret = results[0]; - + // Verify the token jwt.verify(token, secret, options, (err, decoded) => { if (err && credentialsRequired) { - return (err.name === 'TokenExpiredError') ? - next(new errors.UnauthorizedError('The token has expired')) : - next(new errors.InvalidCredentialsError(err)); + return next(err.name === 'TokenExpiredError' + ? new errors.UnauthorizedError('The token has expired') + : new errors.InvalidCredentialsError(err)); } - req[_requestProperty] = decoded; + req[requestProperty] = decoded; next(); }); }); }; + const extractToken = (req) => { + const authHeader = req.headers?.authorization; + if (!authHeader) return null; + + const parts = authHeader.split(' '); + if (parts.length === 2 && /^(Bearer|JWT)$/i.test(parts[0])) { + return parts[1]; + } + + throw new errors.InvalidCredentialsError('Format is Authorization: Bearer [token] or Jwt [token]'); + }; + + const getSecret = (callback, req, token, done) => { + const arity = callback.length; + return arity === 4 + ? callback(req, token.header, token.payload, done) + : callback(req, token.payload, done); + }; + + const checkRevoked = (callback, req, token, done) => callback(req, token.payload, done); + middleware.unless = unless; return middleware; -}; +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 48b4213c..7db37d9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,12 +16,14 @@ "devDependencies": { "@commitlint/cli": "19.5.0", "@commitlint/config-conventional": "19.5.0", + "@eslint/js": "^9.11.1", "@semantic-release/changelog": "6.0.3", "@semantic-release/git": "10.0.1", "@types/restify-errors": "4.3.9", "cz-conventional-changelog": "3.3.0", - "eslint": "8.57.1", - "eslint-config-google": "0.14.0", + "eslint": "9.11.1", + "eslint-plugin-jest": "28.8.3", + "globals": "^15.10.0", "husky": "9.1.6", "jest": "29.7.0", "restify": "^11.1.0", @@ -32,15 +34,6 @@ "node": ">= 4.0" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -1140,25 +1133,61 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", + "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -1166,7 +1195,7 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -1188,6 +1217,18 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1195,29 +1236,34 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "version": "9.11.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.11.1.tgz", + "integrity": "sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==", "dev": true, "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", + "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "levn": "^0.4.1" }, "engines": { - "node": ">=10.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -1233,13 +1279,18 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", "dev": true, - "license": "BSD-3-Clause" + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", @@ -2628,6 +2679,12 @@ "@types/node": "*" } }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -2661,6 +2718,12 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@types/node": { "version": "20.5.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", @@ -2716,12 +2779,146 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.0.tgz", + "integrity": "sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.8.0", + "@typescript-eslint/visitor-keys": "8.8.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.0.tgz", + "integrity": "sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.0.tgz", + "integrity": "sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.8.0", + "@typescript-eslint/visitor-keys": "8.8.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.0.tgz", + "integrity": "sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.8.0", + "@typescript-eslint/types": "8.8.0", + "@typescript-eslint/typescript-estree": "8.8.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.0.tgz", + "integrity": "sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.8.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, "node_modules/abort-controller": { "version": "3.0.0", @@ -2736,10 +2933,11 @@ } }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -4122,18 +4320,6 @@ "node": ">=8" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dot-prop": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", @@ -4476,44 +4662,42 @@ } }, "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "version": "9.11.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.11.1.tgz", + "integrity": "sha512-MobhYKIoAO1s1e4VUrgx1l1Sk2JBR/Gqjjgw8+mfgoLE2xwsHur4gdfTxyTgShrhvdVFTaJSgMiQBl1jv/AWxg==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.6.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.11.1", + "@eslint/plugin-kit": "^0.2.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.0.2", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", @@ -4525,47 +4709,69 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/eslint-config-google": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/eslint-config-google/-/eslint-config-google-0.14.0.tgz", - "integrity": "sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw==", + "node_modules/eslint-plugin-jest": { + "version": "28.8.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.3.tgz", + "integrity": "sha512-HIQ3t9hASLKm2IhIOqnu+ifw7uLZkIlR7RYNv7fMcEi/p0CIiJmfriStQS2LDkgtY4nyLbIZAD+JL347Yc2ETQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^16.10.0 || ^18.12.0 || >=20.0.0" }, "peerDependencies": { - "eslint": ">=5.16.0" + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", + "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", + "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4576,7 +4782,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4592,21 +4797,20 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", + "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.1.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4626,9 +4830,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -4911,15 +5115,15 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { @@ -5026,23 +5230,22 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/formidable": { @@ -5405,15 +5608,13 @@ } }, "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "15.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.10.0.tgz", + "integrity": "sha512-tqFIbz83w4Y5TCbtgjZjApohbuh7K9BxGYFm7ifwDR240tvdb7P9x+/9VvUKlmkPoiknoJtanI8UOrqxS3a7lQ==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5481,12 +5682,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -10197,17 +10392,17 @@ } }, "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -11214,21 +11409,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -12479,6 +12659,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -12557,18 +12750,6 @@ "node": ">=4" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", diff --git a/package.json b/package.json index c31aecab..dbd33b90 100644 --- a/package.json +++ b/package.json @@ -46,12 +46,14 @@ "devDependencies": { "@commitlint/cli": "19.5.0", "@commitlint/config-conventional": "19.5.0", + "@eslint/js": "^9.11.1", "@semantic-release/changelog": "6.0.3", "@semantic-release/git": "10.0.1", "@types/restify-errors": "4.3.9", "cz-conventional-changelog": "3.3.0", - "eslint": "8.57.1", - "eslint-config-google": "0.14.0", + "eslint": "9.11.1", + "eslint-plugin-jest": "28.8.3", + "globals": "^15.10.0", "husky": "9.1.6", "jest": "29.7.0", "restify": "^11.1.0", diff --git a/test/jwt.test.js b/test/jwt.test.js index 4cf53fe1..a7cbcc30 100755 --- a/test/jwt.test.js +++ b/test/jwt.test.js @@ -1,343 +1,329 @@ -const assert = require('assert'); const errors = require('restify-errors'); const jwt = require('jsonwebtoken'); const restifyJWT = require('../lib'); -describe('failure tests', function() { +describe('failure tests', () => { const req = {}; const res = {}; - it('should throw if options not sent', function(done) { - try { + it('should throw if options not sent', () => { + expect(() => { restifyJWT(); - } catch (e) { - assert.ok(e); - assert.strictEqual(e.message, 'secret should be set'); - done(); - } + }).toThrow('secret should be set'); }); - it('should throw if no authorization header and credentials are required', - function() { - restifyJWT({ - secret: 'shhhh', - credentialsRequired: true, - })(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.message, 'No authorization token was found'); + it('should throw if no authorization header and credentials are required', async () => { + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhh', credentialsRequired: true })(req, res, (error) => { + resolve(error); }); - }, - ); + }); + expect(err).toBeDefined(); + expect(err.message).toBe('No authorization token was found'); + }); - it('support unless skip', function() { + it('support unless skip', async () => { req.originalUrl = '/index.html'; - restifyJWT({ - secret: 'shhhh', - }).unless({ - path: '/index.html', - })(req, res, function(err) { - assert.ok(!err); + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhh' }).unless({ path: '/index.html' })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeUndefined(); }); - it('should skip on CORS preflight', function() { - const corsReq = {}; - corsReq.method = 'OPTIONS'; - corsReq.headers = { - 'access-control-request-headers': 'sasa, sras, authorization ', - }; - restifyJWT({secret: 'shhhh'})(corsReq, res, function(err) { - assert.ok(!err); + it('should skip on CORS preflight', async () => { + const corsReq = { method: 'OPTIONS', headers: { 'access-control-request-headers': 'sasa, sras, authorization' } }; + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhh' })(corsReq, res, (error) => { + resolve(error); + }); }); + expect(err).toBeUndefined(); }); - it('should throw if "authorization" does not exist in header ', function() { - const req = {}; + it('should throw if "authorization" does not exist in header', async () => { req.method = 'OPTIONS'; - req.headers = { - 'access-control-request-headers': 'sasa, sras', - }; - restifyJWT({secret: 'shhhh'})(req, res, function(err) { - assert.ok(err); + req.headers = { 'access-control-request-headers': 'sasa, sras' }; + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhh' })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeDefined(); }); - it('should throw if authorization header is malformed', function() { - req.headers = {}; - req.headers.authorization = 'wrong'; - restifyJWT({secret: 'shhhh'})(req, res, function(err) { - assert.ok(err); - assert.strictEqual( - err.message, - 'Format is Authorization: Bearer [token] or Jwt [token]', - ); + it('should throw if authorization header is malformed', async () => { + req.headers = { authorization: 'wrong' }; + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhh' })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeDefined(); + expect(err.message).toBe('Format is Authorization: Bearer [token] or Jwt [token]'); }); - it('should throw if authorization header is not Bearer nor JWT', function() { - req.headers = {}; - req.headers.authorization = 'Basic foobar'; - restifyJWT({secret: 'shhhh'})(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'InvalidCredentials'); + it('should throw if authorization header is not Bearer nor JWT', async () => { + req.headers = { authorization: 'Basic foobar' }; + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhh' })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeDefined(); + expect(err.body.code).toBe('InvalidCredentials'); }); - it('should throw if authorization header is not well-formatted jwt', - function() { - req.headers = {}; - req.headers.authorization = 'Bearer wrongjwt'; - restifyJWT({secret: 'shhhh'})(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'InvalidCredentials'); + it('should throw if authorization header is not well-formatted jwt', async () => { + req.headers = { authorization: 'Bearer wrongjwt' }; + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhh' })(req, res, (error) => { + resolve(error); }); - }, - ); + }); + expect(err).toBeDefined(); + expect(err.body.code).toBe('InvalidCredentials'); + }); - it('should throw if jwt is an invalid json', function() { - req.headers = {}; - req.headers.authorization = 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.' + + it('should throw if jwt is an invalid json', async () => { + req.headers = { + authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.' + 'eyJpYXQiOjExNTg0MDcxNjksImp0aSI6ImVhZDU4YTk1LWY1NDUtNDA1My04Y2RhLTA0' + - 'ODdjYWIYgTBmMiIsImV4cCI6MTUxMTExMDc4OX0.foo'; - restifyJWT({secret: 'shhhh'})(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'InvalidCredentials'); + 'ODdjYWIYgTBmMiIsImV4cCI6MTUxMTExMDc4OX0.foo' + }; + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhh' })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeDefined(); + expect(err.body.code).toBe('InvalidCredentials'); }); - it('should throw if authorization header is not valid jwt', function(done) { + it('should throw if authorization header is not valid jwt', async () => { const secret = 'shhhhhh'; - const token = jwt.sign({foo: 'bar'}, secret); + const token = jwt.sign({ foo: 'bar' }, secret); + req.headers = { authorization: 'Bearer ' + token }; - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - restifyJWT({secret: 'different-shhhh'})(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'InvalidCredentials'); - assert.strictEqual(err.jse_cause.message, 'invalid signature'); - done(); + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'different-shhhh' })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeDefined(); + expect(err.body.code).toBe('InvalidCredentials'); + expect(err.jse_cause.message).toBe('invalid signature'); }); - it('should throw if audience is not expected', function(done) { + it('should throw if audience is not expected', async () => { const secret = 'shhhhhh'; - const token = jwt.sign({foo: 'bar', aud: 'expected-audience'}, secret); + const token = jwt.sign({ foo: 'bar', aud: 'expected-audience' }, secret); + req.headers = { authorization: 'Bearer ' + token }; - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - restifyJWT({ - secret: 'shhhhhh', - audience: 'not-expected-audience', - })(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'InvalidCredentials'); - assert.strictEqual( - err.jse_cause.message, - 'jwt audience invalid. expected: not-expected-audience', - ); - done(); + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhhhh', audience: 'not-expected-audience' })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeDefined(); + expect(err.body.code).toBe('InvalidCredentials'); + expect(err.jse_cause.message).toBe('jwt audience invalid. expected: not-expected-audience'); }); - it('should throw if token is expired', function(done) { + it('should throw if token is expired', async () => { const secret = 'shhhhhh'; - const token = jwt.sign({foo: 'bar', exp: 1382412921}, secret); + const token = jwt.sign({ foo: 'bar', exp: Math.floor(Date.now() / 1000) - 1 }, secret); + req.headers = { authorization: 'Bearer ' + token }; - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - restifyJWT({secret: 'shhhhhh'})(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'Unauthorized'); - assert.strictEqual(err.message, 'The token has expired'); - done(); + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhhhh' })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeDefined(); + expect(err.body.code).toBe('Unauthorized'); + expect(err.message).toBe('The token has expired'); }); - it('should throw if token issuer is wrong', function(done) { + it('should throw if token issuer is wrong', async () => { const secret = 'shhhhhh'; - const token = jwt.sign({foo: 'bar', iss: 'http://foo'}, secret); + const token = jwt.sign({ foo: 'bar', iss: 'http://foo' }, secret); + req.headers = { authorization: 'Bearer ' + token }; - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - restifyJWT({secret: 'shhhhhh', issuer: 'http://wrong'})(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'InvalidCredentials'); - assert.strictEqual(err.jse_cause.message, 'jwt issuer invalid. expected: http://wrong'); - done(); + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhhhh', issuer: 'http://wrong' })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeDefined(); + expect(err.body.code).toBe('InvalidCredentials'); + expect(err.jse_cause.message).toBe('jwt issuer invalid. expected: http://wrong'); }); - it('should use errors thrown from custom getToken function', function() { - /** - * @throws error InvalidCredentials - */ + it('should use errors thrown from custom getToken function', async () => { function getTokenThatThrowsError() { throw new errors.InvalidCredentialsError('Invalid token!'); } - restifyJWT({ - secret: 'shhhhhh', - getToken: getTokenThatThrowsError, - })(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.message, 'Invalid token!'); + const err = await new Promise((resolve) => { + restifyJWT({ + secret: 'shhhhhh', + getToken: getTokenThatThrowsError, + })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeDefined(); + expect(err.message).toBe('Invalid token!'); }); - - it('should throw error when signature is wrong', function(done) { + it('should throw error when signature is wrong', async () => { const secret = 'shhh'; - const token = jwt.sign({foo: 'bar', iss: 'http://www'}, secret); - // manipulate the token - const newContent = Buffer.from('{foo: \'bar\', edg: \'ar\'}') - .toString('base64'); + const token = jwt.sign({ foo: 'bar', iss: 'http://www' }, secret); + const newContent = Buffer.from('{foo: \'bar\', edg: \'ar\'}').toString('base64'); const splitetToken = token.split('.'); splitetToken[1] = newContent; const newToken = splitetToken.join('.'); - - // build request - req.headers = []; - req.headers.authorization = 'Bearer ' + newToken; - restifyJWT({secret: secret})(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'InvalidCredentials'); - assert.strictEqual(err.jse_cause.message, 'invalid token'); - done(); + + req.headers = { authorization: 'Bearer ' + newToken }; + + const err = await new Promise((resolve) => { + restifyJWT({ secret: secret })(req, res, (error) => { + resolve(error); + }); }); + + expect(err).toBeDefined(); + expect(err.body.code).toBe('InvalidCredentials'); + expect(err.jse_cause.message).toBe('invalid token'); }); }); -describe('work tests', function() { +describe('work tests', () => { let req = {}; const res = {}; - it('should work if authorization header is valid jwt ("Bearer ")', - function() { - const secret = 'shhhhhh'; - const token = jwt.sign({foo: 'bar'}, secret); + it('should work if authorization header is valid jwt ("Bearer ")', async () => { + const secret = 'shhhhhh'; + const token = jwt.sign({ foo: 'bar' }, secret); + req.headers = { authorization: 'Bearer ' + token }; - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - restifyJWT({secret: secret})(req, res, function() { - assert.strictEqual('bar', req.user.foo); + await new Promise((resolve) => { + restifyJWT({ secret: secret })(req, res, () => { + expect(req.user.foo).toBe('bar'); + resolve(); }); - }, - ); - - it('should work if authorization header is valid jwt ("JWT ")', - function() { - const secret = 'shhhhhh'; - const token = jwt.sign({foo: 'bar'}, secret); - - req.headers = {}; - req.headers.authorization = 'JWT ' + token; - restifyJWT({secret: secret})(req, res, function() { - assert.strictEqual('bar', req.user.foo); + }); + }); + + it('should work if authorization header is valid jwt ("JWT ")', async () => { + const secret = 'shhhhhh'; + const token = jwt.sign({ foo: 'bar' }, secret); + req.headers = { authorization: 'JWT ' + token }; + + await new Promise((resolve) => { + restifyJWT({ secret: secret })(req, res, () => { + expect(req.user.foo).toBe('bar'); + resolve(); }); - }, - ); - - it('should work if authorization header is valid with a buffer secret', - function() { - const secret = Buffer.from( - 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', - 'base64', - ); - const token = jwt.sign({foo: 'bar'}, secret); - - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - restifyJWT({secret: secret})(req, res, function() { - assert.strictEqual('bar', req.user.foo); + }); + }); + + it('should work if authorization header is valid with a buffer secret', async () => { + const secret = Buffer.from( + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + 'base64', + ); + const token = jwt.sign({ foo: 'bar' }, secret); + req.headers = { authorization: 'Bearer ' + token }; + + await new Promise((resolve) => { + restifyJWT({ secret: secret })(req, res, () => { + expect(req.user.foo).toBe('bar'); + resolve(); }); }); + }); - it('should set userProperty if option provided', function() { + it('should set userProperty if option provided', async () => { const secret = 'shhhhhh'; - const token = jwt.sign({foo: 'bar'}, secret); + const token = jwt.sign({ foo: 'bar' }, secret); + req.headers = { authorization: 'Bearer ' + token }; - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - restifyJWT({secret: secret, userProperty: 'auth'})(req, res, function() { - assert.strictEqual('bar', req.auth.foo); + await new Promise((resolve) => { + restifyJWT({ secret: secret, userProperty: 'auth' })(req, res, () => { + expect(req.auth.foo).toBe('bar'); + resolve(); + }); }); }); - it('should work if no authorization header and credentials are not required', - function() { - req = {}; - restifyJWT({ - secret: 'shhhh', - credentialsRequired: false, - })(req, res, function(err) { - assert(typeof err === 'undefined'); + it('should work if no authorization header and credentials are not required', async () => { + req = {}; + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhh', credentialsRequired: false })(req, res, (error) => { + resolve(error); }); }); + expect(err).toBeUndefined(); + }); - it('should work if token is expired and credentials are not required', - function() { - const secret = 'shhhhhh'; - const token = jwt.sign({foo: 'bar', exp: 1382412921}, secret); + it('should work if token is expired and credentials are not required', async () => { + const secret = 'shhhhhh'; + const token = jwt.sign({ foo: 'bar', exp: Math.floor(Date.now() / 1000) - 1 }, secret); + req.headers = { authorization: 'Bearer ' + token }; - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - restifyJWT({ - secret: secret, - credentialsRequired: false, - })(req, res, function(err) { - assert(typeof err === 'undefined'); - assert(typeof req.user === 'undefined'); + const err = await new Promise((resolve) => { + restifyJWT({ secret: secret, credentialsRequired: false })(req, res, (error) => { + resolve(error); }); - }, - ); + }); + expect(err).toBeUndefined(); + expect(req.user).toBeUndefined(); + }); - it('should not work if no authorization header', function() { + it('should not work if no authorization header', async () => { req = {}; - restifyJWT({secret: 'shhhh'})(req, res, function(err) { - assert(typeof err !== 'undefined'); + const err = await new Promise((resolve) => { + restifyJWT({ secret: 'shhhh' })(req, res, (error) => { + resolve(error); + }); }); + expect(err).toBeDefined(); }); - it('should work with a custom getToken function', function() { + it('should work with a custom getToken function', async () => { const secret = 'shhhhhh'; - const token = jwt.sign({foo: 'bar'}, secret); - + const token = jwt.sign({ foo: 'bar' }, secret); req.headers = {}; - req.query = {}; - req.query.token = token; - - /** - * @param {object} req - * @return {string | string[] | {}} - */ - function getTokenFromQuery(req) { - return req.query.token; - } + req.query = { token }; - restifyJWT({ - secret: secret, - getToken: getTokenFromQuery, - })(req, res, function() { - assert.strictEqual('bar', req.user.foo); + const getTokenFromQuery = (req) => req.query.token; + + await new Promise((resolve) => { + restifyJWT({ secret: secret, getToken: getTokenFromQuery })(req, res, () => { + expect(req.user.foo).toBe('bar'); + resolve(); + }); }); }); - it('should work with a secretCallback function that accepts header argument', - function() { - const secret = 'shhhhhh'; - const secretCallback = function(req, headers, payload, cb) { - assert.strictEqual(headers.alg, 'HS256'); - assert.strictEqual(payload.foo, 'bar'); - process.nextTick(function() { - return cb(null, secret); - }); - }; - const token = jwt.sign({foo: 'bar'}, secret); - - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - restifyJWT({secret: secretCallback})(req, res, function() { - assert.strictEqual('bar', req.user.foo); + it('should work with a secretCallback function that accepts header argument', async () => { + const secret = 'shhhhhh'; + const secretCallback = (req, headers, payload, cb) => { + expect(headers.alg).toBe('HS256'); + expect(payload.foo).toBe('bar'); + process.nextTick(() => cb(null, secret)); + }; + const token = jwt.sign({ foo: 'bar' }, secret); + req.headers = { authorization: 'Bearer ' + token }; + + await new Promise((resolve) => { + restifyJWT({ secret: secretCallback })(req, res, () => { + expect(req.user.foo).toBe('bar'); + resolve(); }); }); -}); + }); +}); \ No newline at end of file diff --git a/test/multitenancy.test.js b/test/multitenancy.test.js index 44497bcd..13160868 100644 --- a/test/multitenancy.test.js +++ b/test/multitenancy.test.js @@ -1,75 +1,72 @@ -const assert = require('assert'); const errors = require('restify-errors'); const jwt = require('jsonwebtoken'); const restifyJWT = require('../lib'); -describe('multitenancy', function() { +describe('multitenancy', () => { const req = {}; const res = {}; const tenants = { - 'a': { + a: { secret: 'secret-a', }, }; - const secretCallback = function(req, payload, cb) { + const secretCallback = (req, payload, cb) => { const issuer = payload.iss; if (tenants[issuer]) { return cb(null, tenants[issuer].secret); } - return cb( - new errors.UnauthorizedError( - 'Could not find secret for issuer.', - ), - ); + return cb(new errors.UnauthorizedError('Could not find secret for issuer.')); }; const middleware = restifyJWT({ secret: secretCallback, }); - it('should retrieve secret using callback', function() { - const token = jwt.sign({iss: 'a', foo: 'bar'}, tenants.a.secret); + it('should retrieve secret using callback', async () => { + const token = jwt.sign({ iss: 'a', foo: 'bar' }, tenants.a.secret); + req.headers = { authorization: `Bearer ${token}` }; - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - - middleware(req, res, function() { - assert.strictEqual('bar', req.user.foo); + await new Promise((resolve) => { + middleware(req, res, () => { + expect(req.user.foo).toBe('bar'); + resolve(); + }); }); }); - it('should throw if an error ocurred when retrieving the token', function() { - const secret = 'shhhhhh'; - const token = jwt.sign({iss: 'inexistent', foo: 'bar'}, secret); - - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; + it('should throw if an error occurred when retrieving the token', async () => { + const token = jwt.sign({ iss: 'inexistent', foo: 'bar' }, 'shhhhhh'); + req.headers = { authorization: `Bearer ${token}` }; - middleware(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'Unauthorized'); - assert.strictEqual(err.message, 'Could not find secret for issuer.'); + await new Promise((resolve) => { + middleware(req, res, (err) => { + expect(err).toBeDefined(); + expect(err.body.code).toBe('Unauthorized'); + expect(err.message).toBe('Could not find secret for issuer.'); + resolve(); + }); }); }); - it('should fail if token is revoked', function() { - const token = jwt.sign({iss: 'a', foo: 'bar'}, tenants.a.secret); - - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; + it('should fail if token is revoked', async () => { + const token = jwt.sign({ iss: 'a', foo: 'bar' }, tenants.a.secret); + req.headers = { authorization: `Bearer ${token}` }; - restifyJWT({ - secret: secretCallback, - isRevoked: function(req, payload, done) { - done(null, true); - }, - })(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'Unauthorized'); - assert.strictEqual(err.message, 'The token has been revoked.'); + await new Promise((resolve) => { + restifyJWT({ + secret: secretCallback, + isRevoked: (req, payload, done) => { + done(null, true); + }, + })(req, res, (err) => { + expect(err).toBeDefined(); + expect(err.body.code).toBe('Unauthorized'); + expect(err.message).toBe('The token has been revoked.'); + resolve(); + }); }); }); -}); +}); \ No newline at end of file diff --git a/test/revocation.test.js b/test/revocation.test.js index 245924b9..6a7c9ee8 100644 --- a/test/revocation.test.js +++ b/test/revocation.test.js @@ -1,63 +1,67 @@ -const assert = require('assert'); const jwt = require('jsonwebtoken'); const restifyJWT = require('../lib'); -describe('revoked jwts', function() { +describe('revoked jwts', () => { const secret = 'shhhhhh'; - const revokedId = '1234'; const middleware = restifyJWT({ secret: secret, - isRevoked: function(req, payload, done) { + isRevoked: (req, payload, done) => { done(null, payload.jti && payload.jti === revokedId); }, }); - it('should throw if token is revoked', function() { + it('should throw if token is revoked', async () => { const req = {}; const res = {}; - const token = jwt.sign({jti: revokedId, foo: 'bar'}, secret); + const token = jwt.sign({ jti: revokedId, foo: 'bar' }, secret); - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; + req.headers = { authorization: `Bearer ${token}` }; - middleware(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.body.code, 'Unauthorized'); - assert.strictEqual(err.message, 'The token has been revoked.'); + await new Promise((resolve) => { + middleware(req, res, (err) => { + expect(err).toBeDefined(); + expect(err.body.code).toBe('Unauthorized'); + expect(err.message).toBe('The token has been revoked.'); + resolve(); + }); }); }); - it('should work if token is not revoked', function() { + it('should work if token is not revoked', async () => { const req = {}; const res = {}; - const token = jwt.sign({jti: '1233', foo: 'bar'}, secret); + const token = jwt.sign({ jti: '1233', foo: 'bar' }, secret); - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; + req.headers = { authorization: `Bearer ${token}` }; - middleware(req, res, function() { - assert.strictEqual('bar', req.user.foo); + await new Promise((resolve) => { + middleware(req, res, () => { + expect(req.user.foo).toBe('bar'); + resolve(); + }); }); }); - it('should throw if error occurs checking if token is revoked', function() { + it('should throw if error occurs checking if token is revoked', async () => { const req = {}; const res = {}; - const token = jwt.sign({jti: revokedId, foo: 'bar'}, secret); - - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - - restifyJWT({ - secret: secret, - isRevoked: function(req, payload, done) { - done(new Error('An error ocurred')); - }, - })(req, res, function(err) { - assert.ok(err); - assert.strictEqual(err.message, 'An error ocurred'); + const token = jwt.sign({ jti: revokedId, foo: 'bar' }, secret); + + req.headers = { authorization: `Bearer ${token}` }; + + await new Promise((resolve) => { + restifyJWT({ + secret: secret, + isRevoked: (req, payload, done) => { + done(new Error('An error occurred')); + }, + })(req, res, (err) => { + expect(err).toBeDefined(); + expect(err.message).toBe('An error occurred'); + resolve(); + }); }); }); -}); +}); \ No newline at end of file diff --git a/test/string-token.test.js b/test/string-token.test.js index 249982f1..32e81daf 100644 --- a/test/string-token.test.js +++ b/test/string-token.test.js @@ -1,19 +1,21 @@ -const assert = require('assert'); const jwt = require('jsonwebtoken'); const restifyJWT = require('../lib'); -describe('string tokens', function() { +describe('string tokens', () => { const req = {}; const res = {}; - it('should work with a valid string token', function() { + it('should work with a valid string token', async () => { const secret = 'shhhhhh'; const token = jwt.sign('foo', secret); - req.headers = {}; - req.headers.authorization = 'Bearer ' + token; - restifyJWT({secret: secret})(req, res, function() { - assert.strictEqual('foo', req.user); + req.headers = { authorization: `Bearer ${token}` }; + + await new Promise((resolve) => { + restifyJWT({ secret })(req, res, () => { + expect(req.user).toBe('foo'); + resolve(); + }); }); }); -}); +}); \ No newline at end of file