From df4b5cced718074dca7c6a9d7a5a283813e6b862 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 6 Jul 2020 09:35:23 -0400 Subject: [PATCH] removes duplicative JWT strategies, flattens cookie jwt retrieval into JWT strategy itself, removes sessions --- src/auth/getExtractJWT.js | 19 +++++++++++++++++++ src/auth/graphql/resolvers/refresh.js | 6 +++++- src/auth/init.js | 10 ++++++++++ src/auth/operations/refresh.js | 5 ++--- src/auth/requestHandlers/refresh.js | 6 +++++- src/auth/strategies/jwt.js | 15 ++++++++++----- src/collections/init.js | 5 ----- src/express/middleware/authenticate.js | 15 ++++----------- .../middleware/createAuthHeaderFromCookie.js | 15 --------------- src/express/middleware/index.js | 6 ------ src/express/static.js | 3 --- src/index.js | 6 +++++- 12 files changed, 60 insertions(+), 51 deletions(-) create mode 100644 src/auth/getExtractJWT.js create mode 100644 src/auth/init.js delete mode 100644 src/express/middleware/createAuthHeaderFromCookie.js diff --git a/src/auth/getExtractJWT.js b/src/auth/getExtractJWT.js new file mode 100644 index 0000000000..876572133e --- /dev/null +++ b/src/auth/getExtractJWT.js @@ -0,0 +1,19 @@ +const getExtractJWT = config => (req) => { + const jwtFromHeader = req.get('Authorization'); + + if (jwtFromHeader && jwtFromHeader.indexOf('JWT ') === 0) { + return jwtFromHeader.replace('JWT ', ''); + } + + if (req.cookies) { + const jwt = req.cookies[`${config.cookiePrefix}-token`]; + + if (jwt) { + return jwt; + } + } + + return null; +}; + +module.exports = getExtractJWT; diff --git a/src/auth/graphql/resolvers/refresh.js b/src/auth/graphql/resolvers/refresh.js index b74733db3a..28041d91bb 100644 --- a/src/auth/graphql/resolvers/refresh.js +++ b/src/auth/graphql/resolvers/refresh.js @@ -1,11 +1,15 @@ /* eslint-disable no-param-reassign */ const { refresh } = require('../../operations'); +const getExtractJWT = require('../../getExtractJWT'); const refreshResolver = (config, collection) => async (_, __, context) => { + const extractJWT = getExtractJWT(config); + const token = extractJWT(context); + const options = { config, collection, - authorization: context.headers.authorization, + token, req: context, }; diff --git a/src/auth/init.js b/src/auth/init.js new file mode 100644 index 0000000000..5a49fc5aa1 --- /dev/null +++ b/src/auth/init.js @@ -0,0 +1,10 @@ +const passport = require('passport'); +const AnonymousStrategy = require('passport-anonymous'); +const jwtStrategy = require('./strategies/jwt'); + +function initAuth() { + passport.use(new AnonymousStrategy.Strategy()); + passport.use('jwt', jwtStrategy(this.config, this.collections)); +} + +module.exports = initAuth; diff --git a/src/auth/operations/refresh.js b/src/auth/operations/refresh.js index 8caba2e127..b00edf130a 100644 --- a/src/auth/operations/refresh.js +++ b/src/auth/operations/refresh.js @@ -25,10 +25,9 @@ const refresh = async (args) => { const opts = {}; opts.expiresIn = options.collection.config.auth.tokenExpiration; - if (typeof options.authorization !== 'string') throw new Forbidden(); + if (typeof options.token !== 'string') throw new Forbidden(); - const token = options.authorization.replace('JWT ', ''); - const payload = jwt.verify(token, secret, {}); + const payload = jwt.verify(options.token, secret, {}); delete payload.iat; delete payload.exp; const refreshedToken = jwt.sign(payload, secret, opts); diff --git a/src/auth/requestHandlers/refresh.js b/src/auth/requestHandlers/refresh.js index 1064f74abd..03291e8ecd 100644 --- a/src/auth/requestHandlers/refresh.js +++ b/src/auth/requestHandlers/refresh.js @@ -1,15 +1,19 @@ const httpStatus = require('http-status'); const formatErrorResponse = require('../../express/responses/formatError'); const { refresh } = require('../operations'); +const getExtractJWT = require('../getExtractJWT'); const refreshHandler = config => async (req, res) => { try { + const extractJWT = getExtractJWT(config); + const token = extractJWT(req); + const result = await refresh({ req, res, collection: req.collection, config, - authorization: req.headers.authorization, + token, }); return res.status(200).json({ diff --git a/src/auth/strategies/jwt.js b/src/auth/strategies/jwt.js index 8c49c919ae..58eb9bacf3 100644 --- a/src/auth/strategies/jwt.js +++ b/src/auth/strategies/jwt.js @@ -1,11 +1,16 @@ const passportJwt = require('passport-jwt'); +const getExtractJWT = require('../getExtractJWT'); const JwtStrategy = passportJwt.Strategy; -const { ExtractJwt } = passportJwt; module.exports = (config, collections) => { - const opts = {}; - opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT'); + const opts = { + session: false, + }; + + const extractJWT = getExtractJWT(config); + + opts.jwtFromRequest = extractJWT; opts.secretOrKey = config.secret; return new JwtStrategy(opts, async (token, done) => { @@ -17,9 +22,9 @@ module.exports = (config, collections) => { const json = user.toJSON({ virtuals: true }); json.collection = collection.config.slug; - return done(null, json); + done(null, json); } catch (err) { - return done(null, false); + done(null, false); } }); }; diff --git a/src/collections/init.js b/src/collections/init.js index fe51008e3e..4c801a2788 100644 --- a/src/collections/init.js +++ b/src/collections/init.js @@ -8,7 +8,6 @@ const mongooseHidden = require('mongoose-hidden')({ const passport = require('passport'); const passportLocalMongoose = require('passport-local-mongoose'); const LocalStrategy = require('passport-local').Strategy; -const jwtStrategy = require('../auth/strategies/jwt'); const apiKeyStrategy = require('../auth/strategies/apiKey'); const collectionRoutes = require('./routes'); const buildSchema = require('./buildSchema'); @@ -137,10 +136,6 @@ function registerCollections() { passport.use(`${AuthCollection.config.slug}-api-key`, apiKeyStrategy(AuthCollection)); } - passport.use(`${AuthCollection.config.slug}-jwt`, jwtStrategy(this.config, this.collections)); - passport.serializeUser(AuthCollection.Model.serializeUser()); - passport.deserializeUser(AuthCollection.Model.deserializeUser()); - this.router.use(authRoutes(AuthCollection, this.config, this.sendEmail)); } else { this.router.use(collectionRoutes(this.collections[formattedCollection.slug])); diff --git a/src/express/middleware/authenticate.js b/src/express/middleware/authenticate.js index 09d40e4c84..d015c476a9 100644 --- a/src/express/middleware/authenticate.js +++ b/src/express/middleware/authenticate.js @@ -2,21 +2,14 @@ const passport = require('passport'); module.exports = (config) => { const methods = config.collections.reduce((enabledMethods, collection) => { - if (collection.auth) { - const collectionMethods = [ - `${collection.slug}-jwt`, - ...enabledMethods, - ]; - - if (collection.auth.enableAPIKey) { - collectionMethods.unshift(`${collection.slug}-api-key`); - } - + if (collection.auth && collection.auth.useAPIKey) { + const collectionMethods = [...enabledMethods]; + collectionMethods.unshift(`${collection.slug}-api-key`); return collectionMethods; } return enabledMethods; - }, ['anonymous']); + }, ['jwt', 'anonymous']); return passport.authenticate(methods, { session: false }); }; diff --git a/src/express/middleware/createAuthHeaderFromCookie.js b/src/express/middleware/createAuthHeaderFromCookie.js deleted file mode 100644 index 6e0b04e3bf..0000000000 --- a/src/express/middleware/createAuthHeaderFromCookie.js +++ /dev/null @@ -1,15 +0,0 @@ -const createAuthHeaderFromCookie = config => (req, _, next) => { - const existingAuthHeader = req.get('Authorization'); - - if (req.cookies) { - const token = req.cookies[`${config.cookiePrefix}-token`]; - - if (!existingAuthHeader && token) { - req.headers.authorization = `JWT ${token}`; - } - } - - next(); -}; - -module.exports = createAuthHeaderFromCookie; diff --git a/src/express/middleware/index.js b/src/express/middleware/index.js index 6e61850792..d30cfcc164 100644 --- a/src/express/middleware/index.js +++ b/src/express/middleware/index.js @@ -1,6 +1,5 @@ const express = require('express'); const passport = require('passport'); -const AnonymousStrategy = require('passport-anonymous'); const compression = require('compression'); const bodyParser = require('body-parser'); const methodOverride = require('method-override'); @@ -8,18 +7,13 @@ const cookieParser = require('cookie-parser'); const qsMiddleware = require('qs-middleware'); const fileUpload = require('express-fileupload'); const localizationMiddleware = require('../../localization/middleware'); -const createAuthHeaderFromCookie = require('./createAuthHeaderFromCookie'); const authenticate = require('./authenticate'); const identifyAPI = require('./identifyAPI'); const middleware = (config) => { - passport.use(new AnonymousStrategy.Strategy()); - return [ cookieParser(), - createAuthHeaderFromCookie(config), passport.initialize(), - passport.session(), authenticate(config), express.json(), methodOverride('X-HTTP-Method-Override'), diff --git a/src/express/static.js b/src/express/static.js index 31b8fc11af..f3124af899 100644 --- a/src/express/static.js +++ b/src/express/static.js @@ -3,7 +3,6 @@ const passport = require('passport'); const cookieParser = require('cookie-parser'); const getExecuteStaticPolicy = require('../auth/getExecuteStaticPolicy'); const authenticate = require('./middleware/authenticate'); -const createAuthHeaderFromCookie = require('./middleware/createAuthHeaderFromCookie'); function initStatic() { Object.entries(this.collections).forEach(([_, collection]) => { @@ -13,9 +12,7 @@ function initStatic() { const router = express.Router(); router.use(cookieParser()); - router.use(createAuthHeaderFromCookie(this.config)); router.use(passport.initialize()); - router.use(passport.session()); router.use(authenticate(this.config)); router.use(getExecuteStaticPolicy(collection)); diff --git a/src/index.js b/src/index.js index 6159acea61..d02d46405c 100644 --- a/src/index.js +++ b/src/index.js @@ -8,6 +8,7 @@ const authenticate = require('./express/middleware/authenticate'); const connectMongoose = require('./mongoose/connect'); const expressMiddleware = require('./express/middleware'); const initAdmin = require('./express/admin'); +const initAuth = require('./auth/init'); const initCollections = require('./collections/init'); const initGlobals = require('./globals/init'); const initStatic = require('./express/static'); @@ -30,6 +31,7 @@ class Payload { this.router = express.Router(); this.collections = {}; + this.initAuth = initAuth.bind(this); this.initCollections = initCollections.bind(this); this.initGlobals = initGlobals.bind(this); this.buildEmail = buildEmail.bind(this); @@ -43,8 +45,10 @@ class Payload { // Setup & initialization connectMongoose(this.config.mongoURL); - this.router.use(...expressMiddleware(this.config)); + this.router.use(...expressMiddleware(this.config, this.collections)); + + this.initAuth(); this.initCollections(); this.initGlobals(); this.initAdmin();