From 47d497824f6e7f4623d635a1743b66cb928b2dbf Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 Oct 2018 23:28:02 -0400 Subject: [PATCH 01/26] add passport packages to npm --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 2778391..22f0eba 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,9 @@ "express-rate-limit": "^3.2.1", "forever": "^0.15.3", "grunt": "^1.0.3", + "passport": "^0.4.0", + "passport-github2": "^0.1.11", + "passport-google-oauth20": "^1.0.0", "react": "^16.6.0", "react-dom": "^16.6.0", "sails": "^1.0.2", @@ -26,7 +29,6 @@ "@babel/polyfill": "^7.0.0", "@babel/preset-env": "^7.1.0", "@babel/preset-react": "^7.0.0", - "@sailshq/eslint": "^4.19.3", "babel-loader": "^8.0.4", "css-loader": "^1.0.1", "html-webpack-plugin": "^3.2.0", From cc22285ea9001f6db979890cccb861d64b20ee4c Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 30 Oct 2018 23:50:52 -0400 Subject: [PATCH 02/26] trying out prebuilt auth packages --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 22f0eba..39f1daf 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,15 @@ "passport": "^0.4.0", "passport-github2": "^0.1.11", "passport-google-oauth20": "^1.0.0", + "passport-local": "^1.0.0", "react": "^16.6.0", "react-dom": "^16.6.0", "sails": "^1.0.2", + "sails-auth": "^2.1.1", "sails-hook-grunt": "^3.0.2", "sails-hook-orm": "^2.1.1", - "sails-hook-sockets": "^1.4.0" + "sails-hook-sockets": "^1.4.0", + "sails-permissions": "^2.1.8" }, "devDependencies": { "@sailshq/eslint": "^4.19.3", From e1f42d3a203856b6cae152b67eefbe8328221385 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 00:03:21 -0400 Subject: [PATCH 03/26] add permissions generator --- .sailsrc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.sailsrc b/.sailsrc index 1a67e89..8750674 100644 --- a/.sailsrc +++ b/.sailsrc @@ -1,6 +1,8 @@ { "generators": { - "modules": {} + "modules": { + "permissions-api": "sails-permissions/generator" + } }, "_generatedWith": { "sails": "1.0.2", From 06861b6be92423692e573d48a5c2772fded71fc7 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 00:03:46 -0400 Subject: [PATCH 04/26] add policy and permissions configs --- assets/styles/lib/vars.scss | 2 +- config/http.js | 15 ++++++++++++++- config/permissions.js | 11 +++++++++++ config/policies.js | 17 +++++++++++++++-- 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 config/permissions.js diff --git a/assets/styles/lib/vars.scss b/assets/styles/lib/vars.scss index ca34ecc..8a44403 100644 --- a/assets/styles/lib/vars.scss +++ b/assets/styles/lib/vars.scss @@ -26,4 +26,4 @@ $accent-1: #4423c4; $accent-2: #4460c4; $accent-3: #D4DBF1; -$red: #FE4C52; \ No newline at end of file +$red: #FE4C52; diff --git a/config/http.js b/config/http.js index 601ed57..81e8847 100644 --- a/config/http.js +++ b/config/http.js @@ -18,6 +18,19 @@ const rateLimiter = rateLimit({ } }) +/*const passport = require('passport') +const LocalStrategy = require('passport-local') +passport.use(new LocalStrategy( + function (username, password, done) { + User.findOne({ username: username }, function (err, user) { + if (err) { return done(err) } + if (!user) { return done(null, false) } + if (!user.verifyPassword(password)) { return done(null, false) } + return done(null, user) + }) + } +))*/ + module.exports.http = { /**************************************************************************** @@ -49,7 +62,7 @@ module.exports.http = { 'www', 'favicon' ], - rateLimit: rateLimiter, + rateLimit: rateLimiter /*************************************************************************** * * diff --git a/config/permissions.js b/config/permissions.js new file mode 100644 index 0000000..d3c5658 --- /dev/null +++ b/config/permissions.js @@ -0,0 +1,11 @@ +// config/permissions.js + +var _ = require('lodash'); +var _super = require('sails-permissions/config/permissions'); + +_.merge(exports, _super); +_.merge(exports, { + + // Extend with custom logic here by adding additional fields, methods, etc. + +}); diff --git a/config/policies.js b/config/policies.js index c4d1bb5..5368665 100644 --- a/config/policies.js +++ b/config/policies.js @@ -17,6 +17,19 @@ module.exports.policies = { * * ***************************************************************************/ - // '*': true, + '*': [ + 'basicAuth', + 'passport', + 'sessionAuth', + 'ModelPolicy', + 'AuditPolicy', + 'OwnerPolicy', + 'PermissionPolicy', + 'RolePolicy', + 'CriteriaPolicy' + ], -}; + AuthController: { + '*': [ 'passport' ] + } +} From ae8c69ffebef04c650dc5c6c3ddf09e2b3981d49 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 00:03:54 -0400 Subject: [PATCH 05/26] add user model --- api/models/User.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 api/models/User.js diff --git a/api/models/User.js b/api/models/User.js new file mode 100644 index 0000000..4d99b77 --- /dev/null +++ b/api/models/User.js @@ -0,0 +1,34 @@ +/** + * User.js + * + * @description :: A model definition. Represents a database table/collection/etc. + * @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models + */ + +module.exports = { + + attributes: { + // ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗ + // ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗ + // ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝ + _id: { + type: 'string', + unique: true + }, + email: { + type: 'string', + unique: true + }, + password: 'string' + + // ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗ + // ║╣ ║║║╠╩╗║╣ ║║╚═╗ + // ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝ + + // ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗ + // ╠═╣╚═╗╚═╗║ ║║ ║╠═╣ ║ ║║ ║║║║╚═╗ + // ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝ + + } + +} From 5aa240812532d09b47e804c225fa8b1090f8525e Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 13:39:58 -0400 Subject: [PATCH 06/26] add bcrypt and basee64url packages --- package.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 39f1daf..996f3b2 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,8 @@ "@sailshq/lodash": "^3.10.3", "@sailshq/socket.io-redis": "^5.2.0", "async": "2.0.1", + "base64url": "^3.0.0", + "bcrypt": "^3.0.2", "express-rate-limit": "^3.2.1", "forever": "^0.15.3", "grunt": "^1.0.3", @@ -19,11 +21,9 @@ "react": "^16.6.0", "react-dom": "^16.6.0", "sails": "^1.0.2", - "sails-auth": "^2.1.1", "sails-hook-grunt": "^3.0.2", "sails-hook-orm": "^2.1.1", - "sails-hook-sockets": "^1.4.0", - "sails-permissions": "^2.1.8" + "sails-hook-sockets": "^1.4.0" }, "devDependencies": { "@sailshq/eslint": "^4.19.3", @@ -46,7 +46,7 @@ "webpack-dev-server": "^3.1.10" }, "scripts": { - "start": "npm run open:client", + "start": "npm-run-all --parallel open:client lift", "start:debug": "npm-run-all --parallel open:client debug", "start:prod": "npm-run-all --parallel build:prod lift", "open:client": "webpack-dev-server --mode development", @@ -70,5 +70,10 @@ "license": "", "engines": { "node": ">=8.10" + }, + "standard": { + "globals": [ + "sails" + ] } } From e301ef38b873c52296d695f6acb738b067945ed0 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 13:40:19 -0400 Subject: [PATCH 07/26] add new auth models and controllers --- api/controllers/AuthController.js | 115 ++++++++++++++++++++++++++++++ api/controllers/UserController.js | 31 ++++++++ api/models/Passport.js | 64 +++++++++++++++++ api/models/User.js | 5 +- 4 files changed, 213 insertions(+), 2 deletions(-) create mode 100644 api/controllers/AuthController.js create mode 100644 api/controllers/UserController.js create mode 100644 api/models/Passport.js diff --git a/api/controllers/AuthController.js b/api/controllers/AuthController.js new file mode 100644 index 0000000..0c90c85 --- /dev/null +++ b/api/controllers/AuthController.js @@ -0,0 +1,115 @@ +/** + * Authentication Controller + */ +module.exports = { + + /** + * Log out a user and return them to the homepage + * + * Passport exposes a logout() function on req (also aliased as logOut()) that + * can be called from any route handler which needs to terminate a login + * session. Invoking logout() will remove the req.user property and clear the + * login session (if any). + * + * For more information on logging out users in Passport.js, check out: + * http://passportjs.org/guide/logout/ + * + * @param {Object} req + * @param {Object} res + */ + logout: function (req, res) { + req.logout() + delete req.user + delete req.session.passport + req.session.authenticated = false + + if (!req.isSocket) { + res.redirect(req.query.next || '/') + } else { + res.ok() + } + }, + + /** + * Create a third-party authentication endpoint + * + * @param {Object} req + * @param {Object} res + */ + provider: function (req, res) { + sails.services.passport.endpoint(req, res) + }, + + /** + * Create a authentication callback endpoint + * + * This endpoint handles everything related to creating and verifying Pass- + * ports and users, both locally and from third-aprty providers. + * + * Passport exposes a login() function on req (also aliased as logIn()) that + * can be used to establish a login session. When the login operation + * completes, user will be assigned to req.user. + * + * For more information on logging in users in Passport.js, check out: + * http://passportjs.org/guide/login/ + * + * @param {Object} req + * @param {Object} res + */ + callback: function (req, res) { + var action = req.param('action') + + function negotiateError (err) { + if (action === 'register') { + res.redirect('/register') + } else if (action === 'login') { + res.redirect('/login') + } else if (action === 'disconnect') { + res.redirect('back') + } else { + // make sure the server always returns a response to the client + // i.e passport-local bad username/email or password + res.send(403, err) + } + } + + sails.services.passport.callback(req, res, function (err, user, info, status) { + if (err || !user) { + sails.log.warn(user, err, info, status) + if (!err && info) { + return negotiateError(info) + } + return negotiateError(err) + } + + req.login(user, function (err) { + if (err) { + sails.log.warn(err) + return negotiateError(err) + } + + req.session.authenticated = true + + // Upon successful login, optionally redirect the user if there is a + // `next` query param + if (req.query.next) { + var url = sails.services.authservice.buildCallbackNextUrl(req) + res.status(302).set('Location', url) + } + + sails.log.info('user', user, 'authenticated successfully') + return res.json(user) + }) + }) + }, + + /** + * Disconnect a passport from a user + * + * @param {Object} req + * @param {Object} res + */ + disconnect: function (req, res) { + sails.services.passport.disconnect(req, res) + } +} diff --git a/api/controllers/UserController.js b/api/controllers/UserController.js new file mode 100644 index 0000000..560aea6 --- /dev/null +++ b/api/controllers/UserController.js @@ -0,0 +1,31 @@ +/** + * UserController + * + * @description :: Server-side logic for managing Users + * @help :: See http://links.sailsjs.org/docs/controllers + */ + +module.exports = { + /** + * @override + */ + create: function (req, res, next) { + sails.services.passport.protocols.local.register(req.body, function (err, user) { + if (err) return res.negotiate(err) + + res.ok(user) + }) + }, + + update: function (req, res, next) { + sails.services.passport.protocols.local.update(req.body, function (err, user) { + if (err) return res.negotiate(err) + + res.ok(user) + }) + }, + + me: function (req, res) { + res.ok(req.user) + } +} diff --git a/api/models/Passport.js b/api/models/Passport.js new file mode 100644 index 0000000..89e61ee --- /dev/null +++ b/api/models/Passport.js @@ -0,0 +1,64 @@ +const bcrypt = require('bcrypt') + +async function hashPassword (passport) { + try { + var config = sails.config.auth.bcrypt + var salt = config.rounds + if (passport.password) { + const hash = await bcrypt.hash(passport.password, salt) + passport.password = hash + } + return passport + } catch (e) { + delete passport.password + sails.log.error(e) + throw e + } +} + +/** + * Passport.js + * + * @description :: A model definition. Represents a database table/collection/etc. + * @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models + */ + +module.exports = { + attributes: { + // local, oauth2, etc + protocol: { + type: 'string', + required: true + }, + password: 'string', + accessToken: 'string', + provider: 'string', + identifier: 'string', + tokens: 'json', + + // User association + user: { + model: 'User', + required: true + }, + + // methods + validatePassword: async function (password) { + return bcrypt.compare(password, this.password) + } + }, + + /** + * callback run before creating a Passport + */ + beforeCreate: async function (passport) { + return hashPassword(passport) + }, + + /** + * callback run before updating + */ + beforeUpdate: async function (passport) { + return hashPassword(passport) + } +} diff --git a/api/models/User.js b/api/models/User.js index 4d99b77..b36d4a1 100644 --- a/api/models/User.js +++ b/api/models/User.js @@ -11,9 +11,10 @@ module.exports = { // ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗ // ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗ // ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝ - _id: { + id: { type: 'string', - unique: true + unique: true, + columnName: '_id' }, email: { type: 'string', From 00273ca9e039751bf3cd75bec9ab6984223fe440 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 13:40:36 -0400 Subject: [PATCH 08/26] add passport helper --- api/helpers/passportload.js | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 api/helpers/passportload.js diff --git a/api/helpers/passportload.js b/api/helpers/passportload.js new file mode 100644 index 0000000..50698ab --- /dev/null +++ b/api/helpers/passportload.js @@ -0,0 +1,44 @@ +// api/helpers/passportload.js +const passport = require('passport') +const url = require('url') + +module.exports = { + friendlyName: 'Load Passport', + description: 'Load the Passport configuration', + inputs: {}, + fn: async function () { + const strategies = sails.config.passport + const protocols = sails.config.protocols + for (const key in strategies) { + let options = {passReqToCallback: true} + let Strategy = strategies[key].strategy + if (key === 'local') { + _.extend(options, { + usernameField: 'identifier' + }) + passport.use(new Strategy(options, protocols.local.login)) + } else { + const protocol = strategies[key].protocol + const callbackURL = strategies[key].callback + let baseURL = '' + if (sails.config.appUrl && sails.config.appUrl !== null) { + baseURL = sails.config.appUrl + } else { + sails.log.warn('Please add \'appUrl\' to configuration') + baseURL = sails.getBaseurl() + } + + switch (protocol) { + case 'oauth2': + options.callbackURL = url.resolve(baseURL, callbackURL) + break + // other protocols (openid, etc can go here) + } + + _.extend(options, strategies[key].options) + + passport.use(new Strategy(options, protocols[protocol].login)) + } + } + } +} From f607083455563f69e57928190dd46fbaebf94296 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 13:40:50 -0400 Subject: [PATCH 09/26] add passport configs --- config/auth.js | 5 ++++ config/http.js | 13 ----------- config/passport.js | 9 ++++++++ config/permissions.js | 11 --------- config/protocols.js | 53 +++++++++++++++++++++++++++++++++++++++++++ config/routes.js | 29 ++++++++++++++--------- 6 files changed, 85 insertions(+), 35 deletions(-) create mode 100644 config/auth.js create mode 100644 config/passport.js delete mode 100644 config/permissions.js create mode 100644 config/protocols.js diff --git a/config/auth.js b/config/auth.js new file mode 100644 index 0000000..2c988ad --- /dev/null +++ b/config/auth.js @@ -0,0 +1,5 @@ +module.exports.auth = { + bcrypt: { + rounds: 8 + } +} diff --git a/config/http.js b/config/http.js index 81e8847..88fc384 100644 --- a/config/http.js +++ b/config/http.js @@ -18,19 +18,6 @@ const rateLimiter = rateLimit({ } }) -/*const passport = require('passport') -const LocalStrategy = require('passport-local') -passport.use(new LocalStrategy( - function (username, password, done) { - User.findOne({ username: username }, function (err, user) { - if (err) { return done(err) } - if (!user) { return done(null, false) } - if (!user.verifyPassword(password)) { return done(null, false) } - return done(null, user) - }) - } -))*/ - module.exports.http = { /**************************************************************************** diff --git a/config/passport.js b/config/passport.js new file mode 100644 index 0000000..7f54c0d --- /dev/null +++ b/config/passport.js @@ -0,0 +1,9 @@ +/** +* Passport configuration +*/ + +module.exports.passport = { + local: { + strategy: require('passport-local').Strategy + } +} diff --git a/config/permissions.js b/config/permissions.js deleted file mode 100644 index d3c5658..0000000 --- a/config/permissions.js +++ /dev/null @@ -1,11 +0,0 @@ -// config/permissions.js - -var _ = require('lodash'); -var _super = require('sails-permissions/config/permissions'); - -_.merge(exports, _super); -_.merge(exports, { - - // Extend with custom logic here by adding additional fields, methods, etc. - -}); diff --git a/config/protocols.js b/config/protocols.js new file mode 100644 index 0000000..6542f73 --- /dev/null +++ b/config/protocols.js @@ -0,0 +1,53 @@ +// Passport protocol configurations +const crypto = require('crypto') +const base64URL = require('base64url') + +module.exports = { + local: { + /** + * Validate a login request + * + * Looks up a user using the supplied identifier (email or username) and then + * attempts to find a local Passport associated with the user. If a Passport is + * found, its password is checked against the password supplied in the form. + * + * @param {Object} req + * @param {string} identifier + * @param {string} password + * @param {Function} next + */ + login: async function (req, identifier, password, next) { + if (!validateEmail(identifier)) { + return next(new Error('invalid email address'), false) + } + try { + const user = await User.findOne({ + email: identifier + }) + if (!user) throw new Error('an account with that email was not found') + + const passport = await Passport.findOne({ + protocol: 'local', + user: user.id + }) + if (passport) { + const res = await passport.validatePassword(password) + if (!res) throw new Error('incorrect password') + return next(null, user, passport) + } + } catch (e) { + return next(e, false) + } + } + } +} + +const EMAIL_REGEX = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i + +function validateEmail (email) { + return EMAIL_REGEX.test(email) +} + +function generateToken () { + return base64URL(crypto.randomBytes(48)) +} diff --git a/config/routes.js b/config/routes.js index 55635aa..5847b2c 100644 --- a/config/routes.js +++ b/config/routes.js @@ -22,13 +22,13 @@ module.exports.routes = { * * ***************************************************************************/ - '/': { + 'GET /': { view: 'pages/index' }, - '/login': { + 'GET /login': { view: 'pages/login' }, - '/register': { + 'GET /register': { view: 'pages/login' }, @@ -47,15 +47,22 @@ module.exports.routes = { // ╠═╣╠═╝║ ║╣ ║║║ ║║╠═╝║ ║║║║║ ║ ╚═╗ // ╩ ╩╩ ╩ ╚═╝╝╚╝═╩╝╩ ╚═╝╩╝╚╝ ╩ ╚═╝ - 'POST /api/publish': { - controller: 'books', - action: 'publish' - }, + 'POST /register': 'UserController.create', + 'POST /logout': 'AuthController.logout', - 'GET /api/books': { - controller: 'books', - action: 'list' - }, + 'POST /auth/local': 'AuthController.callback', + 'POST /auth/local/:action': 'AuthController.callback', + + 'POST /auth/:provider': 'AuthController.callback', + 'POST /auth/:provider/:action': 'AuthController.callback', + + 'GET /auth/:provider': 'AuthController.provider', + 'GET /auth/:provider/callback': 'AuthController.callback', + 'GET /auth/:provider/:action': 'AuthController.callback', + + 'POST /api/publish': 'BooksController.publish', + + 'GET /api/books': 'BooksController.list', // ╦ ╦╔═╗╔╗ ╦ ╦╔═╗╔═╗╦╔═╔═╗ From eea3976cb678e3fdaf59fc3399486fdaf90629f4 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 14:54:38 -0400 Subject: [PATCH 10/26] fix policy configs --- api/policies/passport.js | 29 +++++++++++++++++++++++++++++ config/policies.js | 10 +--------- config/protocols.js | 4 ++-- 3 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 api/policies/passport.js diff --git a/api/policies/passport.js b/api/policies/passport.js new file mode 100644 index 0000000..fb86b6a --- /dev/null +++ b/api/policies/passport.js @@ -0,0 +1,29 @@ +/** + * Passport Middleware + * + * Policy for Sails that initializes Passport.js and as well as its built-in + * session support. + * + * For more information on the Passport.js middleware, check out: + * http://passportjs.org/guide/configure/ + */ + +const http = require('http') + +const methods = ['login', 'logout', 'isAuthenticated', 'isUnauthenticated'] + +module.exports = async function (req, res, next) { + const passport = (await sails.helpers.passport()).getPassport() + passport.initialize()(req, res, function () { + passport.session()(req, res, function () { + if (req.isSocket) { + _.each(methods, function (method) { + req[method] = http.IncomingMessage.prototype[method].bind(req) + }) + } + + req.locals.user = req.user + next() + }) + }) +} diff --git a/config/policies.js b/config/policies.js index 5368665..ba50604 100644 --- a/config/policies.js +++ b/config/policies.js @@ -18,15 +18,7 @@ module.exports.policies = { ***************************************************************************/ '*': [ - 'basicAuth', - 'passport', - 'sessionAuth', - 'ModelPolicy', - 'AuditPolicy', - 'OwnerPolicy', - 'PermissionPolicy', - 'RolePolicy', - 'CriteriaPolicy' + 'passport' ], AuthController: { diff --git a/config/protocols.js b/config/protocols.js index 6542f73..e24d0aa 100644 --- a/config/protocols.js +++ b/config/protocols.js @@ -2,7 +2,7 @@ const crypto = require('crypto') const base64URL = require('base64url') -module.exports = { +module.exports.protocols = { local: { /** * Validate a login request @@ -31,7 +31,7 @@ module.exports = { user: user.id }) if (passport) { - const res = await passport.validatePassword(password) + const res = await Passport.validatePassword(password, passport) if (!res) throw new Error('incorrect password') return next(null, user, passport) } From 7024f032de342a36e50bed5500e9775a9fd38c8f Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 14:54:58 -0400 Subject: [PATCH 11/26] add ids and fix Passport model method --- api/models/Book.js | 6 ++++++ api/models/Passport.js | 16 +++++++++++----- api/models/User.js | 4 +++- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/api/models/Book.js b/api/models/Book.js index 940be66..5c3dfa0 100644 --- a/api/models/Book.js +++ b/api/models/Book.js @@ -12,6 +12,12 @@ module.exports = { // ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗ // ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗ // ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝ + id: { + type: 'string', + unique: true, + autoIncrement: true, + columnName: '_id' + }, title: {type: 'string', required: true}, author: {type: 'string'}, isbn: {type: 'string'}, diff --git a/api/models/Passport.js b/api/models/Passport.js index 89e61ee..93fc08d 100644 --- a/api/models/Passport.js +++ b/api/models/Passport.js @@ -25,6 +25,12 @@ async function hashPassword (passport) { module.exports = { attributes: { + id: { + type: 'string', + unique: true, + autoIncrement: true, + columnName: '_id' + }, // local, oauth2, etc protocol: { type: 'string', @@ -40,11 +46,6 @@ module.exports = { user: { model: 'User', required: true - }, - - // methods - validatePassword: async function (password) { - return bcrypt.compare(password, this.password) } }, @@ -60,5 +61,10 @@ module.exports = { */ beforeUpdate: async function (passport) { return hashPassword(passport) + }, + + // methods + validatePassword: async function (password, passport) { + return bcrypt.compare(password, passport.password) } } diff --git a/api/models/User.js b/api/models/User.js index b36d4a1..8c7d504 100644 --- a/api/models/User.js +++ b/api/models/User.js @@ -14,11 +14,13 @@ module.exports = { id: { type: 'string', unique: true, + autoIncrement: true, columnName: '_id' }, email: { type: 'string', - unique: true + unique: true, + required: true }, password: 'string' From a4378a34128d20fee2c4d5cc043f8fe88a4cb954 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 14:55:22 -0400 Subject: [PATCH 12/26] add passport init hook --- api/helpers/{passportload.js => passport.js} | 26 ++++++++++++++++---- api/hooks/passport/index.js | 17 +++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) rename api/helpers/{passportload.js => passport.js} (69%) create mode 100644 api/hooks/passport/index.js diff --git a/api/helpers/passportload.js b/api/helpers/passport.js similarity index 69% rename from api/helpers/passportload.js rename to api/helpers/passport.js index 50698ab..f1b1507 100644 --- a/api/helpers/passportload.js +++ b/api/helpers/passport.js @@ -1,14 +1,27 @@ -// api/helpers/passportload.js -const passport = require('passport') +// api/helpers/passport.js const url = require('url') module.exports = { - friendlyName: 'Load Passport', - description: 'Load the Passport configuration', + friendlyName: 'Load PassportHelper', + description: 'Load a PassportHelper instance', inputs: {}, - fn: async function () { + exits: { + success: { + outputFriendlyName: 'Passport helper', + outputDescription: 'A PassportHelper instance' + } + }, + fn: async function (inputs, exits) { + return exits.success(new PassportHelper()) + } +} + +function PassportHelper () { + const passport = require('passport') + this.loadStrategies = function () { const strategies = sails.config.passport const protocols = sails.config.protocols + for (const key in strategies) { let options = {passReqToCallback: true} let Strategy = strategies[key].strategy @@ -41,4 +54,7 @@ module.exports = { } } } + this.getPassport = function () { + return passport + } } diff --git a/api/hooks/passport/index.js b/api/hooks/passport/index.js new file mode 100644 index 0000000..68338c4 --- /dev/null +++ b/api/hooks/passport/index.js @@ -0,0 +1,17 @@ +let passportHook = sails.hooks.passport + +if (!passportHook) { + passportHook = function (sails) { + return { + initialize: async function (cb) { + const helper = await sails.helpers.passport() + helper.loadStrategies() + /*sails.after('hook:helpers:loaded', function () { + })*/ + return cb() + } + } + } +} + +module.exports = passportHook From bcd8188958b76b580a8895de9057a56a2adaf1ff Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 16:19:13 -0400 Subject: [PATCH 13/26] update controllers to use new sails syntax --- api/controllers/AuthController.js | 66 +++++++++++++++++++++++++------ api/controllers/UserController.js | 16 ++++---- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/api/controllers/AuthController.js b/api/controllers/AuthController.js index 0c90c85..06c88d5 100644 --- a/api/controllers/AuthController.js +++ b/api/controllers/AuthController.js @@ -1,8 +1,45 @@ /** * Authentication Controller */ +// some also from https://github.com/trailsjs/sails-auth + module.exports = { + /** + * check if the given email has a corresponding user + */ + emailExists: async function (req, res) { + const user = await User.findOne({ + email: req.param('email') + }) + if (!user) { + return res.status(404).json({ + error: 'user does not exist' + }) + } else { + return res.json({ + status: 'ok' + }) + } + }, + /** + * opposite of emailExists + */ + emailAvailable: async function (req, res) { + const user = await User.findOne({ + email: req.param('email') + }) + if (user) { + return res.status(401).json({ + error: 'that email address is not available' + }) + } else { + return res.json({ + status: 'ok' + }) + } + }, + /** * Log out a user and return them to the homepage * @@ -36,8 +73,9 @@ module.exports = { * @param {Object} req * @param {Object} res */ - provider: function (req, res) { - sails.services.passport.endpoint(req, res) + provider: async function (req, res) { + const passportHelper = await sails.helpers.passport() + passportHelper.endpoint(req, res) }, /** @@ -46,7 +84,7 @@ module.exports = { * This endpoint handles everything related to creating and verifying Pass- * ports and users, both locally and from third-aprty providers. * - * Passport exposes a login() function on req (also aliased as logIn()) that + * Passport exposes a login() function on req that * can be used to establish a login session. When the login operation * completes, user will be assigned to req.user. * @@ -56,8 +94,9 @@ module.exports = { * @param {Object} req * @param {Object} res */ - callback: function (req, res) { - var action = req.param('action') + callback: async function (req, res) { + const action = req.param('action') + const passportHelper = await sails.helpers.passport() function negotiateError (err) { if (action === 'register') { @@ -69,11 +108,13 @@ module.exports = { } else { // make sure the server always returns a response to the client // i.e passport-local bad username/email or password - res.send(403, err) + res.status(403).json({ + 'error': err.toString() + }) } } - sails.services.passport.callback(req, res, function (err, user, info, status) { + passportHelper.callback(req, res, function (err, user, info, status) { if (err || !user) { sails.log.warn(user, err, info, status) if (!err && info) { @@ -90,11 +131,9 @@ module.exports = { req.session.authenticated = true - // Upon successful login, optionally redirect the user if there is a - // `next` query param + // redirect if there is a 'next' param if (req.query.next) { - var url = sails.services.authservice.buildCallbackNextUrl(req) - res.status(302).set('Location', url) + res.status(302).set('Location', req.query.next) } sails.log.info('user', user, 'authenticated successfully') @@ -109,7 +148,8 @@ module.exports = { * @param {Object} req * @param {Object} res */ - disconnect: function (req, res) { - sails.services.passport.disconnect(req, res) + disconnect: async function (req, res) { + const passportHelper = await sails.helpers.passport() + passportHelper.disconnect(req, res) } } diff --git a/api/controllers/UserController.js b/api/controllers/UserController.js index 560aea6..025eee2 100644 --- a/api/controllers/UserController.js +++ b/api/controllers/UserController.js @@ -9,23 +9,25 @@ module.exports = { /** * @override */ - create: function (req, res, next) { - sails.services.passport.protocols.local.register(req.body, function (err, user) { + create: async function (req, res, next) { + const passportHelper = await sails.helpers.passport() + passportHelper.protocols.local.register(req.body, function (err, user) { if (err) return res.negotiate(err) - res.ok(user) + res.json(user) }) }, - update: function (req, res, next) { - sails.services.passport.protocols.local.update(req.body, function (err, user) { + update: async function (req, res, next) { + const passportHelper = await sails.helpers.passport() + passportHelper.protocols.local.update(req.body, function (err, user) { if (err) return res.negotiate(err) - res.ok(user) + res.json(user) }) }, me: function (req, res) { - res.ok(req.user) + res.json(req.user) } } From f33149991db119da6e3192f876ea34632c8e41fb Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 16:19:45 -0400 Subject: [PATCH 14/26] these as well --- api/helpers/passport.js | 105 ++++++++++++++++++++++++++++++++++-- api/hooks/passport/index.js | 2 - api/policies/passport.js | 1 + config/protocols.js | 50 ++++++++++++++++- config/routes.js | 7 ++- 5 files changed, 157 insertions(+), 8 deletions(-) diff --git a/api/helpers/passport.js b/api/helpers/passport.js index f1b1507..61a83f8 100644 --- a/api/helpers/passport.js +++ b/api/helpers/passport.js @@ -1,4 +1,5 @@ // api/helpers/passport.js +// from https://github.com/trailsjs/sails-auth/blob/master/api/services/passport.js const url = require('url') module.exports = { @@ -18,9 +19,10 @@ module.exports = { function PassportHelper () { const passport = require('passport') + this.protocols = sails.config.protocols + this.loadStrategies = function () { const strategies = sails.config.passport - const protocols = sails.config.protocols for (const key in strategies) { let options = {passReqToCallback: true} @@ -29,7 +31,7 @@ function PassportHelper () { _.extend(options, { usernameField: 'identifier' }) - passport.use(new Strategy(options, protocols.local.login)) + passport.use(new Strategy(options, this.protocols.local.login)) } else { const protocol = strategies[key].protocol const callbackURL = strategies[key].callback @@ -50,10 +52,107 @@ function PassportHelper () { _.extend(options, strategies[key].options) - passport.use(new Strategy(options, protocols[protocol].login)) + passport.use(new Strategy(options, this.protocols[protocol].login)) } } } + this.endpoint = function (req, res) { + const strategies = sails.config.passport + const provider = req.param('provider') + + if (!_.has(strategies, provider)) return res.redirect('/login') + + passport.authenticate(provider, {})(req, res, req.next) + } + // a callback helper to split by req + this.callback = function (req, res, next) { + var provider = req.param('provider', 'local') + var action = req.param('action') + + if (provider === 'local' && action !== undefined) { + if (action === 'register' && !req.user) { + this.protocols.local.register(req, res, next) + } else if (action === 'connect' && req.user) { + this.protocols.local.connect(req, res, next) + } else if (action === 'disconnect' && req.user) { + this.protocols.local.disconnect(req, res, next) + } else { + next(new Error('Invalid action')) + } + } else { + if (action === 'disconnect' && req.user) { + this.disconnect(req, res, next) + } else { + passport.authenticate(provider, next)(req, res, req.next) + } + } + } + this.connect = async function (req, q, profile, next) { + let userAttrs = {} + let provider = profile.provider || req.param('provider') + + req.session.tokens = q.tokens + q.provider = provider + + if (!provider) { + return next(new Error('No provider identified')) + } + + // if the profile object from passport has an email, use it + if (profile.emails && profile.emails[0]) userAttrs.email = profile.emails[0].value + if (!userAttrs.email) return next(new Error('No email available')) + + const pass = await Passport.findOne({ + provider, + identifier: q.identifier.toString() + }) + + let user + + if (!req.user) { + if (!passport) { // new user signing up, create a new user + user = await User.create(userAttrs) + await Passport.create({ + ...q, + user: user.id + }) + next(null, user) + } else { // existing user logging in + if (_.has(q, 'tokens') && q.tokens !== passport.tokens) { + passport.tokens = q.tokens + } + await passport.save() + user = User.findOne(passport.user) + next(null, user) + } + } else { // user logged in and trying to add new Passport + if (!passport) { + await Passport.create({ + ...q, + user: req.user.id + }) + next(null, req.user) + } else { // no action, user already logged in and passport exists + next(null, user) + } + } + } + this.disconnect = async function (req, res, next) { + try { + const user = req.user + const provider = req.param('provider') + + const pass = Passport.findOne({ + provider, + user: user.id + }) + await Passport.destroy(pass.id) + next(null, user) + return user + } catch (e) { + next(e) + } + } this.getPassport = function () { return passport } diff --git a/api/hooks/passport/index.js b/api/hooks/passport/index.js index 68338c4..a3864d7 100644 --- a/api/hooks/passport/index.js +++ b/api/hooks/passport/index.js @@ -6,8 +6,6 @@ if (!passportHook) { initialize: async function (cb) { const helper = await sails.helpers.passport() helper.loadStrategies() - /*sails.after('hook:helpers:loaded', function () { - })*/ return cb() } } diff --git a/api/policies/passport.js b/api/policies/passport.js index fb86b6a..199cc23 100644 --- a/api/policies/passport.js +++ b/api/policies/passport.js @@ -22,6 +22,7 @@ module.exports = async function (req, res, next) { }) } + if (!req.locals) req.locals = {} req.locals.user = req.user next() }) diff --git a/config/protocols.js b/config/protocols.js index e24d0aa..f16cfbf 100644 --- a/config/protocols.js +++ b/config/protocols.js @@ -36,7 +36,55 @@ module.exports.protocols = { return next(null, user, passport) } } catch (e) { - return next(e, false) + return next(e) + } + }, + register: async function (user, next) { + try { + const token = generateToken() + const password = user.password + delete user.password + + const newUser = User.create(user) + try { + await Passport.create({ + protocol: 'local', + password, + user: newUser.id, + accessToken: token + }) + } catch (e) { + await User.destroy(newUser.id) + throw e + } + return next(null, newUser) + } catch (e) { + return next(e) + } + }, + update: async function (user, next) { + throw new Error('not implemented') + }, + connect: async function (req, res, next) { + try { + const user = req.user + const password = req.param('password') + + const pass = await Passport.findOne({ + protocol: 'local', + user: user.id + }) + if (!pass) { + await Passport.create({ + protocol: 'local', + password, + user: user.id + }) + } else { + return next(null, user) + } + } catch (e) { + return next(e) } } } diff --git a/config/routes.js b/config/routes.js index 5847b2c..490dc69 100644 --- a/config/routes.js +++ b/config/routes.js @@ -50,8 +50,10 @@ module.exports.routes = { 'POST /register': 'UserController.create', 'POST /logout': 'AuthController.logout', - 'POST /auth/local': 'AuthController.callback', - 'POST /auth/local/:action': 'AuthController.callback', + 'POST /auth/email_exists': 'AuthController.emailExists', + 'POST /auth/email_available': 'AuthController.emailAvailable', + // 'POST /auth/local': 'AuthController.callback', + // 'POST /auth/local/:action': 'AuthController.callback', 'POST /auth/:provider': 'AuthController.callback', 'POST /auth/:provider/:action': 'AuthController.callback', @@ -63,6 +65,7 @@ module.exports.routes = { 'POST /api/publish': 'BooksController.publish', 'GET /api/books': 'BooksController.list', + 'GET /api/me': 'UserController.me', // ╦ ╦╔═╗╔╗ ╦ ╦╔═╗╔═╗╦╔═╔═╗ From 7774197a7e5ac7d5ffbb4465e7bb23cb15f7920b Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 16:19:56 -0400 Subject: [PATCH 15/26] change login endpoint --- assets/js/actions/login.js | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/assets/js/actions/login.js b/assets/js/actions/login.js index 1ce5828..7eeaad8 100644 --- a/assets/js/actions/login.js +++ b/assets/js/actions/login.js @@ -28,10 +28,13 @@ export const setPassword = pass => ({ data: pass }) -export const setCarousel = pos => ({ - type: ACTIONS.set_carousel, - data: pos -}) +export const setCarousel = pos => (dispatch, getState) => { + dispatch(clearError()) + dispatch({ + type: ACTIONS.set_carousel, + data: pos + }) +} export const setError = data => ({ type: ACTIONS.set_error, @@ -47,18 +50,31 @@ export const setLoggedIn = (data) => (dispatch, getState) => { window.location.href = '/app' } -export const checkEmail = email => (dispatch, getState) => { - // dispatch(setWorking(true)) +export const checkEmail = email => async (dispatch, getState) => { + dispatch(setWorking(true)) dispatch(clearError()) if (/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(email)) { - dispatch(setCarousel(2)) + try { + const res = await Ajax.post({ + url: '/auth/email_exists', + data: { + email + } + }) + dispatch(setCarousel(2)) + } catch (e) { + dispatch(setError({ + type: 'email', + error: 'An account with that email does not exist.' + })) + } } else { dispatch(setError({ type: 'email', error: 'Please enter a valid email address.' })) } - // dispatch(setWorking(false)) + dispatch(setWorking(false)) } export const checkPassword = (email, password) => async (dispatch, getState) => { @@ -67,10 +83,9 @@ export const checkPassword = (email, password) => async (dispatch, getState) => // do email + password check try { const res = await Ajax.post({ - url: '/api/token', + url: '/auth/local', data: { - grant_type: 'credentials', - email, + identifier: email, password } }) From 0cdca62c3650214d4b3a0f8218b50d65a53558ec Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 16:48:09 -0400 Subject: [PATCH 16/26] add fetch() to create() --- api/helpers/passport.js | 2 +- config/protocols.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/api/helpers/passport.js b/api/helpers/passport.js index 61a83f8..6a1b3b8 100644 --- a/api/helpers/passport.js +++ b/api/helpers/passport.js @@ -111,7 +111,7 @@ function PassportHelper () { if (!req.user) { if (!passport) { // new user signing up, create a new user - user = await User.create(userAttrs) + user = await User.create(userAttrs).fetch() await Passport.create({ ...q, user: user.id diff --git a/config/protocols.js b/config/protocols.js index f16cfbf..5c3fe01 100644 --- a/config/protocols.js +++ b/config/protocols.js @@ -43,9 +43,10 @@ module.exports.protocols = { try { const token = generateToken() const password = user.password + if (!password.length) throw new Error('password cannot be blank') delete user.password - const newUser = User.create(user) + const newUser = await User.create(user).fetch() try { await Passport.create({ protocol: 'local', From 2002d3ebd73ea47b79470fa92a65cca76596e64e Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 17:05:27 -0400 Subject: [PATCH 17/26] model id is a number --- api/models/Book.js | 2 +- api/models/Passport.js | 2 +- api/models/User.js | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/api/models/Book.js b/api/models/Book.js index 5c3dfa0..7b002dd 100644 --- a/api/models/Book.js +++ b/api/models/Book.js @@ -13,7 +13,7 @@ module.exports = { // ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗ // ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝ id: { - type: 'string', + type: 'number', unique: true, autoIncrement: true, columnName: '_id' diff --git a/api/models/Passport.js b/api/models/Passport.js index 93fc08d..ba48952 100644 --- a/api/models/Passport.js +++ b/api/models/Passport.js @@ -26,7 +26,7 @@ async function hashPassword (passport) { module.exports = { attributes: { id: { - type: 'string', + type: 'number', unique: true, autoIncrement: true, columnName: '_id' diff --git a/api/models/User.js b/api/models/User.js index 8c7d504..08bd61e 100644 --- a/api/models/User.js +++ b/api/models/User.js @@ -12,7 +12,7 @@ module.exports = { // ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗ // ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝ id: { - type: 'string', + type: 'number', unique: true, autoIncrement: true, columnName: '_id' @@ -21,8 +21,7 @@ module.exports = { type: 'string', unique: true, required: true - }, - password: 'string' + } // ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗ // ║╣ ║║║╠╩╗║╣ ║║╚═╗ From 54b65bc3da314e4b16f837ed8dbb83d52dfb2001 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 21:17:57 -0400 Subject: [PATCH 18/26] users can be registered and logged in --- api/controllers/AuthController.js | 2 +- api/controllers/UserController.js | 8 ++++++-- api/helpers/passport.js | 16 ++++++++++++++-- api/models/Passport.js | 10 ++++++---- config/protocols.js | 5 ++++- config/routes.js | 1 + 6 files changed, 32 insertions(+), 10 deletions(-) diff --git a/api/controllers/AuthController.js b/api/controllers/AuthController.js index 06c88d5..f1d1ac9 100644 --- a/api/controllers/AuthController.js +++ b/api/controllers/AuthController.js @@ -108,7 +108,7 @@ module.exports = { } else { // make sure the server always returns a response to the client // i.e passport-local bad username/email or password - res.status(403).json({ + res.status(401).json({ 'error': err.toString() }) } diff --git a/api/controllers/UserController.js b/api/controllers/UserController.js index 025eee2..7eae227 100644 --- a/api/controllers/UserController.js +++ b/api/controllers/UserController.js @@ -12,7 +12,9 @@ module.exports = { create: async function (req, res, next) { const passportHelper = await sails.helpers.passport() passportHelper.protocols.local.register(req.body, function (err, user) { - if (err) return res.negotiate(err) + if (err) return res.status(500).json({ + error: err.toString() + }) res.json(user) }) @@ -21,7 +23,9 @@ module.exports = { update: async function (req, res, next) { const passportHelper = await sails.helpers.passport() passportHelper.protocols.local.update(req.body, function (err, user) { - if (err) return res.negotiate(err) + if (err) return res.status(500).json({ + error: err.toString() + }) res.json(user) }) diff --git a/api/helpers/passport.js b/api/helpers/passport.js index 6a1b3b8..0c38186 100644 --- a/api/helpers/passport.js +++ b/api/helpers/passport.js @@ -1,5 +1,6 @@ // api/helpers/passport.js // from https://github.com/trailsjs/sails-auth/blob/master/api/services/passport.js + const url = require('url') module.exports = { @@ -17,8 +18,19 @@ module.exports = { } } +const passport = require('passport') +passport.serializeUser(function (user, next) { + next(null, user.id) +}) +passport.deserializeUser(function (id, next) { + return User.findOne({id: id}) + .then(function (user) { + next(null, user || null) + return user + }).catch(next) +}) + function PassportHelper () { - const passport = require('passport') this.protocols = sails.config.protocols this.loadStrategies = function () { @@ -110,7 +122,7 @@ function PassportHelper () { let user if (!req.user) { - if (!passport) { // new user signing up, create a new user + if (!passport) { // new user signing up, create a new user user = await User.create(userAttrs).fetch() await Passport.create({ ...q, diff --git a/api/models/Passport.js b/api/models/Passport.js index ba48952..c65c1c6 100644 --- a/api/models/Passport.js +++ b/api/models/Passport.js @@ -52,15 +52,17 @@ module.exports = { /** * callback run before creating a Passport */ - beforeCreate: async function (passport) { - return hashPassword(passport) + beforeCreate: async function (passport, next) { + await hashPassword(passport) + return next() }, /** * callback run before updating */ - beforeUpdate: async function (passport) { - return hashPassword(passport) + beforeUpdate: async function (passport, next) { + await hashPassword(passport) + return next() }, // methods diff --git a/config/protocols.js b/config/protocols.js index 5c3fe01..59c85fd 100644 --- a/config/protocols.js +++ b/config/protocols.js @@ -34,6 +34,8 @@ module.exports.protocols = { const res = await Passport.validatePassword(password, passport) if (!res) throw new Error('incorrect password') return next(null, user, passport) + } else { + throw new Error('that account does not have password login enabled') } } catch (e) { return next(e) @@ -54,11 +56,12 @@ module.exports.protocols = { user: newUser.id, accessToken: token }) + return next(null, newUser) } catch (e) { + console.log(newUser) await User.destroy(newUser.id) throw e } - return next(null, newUser) } catch (e) { return next(e) } diff --git a/config/routes.js b/config/routes.js index 490dc69..9c43204 100644 --- a/config/routes.js +++ b/config/routes.js @@ -31,6 +31,7 @@ module.exports.routes = { 'GET /register': { view: 'pages/login' }, + 'GET /app': 'TargetController.show', /*************************************************************************** * * From 8cec0ca3539c686ff3a022c086716bd9cca4de69 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 21:27:26 -0400 Subject: [PATCH 19/26] detailed error message --- assets/js/actions/login.js | 36 +++++++++++++++++++++++- assets/js/lib/Ajax.js | 2 +- assets/js/login.js | 8 +++--- assets/styles/shared/underlineinput.scss | 5 ++++ config/protocols.js | 3 +- 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/assets/js/actions/login.js b/assets/js/actions/login.js index 7eeaad8..c35e747 100644 --- a/assets/js/actions/login.js +++ b/assets/js/actions/login.js @@ -46,7 +46,7 @@ export const clearError = () => ({ }) export const setLoggedIn = (data) => (dispatch, getState) => { - document.localStorage.setItem('roe-token', JSON.stringify(data)) + window.localStorage.setItem('roe-token', JSON.stringify(data)) window.location.href = '/app' } @@ -99,3 +99,37 @@ export const checkPassword = (email, password) => async (dispatch, getState) => dispatch(setWorking(false)) } } + +export const signup = (email, password) => async (dispatch, getState) => { + dispatch(setWorking(true)) + dispatch(clearError()) + if (/^([a-zA-Z0-9_\-\.]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$/.test(email)) { + try { + await Ajax.post({ + url: '/auth/email_available', + data: { + email + } + }) + await Ajax.post({ + url: '/register', + data: { + email, + password + } + }) + dispatch(setCarousel(2)) + } catch (e) { + dispatch(setError({ + type: 'email', + error: e.toString() + })) + } + } else { + dispatch(setError({ + type: 'email', + error: 'Please enter a valid email address.' + })) + } + dispatch(setWorking(false)) +} diff --git a/assets/js/lib/Ajax.js b/assets/js/lib/Ajax.js index b6b9439..cde43d0 100644 --- a/assets/js/lib/Ajax.js +++ b/assets/js/lib/Ajax.js @@ -104,7 +104,7 @@ export default class Ajax { } catch (e) { reject(new AjaxError(e.toString(), data, xhr)) } finally { - reject(new AjaxError(xhr.status, data, xhr)) + reject(new AjaxError(data, xhr.status, xhr)) } } diff --git a/assets/js/login.js b/assets/js/login.js index e6e6203..8524d4f 100644 --- a/assets/js/login.js +++ b/assets/js/login.js @@ -6,7 +6,7 @@ import Progress from './components/Progress' import Carousel, {CarouselItem} from './containers/Carousel' import UnderlineInput from './components/UnderlineInput' import reducer from './reducers/login' -import {setEmail, setPassword, setCarousel, checkEmail, checkPassword} from './actions/login' +import {setEmail, setPassword, setCarousel, checkEmail, checkPassword, signup} from './actions/login' import STYLE from '../styles/login.scss' @@ -55,7 +55,7 @@ class App extends React.Component { getPasswordInputs () { return [ null} + onButtonClick={() => this.dispatch(signup(this.state.user.email, this.state.user.password))} smallButton='Have an account?' onSmallButtonClick={() => this.dispatch(setCarousel(1))} footer='Sign up with your Google account' /> diff --git a/assets/styles/shared/underlineinput.scss b/assets/styles/shared/underlineinput.scss index a539719..0723490 100644 --- a/assets/styles/shared/underlineinput.scss +++ b/assets/styles/shared/underlineinput.scss @@ -84,4 +84,9 @@ } } } + + & + .underlined-input, + & + .underlined-input-readonly { + margin-top: 8px + } } diff --git a/config/protocols.js b/config/protocols.js index 59c85fd..ce3cf64 100644 --- a/config/protocols.js +++ b/config/protocols.js @@ -56,9 +56,8 @@ module.exports.protocols = { user: newUser.id, accessToken: token }) - return next(null, newUser) + return next(null, token) } catch (e) { - console.log(newUser) await User.destroy(newUser.id) throw e } From aaa82be11be81bfb0032e37de19256960d5be272 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 21:42:51 -0400 Subject: [PATCH 20/26] add missing passport methods --- api/policies/passport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/policies/passport.js b/api/policies/passport.js index 199cc23..8297a13 100644 --- a/api/policies/passport.js +++ b/api/policies/passport.js @@ -10,7 +10,7 @@ const http = require('http') -const methods = ['login', 'logout', 'isAuthenticated', 'isUnauthenticated'] +const methods = ['login', 'logIn', 'logout', 'logOut', 'isAuthenticated', 'isUnauthenticated'] module.exports = async function (req, res, next) { const passport = (await sails.helpers.passport()).getPassport() From 4e88371cc0d83e26c153bc4b7497d201fc8335d7 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 22:51:01 -0400 Subject: [PATCH 21/26] sessions work correctly now --- api/controllers/TargetController.js | 7 +++++++ api/helpers/passport.js | 2 +- api/policies/passport.js | 30 ----------------------------- api/policies/sessionAuth.js | 14 ++++++++++++++ config/http.js | 6 +++++- config/policies.js | 18 +++++++++++++---- config/protocols.js | 4 ++-- config/routes.js | 2 +- 8 files changed, 44 insertions(+), 39 deletions(-) create mode 100644 api/controllers/TargetController.js delete mode 100644 api/policies/passport.js create mode 100644 api/policies/sessionAuth.js diff --git a/api/controllers/TargetController.js b/api/controllers/TargetController.js new file mode 100644 index 0000000..5130ba9 --- /dev/null +++ b/api/controllers/TargetController.js @@ -0,0 +1,7 @@ +module.exports = { + show: function (req, res) { + res.view('pages/temp', { + email: req.user.email + }) + } +} diff --git a/api/helpers/passport.js b/api/helpers/passport.js index 0c38186..3e18126 100644 --- a/api/helpers/passport.js +++ b/api/helpers/passport.js @@ -25,7 +25,7 @@ passport.serializeUser(function (user, next) { passport.deserializeUser(function (id, next) { return User.findOne({id: id}) .then(function (user) { - next(null, user || null) + next(null, user) return user }).catch(next) }) diff --git a/api/policies/passport.js b/api/policies/passport.js deleted file mode 100644 index 8297a13..0000000 --- a/api/policies/passport.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Passport Middleware - * - * Policy for Sails that initializes Passport.js and as well as its built-in - * session support. - * - * For more information on the Passport.js middleware, check out: - * http://passportjs.org/guide/configure/ - */ - -const http = require('http') - -const methods = ['login', 'logIn', 'logout', 'logOut', 'isAuthenticated', 'isUnauthenticated'] - -module.exports = async function (req, res, next) { - const passport = (await sails.helpers.passport()).getPassport() - passport.initialize()(req, res, function () { - passport.session()(req, res, function () { - if (req.isSocket) { - _.each(methods, function (method) { - req[method] = http.IncomingMessage.prototype[method].bind(req) - }) - } - - if (!req.locals) req.locals = {} - req.locals.user = req.user - next() - }) - }) -} diff --git a/api/policies/sessionAuth.js b/api/policies/sessionAuth.js new file mode 100644 index 0000000..1d22cbd --- /dev/null +++ b/api/policies/sessionAuth.js @@ -0,0 +1,14 @@ +/** + * sessionAuth + * + * @module :: Policy + * @description :: Simple policy to allow any authenticated user + * @docs :: http://sailsjs.org/#!documentation/policies + */ +module.exports = function (req, res, next) { + if (req.session.authenticated) { + return next() + } + res.status(403).json({ error: 'You are not permitted to perform this action.' }) + // res.redirect('/login') +} diff --git a/config/http.js b/config/http.js index 88fc384..1f1469c 100644 --- a/config/http.js +++ b/config/http.js @@ -42,6 +42,8 @@ module.exports.http = { 'rateLimit', 'cookieParser', 'session', + 'passportInit', + 'passportSession', 'bodyParser', 'compress', 'poweredBy', @@ -49,7 +51,9 @@ module.exports.http = { 'www', 'favicon' ], - rateLimit: rateLimiter + rateLimit: rateLimiter, + passportInit: require('passport').initialize(), + passportSession: require('passport').session(), /*************************************************************************** * * diff --git a/config/policies.js b/config/policies.js index ba50604..4c1514c 100644 --- a/config/policies.js +++ b/config/policies.js @@ -17,11 +17,21 @@ module.exports.policies = { * * ***************************************************************************/ - '*': [ - 'passport' - ], + '*': true, + + UserController: { + '*': true, + update: [ 'sessionAuth' ], + me: [ 'sessionAuth' ] + }, AuthController: { - '*': [ 'passport' ] + '*': true, + logout: [ 'sessionAuth' ], + disconnect: [ 'sessionAuth' ] + }, + + TargetController: { + '*': [ 'sessionAuth' ] } } diff --git a/config/protocols.js b/config/protocols.js index ce3cf64..ae9f9ce 100644 --- a/config/protocols.js +++ b/config/protocols.js @@ -33,12 +33,12 @@ module.exports.protocols = { if (passport) { const res = await Passport.validatePassword(password, passport) if (!res) throw new Error('incorrect password') - return next(null, user, passport) + return next(null, user) } else { throw new Error('that account does not have password login enabled') } } catch (e) { - return next(e) + return next(e, false) } }, register: async function (user, next) { diff --git a/config/routes.js b/config/routes.js index 9c43204..1fb0f4e 100644 --- a/config/routes.js +++ b/config/routes.js @@ -49,7 +49,7 @@ module.exports.routes = { // ╩ ╩╩ ╩ ╚═╝╝╚╝═╩╝╩ ╚═╝╩╝╚╝ ╩ ╚═╝ 'POST /register': 'UserController.create', - 'POST /logout': 'AuthController.logout', + 'GET /logout': 'AuthController.logout', 'POST /auth/email_exists': 'AuthController.emailExists', 'POST /auth/email_available': 'AuthController.emailAvailable', From f30f0b1a6a59a9fc4e9c778400202773ac3ade52 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 22:51:17 -0400 Subject: [PATCH 22/26] temp logged in view --- views/pages/index.ejs | 2 +- views/pages/temp.ejs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 views/pages/temp.ejs diff --git a/views/pages/index.ejs b/views/pages/index.ejs index f43d01b..107b499 100644 --- a/views/pages/index.ejs +++ b/views/pages/index.ejs @@ -19,7 +19,7 @@ setTimeout(function sunrise () {

<%= __('A brand new app.') %>

You're looking at: <%= view.pathFromApp + '.' +view.ext %>

-

Go to Login: Here

+

Go to Login: Here

    diff --git a/views/pages/temp.ejs b/views/pages/temp.ejs new file mode 100644 index 0000000..640d92f --- /dev/null +++ b/views/pages/temp.ejs @@ -0,0 +1,2 @@ +authed: <%- email %>
    +Logout From 20fb8d6d1a6c421df88751c564a055fd29ee48ea Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 31 Oct 2018 22:56:31 -0400 Subject: [PATCH 23/26] change accent colors --- assets/styles/lib/vars.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/styles/lib/vars.scss b/assets/styles/lib/vars.scss index 8a44403..7d33f4b 100644 --- a/assets/styles/lib/vars.scss +++ b/assets/styles/lib/vars.scss @@ -22,8 +22,8 @@ $background-2: white; $text-dark-1: $black-1; $text-dark-2: $black-2; -$accent-1: #4423c4; -$accent-2: #4460c4; +$accent-1: #731212; +$accent-2: #9a834d; $accent-3: #D4DBF1; $red: #FE4C52; From 220e88551f53cad002b742ef2cb13404977da747 Mon Sep 17 00:00:00 2001 From: Theodore Kluge <1696813+villa7@users.noreply.github.com> Date: Wed, 31 Oct 2018 23:24:16 -0400 Subject: [PATCH 24/26] add viewport meta tag --- assets/templates/login.html | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/templates/login.html b/assets/templates/login.html index 94568bd..a3e9c5e 100644 --- a/assets/templates/login.html +++ b/assets/templates/login.html @@ -9,6 +9,7 @@ if (typeof item === 'string' || item instanceof String) { item = { href: item, rel: 'stylesheet' } } %> <%= key %>="<%= item[key] %>"<% } %> /><% } %> +
    From 0f8a681c9add1255762f0bf548d0014677ccb471 Mon Sep 17 00:00:00 2001 From: Michael Iacona Date: Thu, 1 Nov 2018 07:24:58 -0400 Subject: [PATCH 25/26] Travis build --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e66e60d..9fed4ba 100644 --- a/README.md +++ b/README.md @@ -25,3 +25,5 @@ This app was originally generated on Tue Oct 16 2018 01:21:31 GMT+0000 (UTC) usi Note: Generators are usually run using the globally-installed `sails` CLI (command-line interface). This CLI version is _environment-specific_ rather than app-specific, thus over time, as a project's dependencies are upgraded or the project is worked on by different developers on different computers using different versions of Node.js, the Sails dependency in its package.json file may differ from the globally-installed Sails CLI release it was originally generated with. (Be sure to always check out the relevant [upgrading guides](https://sailsjs.com/upgrading) before upgrading the version of Sails used by your app. If you're stuck, [get help here](https://sailsjs.com/support).) --> +[![Build Status](https://travis-ci.org/miacona96/RoE-pipe.svg?branch=master)](https://travis-ci.org/miacona96/RoE-pipe) + From c02176ba9aad2ee7d8708488df3d6213e615fc9a Mon Sep 17 00:00:00 2001 From: Michael Iacona Date: Thu, 1 Nov 2018 08:26:04 -0400 Subject: [PATCH 26/26] Travis yml file --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..38732f6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: node_js +node_js: + - "node" +deploy: + provider: elasticbeanstalk + access_key_id: "Encrypted =" + secret_access_key: + secure: "Encypted =" + region: "us-east-1" + app: "example-app-name" + env: "example-app-environment" + bucket_name: "the-target-S3-bucket" + +#script: node testfile