From 192b5b6d297a310936a47f7c21f7bc414161b917 Mon Sep 17 00:00:00 2001 From: joelchu Date: Sun, 10 Nov 2019 21:24:43 +0800 Subject: [PATCH 1/4] Fix the problem when the create jwt validator throw error --- packages/jwt/package.json | 16 ++++++++-------- .../src/server/auth/create-token-validator.js | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/jwt/package.json b/packages/jwt/package.json index 5967f6bf..bbc73430 100644 --- a/packages/jwt/package.json +++ b/packages/jwt/package.json @@ -1,6 +1,6 @@ { "name": "jsonql-jwt", - "version": "1.3.3", + "version": "1.3.4", "description": "jwt authentication helpers library for jsonql browser / node", "main": "main.js", "module": "index.js", @@ -38,10 +38,10 @@ "dependencies": { "colors": "^1.4.0", "fs-extra": "^8.1.0", - "jsonql-constants": "^1.8.5", - "jsonql-errors": "^1.1.3", + "jsonql-constants": "^1.8.10", + "jsonql-errors": "^1.1.5", "jsonql-params-validator": "^1.4.11", - "jsonql-utils": "^0.7.6", + "jsonql-utils": "^0.8.3", "jsonwebtoken": "^8.5.1", "jwt-decode": "^2.2.0", "socketio-jwt": "^4.5.0", @@ -52,13 +52,13 @@ }, "devDependencies": { "socket.io-client": "^2.3.0", - "ws": "^7.1.2", + "ws": "^7.2.0", "ava": "^2.4.0", "debug": "^4.1.1", "esm": "^3.2.25", - "koa": "^2.10.0", - "rollup": "^1.24.0", - "rollup-plugin-alias": "^2.0.1", + "koa": "^2.11.0", + "rollup": "^1.26.4", + "rollup-plugin-alias": "^2.2.0", "rollup-plugin-async": "^1.2.0", "rollup-plugin-buble": "^0.19.8", "rollup-plugin-bundle-size": "^1.0.3", diff --git a/packages/jwt/src/server/auth/create-token-validator.js b/packages/jwt/src/server/auth/create-token-validator.js index 7f906e4f..4273ba65 100644 --- a/packages/jwt/src/server/auth/create-token-validator.js +++ b/packages/jwt/src/server/auth/create-token-validator.js @@ -1,5 +1,5 @@ const { join, resolve } = require('path') -const { JsonqlValidatorError } = require('jsonql-errors') +const { JsonqlValidationError } = require('jsonql-errors') const { isString } = require('jsonql-params-validator') const { RSA_ALGO, HSA_ALGO } = require('jsonql-constants') const jwtDecode = require('../../jwt/jwt-decode') @@ -22,7 +22,7 @@ module.exports = function createTokenValidator(config) { opts = { algorithms: RSA_ALGO } } if (!key) { - throw new JsonqlValidatorError(`key is not provided!`) + throw new JsonqlValidationError(`key is not provided!`) } return function tokenValidator(token) { return jwtDecode(token, key, opts) -- Gitee From a756a1e77304ff24dee5ffd29fdf3e421705acfa Mon Sep 17 00:00:00 2001 From: joelchu Date: Sun, 10 Nov 2019 22:30:00 +0800 Subject: [PATCH 2/4] breaking up the auth-middleware --- packages/koa/package.json | 2 +- .../koa/src/middlewares/auth-middleware.js | 121 ++---------------- packages/koa/src/options/process-jwt-keys.js | 4 +- .../koa/src/utils/auth-middleware-helpers.js | 112 ++++++++++++++++ packages/koa/tests/auth.test.js | 25 ++-- 5 files changed, 143 insertions(+), 121 deletions(-) create mode 100644 packages/koa/src/utils/auth-middleware-helpers.js diff --git a/packages/koa/package.json b/packages/koa/package.json index 4cd16f03..f0071cdb 100644 --- a/packages/koa/package.json +++ b/packages/koa/package.json @@ -76,7 +76,7 @@ "jsonql-constants": "^1.8.10", "jsonql-contract": "^1.8.4", "jsonql-errors": "^1.1.5", - "jsonql-jwt": "^1.3.3", + "jsonql-jwt": "^1.3.4", "jsonql-node-client": "^1.2.2", "jsonql-params-validator": "^1.4.11", "jsonql-resolver": "^0.9.4", diff --git a/packages/koa/src/middlewares/auth-middleware.js b/packages/koa/src/middlewares/auth-middleware.js index 52829346..88b34f31 100644 --- a/packages/koa/src/middlewares/auth-middleware.js +++ b/packages/koa/src/middlewares/auth-middleware.js @@ -1,119 +1,26 @@ -// jsonql-auth middleware +// auth middleware +import { AUTH_TYPE, NOT_FOUND_STATUS } from 'jsonql-constants' +import { JsonqlResolverNotFoundError } from 'jsonql-errors' import { - AUTH_TYPE, - ISSUER_NAME, - VALIDATOR_NAME, - AUTH_CHECK_HEADER, - BEARER -} from 'jsonql-constants' -import { - chainFns, + // chainFns, getDebug, - packResult, - headerParser, - printError, - isNotEmpty, - handleOutput, + // packResult, + // headerParser, + // printError, + // handleOutput, forbiddenHandler, ctxErrorHandler, - - createTokenValidator, // import from the jwt - + isNotEmpty, isObject } from '../utils' import { - JsonqlResolverNotFoundError, - JsonqlAuthorisationError, - JsonqlValidationError, - finalCatch -} from 'jsonql-errors' -// this method just search if the user provide their own validate token method -import { getLocalValidator } from 'jsonql-resolver' -import { trim } from 'lodash' - + authHeaderParser, + getToken, + getValidator +} from '../utils/auth-middleware-helpers' const debug = getDebug('auth-middleware') - -// this will create a cache version without keep calling the getter -var validatorFn; - -// declare a global variable to store the userdata with null value -// this way we don't mess up with the resolver have to check -// the last param is the user data - -/** - * @param {object} ctx Koa context - * @param {string} type to look for - * @return {mixed} the bearer token on success - */ -const authHeaderParser = ctx => { - // const header = headers[AUTH_CHECK_HEADER]; - let header = ctx.request.get(AUTH_CHECK_HEADER) - // debug(_header, AUTH_CHECK_HEADER); - // debug('Did we get the token?', header); - return header ? getToken(header) : false; -} - -/** - * just return the token string - * @param {string} header - * @return {string} token - */ -const getToken = header => { - return trim(header.replace(BEARER, '')) -} - -/** - * when using useJwt we allow the user to provide their own validator - * and we pass the result to this validator for them to do further processing - * This is useful because the user can control if they want to invalidate the client side - * from here based on their need - * @param {object} config configuration - * @param {function|boolean} validator false if there is none - * @return {function} the combine validator - */ -const createJwtValidatorChain = (config, validator = false) => { - const jwtFn = createTokenValidator(config) - if (!validator || typeof validator !== 'function') { - return jwtFn; - } - - return chainFns(jwtFn, validator) -} - -/** - * if useJwt = true then use the jsonql-jwt version - * @param {object} config configuration - * @param {string} type type of call - * @param {object} contract contract.json - * @return {function} the correct handler - */ -const getValidator = (config, type, contract) => { - if (validatorFn && typeof validatorFn === 'function') { - debug(`return the cache validatorFn`) - return validatorFn; - } - let localValidator; - try { - localValidator = getLocalValidator(config, type, contract) - } catch(e) { - const checkErr = e instanceof JsonqlResolverNotFoundError - // debug('checkErr', checkErr) - // we ignore this error becasue they might not have one? - if (!checkErr) { - return finalCatch(e) - } - } - // if (config.useJwt) { // we always use the jwt opiton now - ///debug(`return the jwt validation chain`) - return createJwtValidatorChain(config, localValidator) - //} - // debug(`return a local validator`) - // return localValidator; -} - /** * Auth middleware, we support - * 1) OAuth 2 * 2) JWT token * This is just front we don't do any real auth here, * instead we expect you to supply a function and pass you data and let you @@ -149,7 +56,7 @@ export default function authMiddleware(config) { } } catch(e) { if (e instanceof JsonqlResolverNotFoundError) { - return ctxErrorHandler(ctx, 404, e) + return ctxErrorHandler(ctx, NOT_FOUND_STATUS, e) } else { debug('throw at some where throw error', e) return forbiddenHandler(ctx, e) diff --git a/packages/koa/src/options/process-jwt-keys.js b/packages/koa/src/options/process-jwt-keys.js index a6100119..19fabd42 100644 --- a/packages/koa/src/options/process-jwt-keys.js +++ b/packages/koa/src/options/process-jwt-keys.js @@ -24,8 +24,8 @@ const getKeysFromCache = (ctx, config) => { // @TODO need to check the getter sequence, also allow supply a setter and getter const { setter, getter } = ctx.state.jsonql; if (config.enableAuth && - config.useJwt && - !isString(config.useJwt) && + config.useJwt && // @TODO need to remove this + !isString(config.useJwt) && // @TODO need to change this key name (!config.publicKey || !config.privateKey)) { if (typeof getter === 'function') { let privateKey = getter('privateKey') diff --git a/packages/koa/src/utils/auth-middleware-helpers.js b/packages/koa/src/utils/auth-middleware-helpers.js new file mode 100644 index 00000000..6772415f --- /dev/null +++ b/packages/koa/src/utils/auth-middleware-helpers.js @@ -0,0 +1,112 @@ +// Taking the methods out from the auth-middleware to keep it simple +// jsonql-auth middleware +import { + AUTH_TYPE, + ISSUER_NAME, + VALIDATOR_NAME, + AUTH_CHECK_HEADER, + BEARER +} from 'jsonql-constants' +import { + chainFns, + getDebug, + // packResult, + // headerParser, + // printError, + // isNotEmpty, + // handleOutput, + forbiddenHandler, + ctxErrorHandler, + + createTokenValidator, // import from the jwt + + isObject +} from '../utils' +import { + JsonqlResolverNotFoundError, + JsonqlAuthorisationError, + JsonqlValidationError, + finalCatch +} from 'jsonql-errors' +// this method just search if the user provide their own validate token method +import { getLocalValidator } from 'jsonql-resolver' +import { trim } from 'lodash' + +const debug = getDebug('auth-middleware-helpers') + +// this will create a cache version without keep calling the getter +var validatorFn; + +// declare a global variable to store the userdata with null value +// this way we don't mess up with the resolver have to check +// the last param is the user data + +/** + * @param {object} ctx Koa context + * @param {string} type to look for + * @return {mixed} the bearer token on success + */ +export const authHeaderParser = ctx => { + // const header = headers[AUTH_CHECK_HEADER]; + let header = ctx.request.get(AUTH_CHECK_HEADER) + // debug(_header, AUTH_CHECK_HEADER); + // debug('Did we get the token?', header); + return header ? getToken(header) : false; +} + +/** + * just return the token string + * @param {string} header + * @return {string} token + */ +export const getToken = header => trim(header.replace(BEARER, '')) + + +/** + * when using useJwt we allow the user to provide their own validator + * and we pass the result to this validator for them to do further processing + * This is useful because the user can control if they want to invalidate the client side + * from here based on their need + * @param {object} config configuration + * @param {function|boolean} validator false if there is none + * @return {function} the combine validator + */ +const createJwtValidatorChain = (config, validator = false) => { + const jwtFn = createTokenValidator(config) + if (!validator || typeof validator !== 'function') { + return jwtFn; + } + + return chainFns(jwtFn, validator) +} + +/** + * if useJwt = true then use the jsonql-jwt version + * @param {object} config configuration + * @param {string} type type of call + * @param {object} contract contract.json + * @return {function} the correct handler + */ +export const getValidator = (config, type, contract) => { + if (validatorFn && typeof validatorFn === 'function') { + debug(`return the cache validatorFn`) + return validatorFn; + } + let localValidator; + try { + localValidator = getLocalValidator(config, type, contract) + } catch(e) { + const checkErr = e instanceof JsonqlResolverNotFoundError + // debug('checkErr', checkErr) + // we ignore this error becasue they might not have one? + if (!checkErr) { + return finalCatch(e) + } + } + // if (config.useJwt) { // we always use the jwt opiton now + ///debug(`return the jwt validation chain`) + return createJwtValidatorChain(config, localValidator) + //} + // debug(`return a local validator`) + // return localValidator; +} diff --git a/packages/koa/tests/auth.test.js b/packages/koa/tests/auth.test.js index 9081738d..8b606775 100644 --- a/packages/koa/tests/auth.test.js +++ b/packages/koa/tests/auth.test.js @@ -13,23 +13,25 @@ const createServer = require('./helpers/server') const myKey = '4670994sdfkl'; const dir = 'auth'; const thisHeader = merge({}, {[contractKeyName]: myKey}, headers) +const keysDir = join(join(dirs.contractDir, 'auth-keys')) test.before((t) => { t.context.app = createServer({ - useJwt: false, enableAuth: true, - contractKey: myKey + contractKey: myKey, + keysDir }, dir) }) test.after( () => { // remove the files after fsx.removeSync(join(dirs.contractDir, dir)) + fsx.removeSync(keysDir) }) // Start running test(s) -test("Should NOT fail this Hello world test even I am not login", async t => { +test.serial("Should NOT fail this Hello world test even I am not login", async t => { let res = await superkoa(t.context.app) .post('/jsonql') .query({_cb: Date.now()}) @@ -40,7 +42,7 @@ test("Should NOT fail this Hello world test even I am not login", async t => { t.is(200, res.status) }) -test("The public-contract.json file should contain issuer information", async t => { +test.serial("The public-contract.json file should contain issuer information", async t => { let res = await superkoa(t.context.app) .get('/jsonql') .query({_cb: Date.now()}) @@ -50,7 +52,7 @@ test("The public-contract.json file should contain issuer information", async t }) -test('Should able to login with this credential', async t => { +test.serial('Should able to login with this credential', async t => { let res = await superkoa(t.context.app) .post('/jsonql') .query({_cb: Date.now()}) @@ -59,10 +61,11 @@ test('Should able to login with this credential', async t => { createQuery('login', ['nobody', myKey]) ); t.is(200, res.status) - t.is(bearer, res.body.data) + // we are not using the useJwt anymore, so the token is different from the key + // t.is(bearer, res.body.data) }) -test('Should cause a JsonqlAuthorisationError if I pass the wrong username or password', async t => { +test.serial('Should cause a JsonqlAuthorisationError if I pass the wrong username or password', async t => { let res = await superkoa(t.context.app) .post('/jsonql') .query({_cb: Date.now()}) @@ -76,7 +79,7 @@ test('Should cause a JsonqlAuthorisationError if I pass the wrong username or pa }) -test("It should able to call a method that is mark as public without login", async t => { +test.serial("It should able to call a method that is mark as public without login", async t => { // alwaysAvailable let res = await superkoa(t.context.app) .post('/jsonql') @@ -89,20 +92,20 @@ test("It should able to call a method that is mark as public without login", asy t.is('Hello there', res.body.data) }) -test('Now I should able to call the api with credential', async t => { +test.serial('Now I should able to call the api with credential', async t => { let res = await superkoa(t.context.app) .post('/jsonql') .query({_cb: Date.now()}) .set(merge({} , thisHeader, {Authorization: `Bearer ${bearer}`})) .send( createQuery('getUser', ['testing', 'userId']) - ); + ) t.is(200, res.status) // debug(res.body) t.is(1, res.body.data.userId) }) -test("Should NOT able to call the logout method without the Bearer", async t => { +test.serial("Should NOT able to call the logout method without the Bearer", async t => { let res = await superkoa(t.context.app) .post('/jsonql') .query({_cb: Date.now()}) -- Gitee From 4e3377b9625d2f7fe9f2da4610bd04f184b7f8bf Mon Sep 17 00:00:00 2001 From: joelchu Date: Sun, 10 Nov 2019 22:51:58 +0800 Subject: [PATCH 3/4] have to restore the useJwt option because if I remove it now could lead to whole lot of problems --- .../koa/src/middlewares/auth-middleware.js | 7 +------ .../middlewares/public-method-middleware.js | 2 +- .../koa/src/utils/auth-middleware-helpers.js | 20 +++++++------------ packages/koa/tests/auth.test.js | 7 ++++--- .../tests/fixtures/resolvers/auth/login.js | 6 +++--- .../fixtures/resolvers/auth/validator.js | 10 +++++----- packages/resolver/src/handle-auth-methods.js | 10 +++++----- 7 files changed, 26 insertions(+), 36 deletions(-) diff --git a/packages/koa/src/middlewares/auth-middleware.js b/packages/koa/src/middlewares/auth-middleware.js index 88b34f31..c3d72927 100644 --- a/packages/koa/src/middlewares/auth-middleware.js +++ b/packages/koa/src/middlewares/auth-middleware.js @@ -2,12 +2,7 @@ import { AUTH_TYPE, NOT_FOUND_STATUS } from 'jsonql-constants' import { JsonqlResolverNotFoundError } from 'jsonql-errors' import { - // chainFns, getDebug, - // packResult, - // headerParser, - // printError, - // handleOutput, forbiddenHandler, ctxErrorHandler, isNotEmpty, @@ -37,7 +32,7 @@ export default function authMiddleware(config) { const token = authHeaderParser(ctx) if (token) { debug('got a token', token) - validatorFn = getValidator(config , AUTH_TYPE, contract) + let validatorFn = getValidator(config , AUTH_TYPE, contract) let userdata = await validatorFn(token) debug('validatorFn result', userdata) if (isNotEmpty(userdata) && isObject(userdata)) { diff --git a/packages/koa/src/middlewares/public-method-middleware.js b/packages/koa/src/middlewares/public-method-middleware.js index c9548312..801dedfd 100644 --- a/packages/koa/src/middlewares/public-method-middleware.js +++ b/packages/koa/src/middlewares/public-method-middleware.js @@ -19,7 +19,7 @@ export default function publicMethodMiddleware(opts) { return resolveMethod(ctx, resolverType, opts, contract) } } else if (resolverType === AUTH_TYPE && resolverName === opts.loginHandlerName) { - debug(`This is an auth ${opts.loginHandlerName} call`); + debug(`This is an auth ${opts.loginHandlerName} call`) return handleAuthMethods(ctx, resolverName, payload, opts, contract) } } diff --git a/packages/koa/src/utils/auth-middleware-helpers.js b/packages/koa/src/utils/auth-middleware-helpers.js index 6772415f..e7214136 100644 --- a/packages/koa/src/utils/auth-middleware-helpers.js +++ b/packages/koa/src/utils/auth-middleware-helpers.js @@ -10,16 +10,9 @@ import { import { chainFns, getDebug, - // packResult, - // headerParser, - // printError, - // isNotEmpty, - // handleOutput, forbiddenHandler, ctxErrorHandler, - createTokenValidator, // import from the jwt - isObject } from '../utils' import { @@ -96,6 +89,7 @@ export const getValidator = (config, type, contract) => { try { localValidator = getLocalValidator(config, type, contract) } catch(e) { + debug('localValidator throw error', e) const checkErr = e instanceof JsonqlResolverNotFoundError // debug('checkErr', checkErr) // we ignore this error becasue they might not have one? @@ -103,10 +97,10 @@ export const getValidator = (config, type, contract) => { return finalCatch(e) } } - // if (config.useJwt) { // we always use the jwt opiton now - ///debug(`return the jwt validation chain`) - return createJwtValidatorChain(config, localValidator) - //} - // debug(`return a local validator`) - // return localValidator; + if (config.useJwt) { // we always use the jwt opiton now + debug(`return the jwt validation chain`) + return createJwtValidatorChain(config, localValidator) + } + debug(`return a local validator`, localValidator) + return localValidator; } diff --git a/packages/koa/tests/auth.test.js b/packages/koa/tests/auth.test.js index 8b606775..e153d792 100644 --- a/packages/koa/tests/auth.test.js +++ b/packages/koa/tests/auth.test.js @@ -17,16 +17,17 @@ const keysDir = join(join(dirs.contractDir, 'auth-keys')) test.before((t) => { t.context.app = createServer({ + useJwt: false, enableAuth: true, - contractKey: myKey, - keysDir + contractKey: myKey + // keysDir }, dir) }) test.after( () => { // remove the files after fsx.removeSync(join(dirs.contractDir, dir)) - fsx.removeSync(keysDir) + // fsx.removeSync(keysDir) }) // Start running test(s) diff --git a/packages/koa/tests/fixtures/resolvers/auth/login.js b/packages/koa/tests/fixtures/resolvers/auth/login.js index 5f8d1e79..4b73763e 100644 --- a/packages/koa/tests/fixtures/resolvers/auth/login.js +++ b/packages/koa/tests/fixtures/resolvers/auth/login.js @@ -1,5 +1,5 @@ -const debug = require('debug')('jsonql:koa:issuer'); -const { bearer } = require('../../options'); +const debug = require('debug')('jsonql:koa:issuer') +const { bearer } = require('../../options') // this is the stock method for giving out author header @@ -10,7 +10,7 @@ const { bearer } = require('../../options'); * @return {string|boolean} token on success, false on fail */ module.exports = function(username, password) { - debug('received this', username, password); + debug('received this', username, password) if (username === 'nobody' && password) { return bearer; } diff --git a/packages/koa/tests/fixtures/resolvers/auth/validator.js b/packages/koa/tests/fixtures/resolvers/auth/validator.js index b588aa81..939af888 100644 --- a/packages/koa/tests/fixtures/resolvers/auth/validator.js +++ b/packages/koa/tests/fixtures/resolvers/auth/validator.js @@ -1,14 +1,14 @@ -const debug = require('debug')('jsonql:koa:validator'); -const { bearer } = require('../../options'); +const debug = require('debug')('jsonql:koa:validator') +const { bearer } = require('../../options') // this is the core method for checking the Auth header /** * @param {string} token jwt * @return {object|boolean} user data on success, false on fail */ -module.exports = function(token) { - debug('received this token', token, bearer); +module.exports = function validator(token) { + debug('received this token', token, bearer) if (token === bearer) { - return {userId: 1}; + return {userId: 1} } return false; } diff --git a/packages/resolver/src/handle-auth-methods.js b/packages/resolver/src/handle-auth-methods.js index 38d416a9..33b3d443 100644 --- a/packages/resolver/src/handle-auth-methods.js +++ b/packages/resolver/src/handle-auth-methods.js @@ -1,9 +1,4 @@ // Auth methods handler -const { getDebug } = require('./utils') -const { searchResolvers } = require('./search-resolvers') -const { validateAndCall } = require('./validate-and-call') - -const { handleOutput, packResult, ctxErrorHandler } = require('jsonql-utils') const { QUERY_NAME, MUTATION_NAME, @@ -20,6 +15,11 @@ const { getErrorNameByInstance, UNKNOWN_ERROR } = require('jsonql-errors') +const { handleOutput, packResult, ctxErrorHandler } = require('jsonql-utils') + +const { getDebug } = require('./utils') +const { searchResolvers } = require('./search-resolvers') +const { validateAndCall } = require('./validate-and-call') const debug = getDebug('public-method-middleware') -- Gitee From 492736795a12e58a0b8e2b465bf929783e89de09 Mon Sep 17 00:00:00 2001 From: joelchu Date: Sun, 10 Nov 2019 22:53:10 +0800 Subject: [PATCH 4/4] jsonql-koa to 1.4.9 --- packages/koa/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/koa/package.json b/packages/koa/package.json index f0071cdb..a91956ed 100644 --- a/packages/koa/package.json +++ b/packages/koa/package.json @@ -1,6 +1,6 @@ { "name": "jsonql-koa", - "version": "1.4.8", + "version": "1.4.9", "description": "jsonql Koa middleware", "main": "main.js", "module": "index.js", -- Gitee