From 7b63c788f40f039bf81281a9b0bacc66d582ade1 Mon Sep 17 00:00:00 2001 From: Liran Tal Date: Tue, 12 Sep 2017 23:01:01 +0300 Subject: [PATCH] feat(jwt): adding initial support for jwt authentication --- server/config.js | 3 ++ .../users/server/config/strategies/jwt.js | 28 ++++++++++++++++++ .../users/server/config/strategies/local.js | 4 +-- .../users.authentication.server.controller.js | 29 +++++++++++++++++++ .../users/server/routes/auth.server.routes.js | 10 +++++++ .../users/server/services/user.service.js | 4 +++ server/package.json | 4 ++- 7 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 server/modules/users/server/config/strategies/jwt.js diff --git a/server/config.js b/server/config.js index 97f566d27..200937351 100644 --- a/server/config.js +++ b/server/config.js @@ -145,6 +145,9 @@ module.exports = { minOptionalTestsToPass: 4 } }, + jwt: { + secret: process.env.FACEBOOK_ID || 'test' + }, facebook: { clientID: process.env.FACEBOOK_ID || 'APP_ID', clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET', diff --git a/server/modules/users/server/config/strategies/jwt.js b/server/modules/users/server/config/strategies/jwt.js new file mode 100644 index 000000000..37391f654 --- /dev/null +++ b/server/modules/users/server/config/strategies/jwt.js @@ -0,0 +1,28 @@ +'use strict' + +const passport = require('passport') +const passportJwt = require('passport-jwt') +const UserService = require('../../services/user.service') + +const JwtStrategy = passportJwt.Strategy +const ExtractJwt = passportJwt.ExtractJwt + +module.exports = function (config) { + passport.use(new JwtStrategy({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + secretOrKey: config.jwt.secret + }, async (jwtPayload, done) => { + try { + const user = await UserService.getUserDeserializedById(jwtPayload.id) + if (user) { + return done(null, user) + } else { + return done(null, false, { + message: 'Incorrect token' + }) + } + } catch (err) { + return done(err) + } + })) +} diff --git a/server/modules/users/server/config/strategies/local.js b/server/modules/users/server/config/strategies/local.js index 6ee1168a7..4049d2cd9 100644 --- a/server/modules/users/server/config/strategies/local.js +++ b/server/modules/users/server/config/strategies/local.js @@ -1,4 +1,4 @@ -'use strict'; +'use strict' /** * Module dependencies @@ -8,7 +8,7 @@ const LocalStrategy = require('passport-local').Strategy // const User = require('mongoose').model('User') const UserService = require('../../services/user.service') -module.exports = function () { +module.exports = function (config) { passport.use(new LocalStrategy({ usernameField: 'usernameOrEmail', passwordField: 'password' diff --git a/server/modules/users/server/controllers/users/users.authentication.server.controller.js b/server/modules/users/server/controllers/users/users.authentication.server.controller.js index a48249cc1..c83ee8619 100644 --- a/server/modules/users/server/controllers/users/users.authentication.server.controller.js +++ b/server/modules/users/server/controllers/users/users.authentication.server.controller.js @@ -4,9 +4,11 @@ * Module dependencies */ const path = require('path') +const config = require(path.resolve('./lib/config')) const errorHandler = require(path.resolve('./modules/core/server/controllers/errors.server.controller')) const mongoose = require('mongoose') const passport = require('passport') +const jwt = require('jsonwebtoken') const User = mongoose.model('User') const UserService = require('../../services/user.service') @@ -44,6 +46,33 @@ exports.signout = function (req, res) { return res.status(200).send() } +/** + * Jwt Token Auth + */ +exports.token = async function (req, res) { + try { + // Authenticate the user based on credentials + // @TODO be consistent with whether the login field for user identification + // is a username or an email + const username = req.body.email + const password = req.body.password + const user = await UserService.authenticate(username, password) + + // Create the token and send + // @TODO properly create the token with all of its metadata + const payload = { + id: user.id + } + // @TODO properly sign the token, not with a shared secret (use pubkey instead), + // and specify proper expiration, issuer, algorithm, etc. + const token = jwt.sign(payload, config.jwt.secret) + + res.status(200).json({token: token}) + } catch (err) { + return res.status(500).send(err.message) + } +} + /** * OAuth provider call */ diff --git a/server/modules/users/server/routes/auth.server.routes.js b/server/modules/users/server/routes/auth.server.routes.js index 24a35e661..f6f5a2738 100644 --- a/server/modules/users/server/routes/auth.server.routes.js +++ b/server/modules/users/server/routes/auth.server.routes.js @@ -16,6 +16,16 @@ module.exports = function (app) { app.route('/api/auth/signin').post(passport.authenticate('local'), users.signin) app.route('/api/auth/signout').post(users.signout) + // Jwt token + app.route('/api/auth/token').post(users.token) + // Jwt protected route example: + // app.route('/api/auth/secretPlace').get(passport.authenticate('jwt'), (req, res) => { + // console.log(req.user) + // console.log(req.session) + // console.log(req.isAuthenticated()) + // res.status(200).send() + // }) + // Setting the facebook oauth routes app.route('/api/auth/facebook').get(users.oauthCall('facebook', { scope: ['email'] diff --git a/server/modules/users/server/services/user.service.js b/server/modules/users/server/services/user.service.js index ffccc1e8c..3b698418e 100644 --- a/server/modules/users/server/services/user.service.js +++ b/server/modules/users/server/services/user.service.js @@ -15,6 +15,10 @@ const SALT_ROUNDS = 10 class UserService { static deserialize (user) { + if (!user || typeof user !== 'object') { + return null + } + return { id: user.id, displayName: user.displayName, diff --git a/server/package.json b/server/package.json index e4d9e1ab7..9a8f520c1 100644 --- a/server/package.json +++ b/server/package.json @@ -49,6 +49,7 @@ "gulp-ava": "^0.15.0", "helmet": "~2.3.0", "jasmine-core": "~2.5.0", + "jsonwebtoken": "^8.0.0", "lodash": "~4.16.2", "lusca": "~1.4.1", "method-override": "~2.3.5", @@ -59,10 +60,11 @@ "nodemailer": "~2.6.4", "nodemon": "^1.11.0", "owasp-password-strength-test": "~1.3.0", - "passport": "~0.3.2", + "passport": "^0.3.2", "passport-facebook": "~2.1.0", "passport-github": "~1.1.0", "passport-google-oauth": "~1.0.0", + "passport-jwt": "^3.0.0", "passport-linkedin": "~1.0.0", "passport-local": "~1.0.0", "passport-paypal-openidconnect": "~0.1.1",