diff --git a/.eslintignore b/.eslintignore index 6ec4c0c88..d506a20c2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,6 @@ dist client/lib **/*.html -plugins/ -plugins/**/node_modules \ No newline at end of file +plugins/* +!plugins/coral-plugin-facebook-auth +node_modules \ No newline at end of file diff --git a/.gitignore b/.gitignore index 62a765fff..4ad15747d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,18 @@ node_modules -npm-debug.log* + dist -test/e2e/reports + +npm-debug.log* dump.rdb + .env *.cfg + .idea/ -coverage/ *.swp -plugins -plugins.json + +test/e2e/reports +coverage/ + +plugins/* +!plugins/coral-plugin-facebook-auth \ No newline at end of file diff --git a/README.md b/README.md index 3111446ea..b88138425 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,6 @@ The Talk application looks for the following configuration values either as envi secure cookies. - `TALK_ROOT_URL` (*required*) - root url of the installed application externally available in the format: `://` without the path. - -- `TALK_FACEBOOK_APP_ID` (*required for login via fb*) - the Facebook app id for your Facebook -Login enabled app. -- `TALK_FACEBOOK_APP_SECRET` (*required for login via fb*) - the Facebook app secret for your -Facebook Login enabled app. - - `TALK_SMTP_EMAIL` (*required for email*) - the address to send emails from using the SMTP provider. - `TALK_SMTP_USERNAME` (*required for email*) - username of the SMTP provider you are using. diff --git a/app.js b/app.js index 3ae03ec72..e1e864b10 100644 --- a/app.js +++ b/app.js @@ -4,6 +4,7 @@ const morgan = require('morgan'); const path = require('path'); const helmet = require('helmet'); const {passport} = require('./services/passport'); +const plugins = require('./services/plugins'); const session = require('express-session'); const enabled = require('debug').enabled; const RedisStore = require('connect-redis')(session); @@ -75,6 +76,17 @@ app.use(session(session_opts)); // PASSPORT MIDDLEWARE //============================================================================== +const passportDebug = require('debug')('talk:passport'); + +// Install the passport plugins. +plugins.get('server', 'passport').forEach((plugin) => { + passportDebug(`added plugin '${plugin.plugin.name}'`); + + // Pass the passport.js instance to the plugin to allow it to inject it's + // functionality. + plugin.passport(passport); +}); + // Setup the PassportJS Middleware. app.use(passport.initialize()); app.use(passport.session()); diff --git a/package.json b/package.json index 5dcb93bcc..3ea18dfcd 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "A commenting platform from The Coral Project. https://coralproject.net", "main": "app.js", "scripts": { + "postinstall": "./bin/cli plugins reconcile --skip-remote", "start": "./bin/cli serve --jobs", "dev-start": "nodemon --config .nodemon.json --exec \"./bin/cli -c .env serve --jobs\"", "build": "NODE_ENV=production webpack --progress -p --config webpack.config.js --bail", @@ -88,7 +89,6 @@ "nodemailer": "^2.6.4", "parse-duration": "^0.1.1", "passport": "^0.3.2", - "passport-facebook": "^2.1.1", "passport-local": "^1.0.0", "react-apollo": "^0.10.0", "react-recaptcha": "^2.2.6", diff --git a/plugins.json b/plugins.json new file mode 100644 index 000000000..40702cde9 --- /dev/null +++ b/plugins.json @@ -0,0 +1,5 @@ +{ + "server": [ + "coral-plugin-facebook-auth" + ] +} diff --git a/plugins/coral-plugin-facebook-auth/README.md b/plugins/coral-plugin-facebook-auth/README.md new file mode 100644 index 000000000..3a0f72c2f --- /dev/null +++ b/plugins/coral-plugin-facebook-auth/README.md @@ -0,0 +1,24 @@ +# coral-plugin-facebook-auth + +This plugin provides facebook authentication support for Talk. + +## Configuration + +This plugin looks for the following configuration from the environment: + +- `TALK_FACEBOOK_APP_ID` (*required*) - the Facebook app id for your Facebook +Login enabled app. +- `TALK_FACEBOOK_APP_SECRET` (*required*) - the Facebook app secret for your +Facebook Login enabled app. + +### License + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + + See the License for the specific language governing permissions and limitations under the License. diff --git a/plugins/coral-plugin-facebook-auth/index.js b/plugins/coral-plugin-facebook-auth/index.js new file mode 100644 index 000000000..3e7c61452 --- /dev/null +++ b/plugins/coral-plugin-facebook-auth/index.js @@ -0,0 +1,7 @@ +const passport = require('./server/passport'); +const router = require('./server/router'); + +module.exports = { + passport, + router +}; diff --git a/plugins/coral-plugin-facebook-auth/package.json b/plugins/coral-plugin-facebook-auth/package.json new file mode 100644 index 000000000..ae6721ee9 --- /dev/null +++ b/plugins/coral-plugin-facebook-auth/package.json @@ -0,0 +1,9 @@ +{ + "name": "coral-plugin-facebook-auth", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "passport-facebook": "^2.1.1" + } +} diff --git a/plugins/coral-plugin-facebook-auth/server/passport.js b/plugins/coral-plugin-facebook-auth/server/passport.js new file mode 100644 index 000000000..ae69ec4bd --- /dev/null +++ b/plugins/coral-plugin-facebook-auth/server/passport.js @@ -0,0 +1,27 @@ +const FacebookStrategy = require('passport-facebook').Strategy; +const UsersService = require('services/users'); +const {ValidateUserLogin} = require('services/passport'); + +module.exports = (passport) => { + if (process.env.TALK_FACEBOOK_APP_ID && process.env.TALK_FACEBOOK_APP_SECRET && process.env.TALK_ROOT_URL) { + passport.use(new FacebookStrategy({ + clientID: process.env.TALK_FACEBOOK_APP_ID, + clientSecret: process.env.TALK_FACEBOOK_APP_SECRET, + callbackURL: `${process.env.TALK_ROOT_URL}/api/v1/auth/facebook/callback`, + passReqToCallback: true, + profileFields: ['id', 'displayName', 'picture.type(large)'] + }, async (req, accessToken, refreshToken, profile, done) => { + + let user; + try { + user = await UsersService.findOrCreateExternalUser(profile); + } catch (err) { + return done(err); + } + + return ValidateUserLogin(profile, user, done); + })); + } else if (process.env.NODE_ENV !== 'test') { + throw new Error('Facebook cannot be enabled, missing one of TALK_FACEBOOK_APP_ID, TALK_FACEBOOK_APP_SECRET, TALK_ROOT_URL'); + } +}; diff --git a/plugins/coral-plugin-facebook-auth/server/router.js b/plugins/coral-plugin-facebook-auth/server/router.js new file mode 100644 index 000000000..7ef7b0ed2 --- /dev/null +++ b/plugins/coral-plugin-facebook-auth/server/router.js @@ -0,0 +1,20 @@ +module.exports = (router) => { + + const {passport, HandleAuthPopupCallback} = require('services/passport'); + + /** + * Facebook auth endpoint, this will redirect the user immediatly to facebook + * for authorization. + */ + router.get('/api/v1/auth/facebook', passport.authenticate('facebook', {display: 'popup', authType: 'rerequest', scope: ['public_profile']})); + + /** + * Facebook callback endpoint, this will send the user a html page designed to + * send back the user credentials upon sucesfull login. + */ + router.get('/api/v1/auth/facebook/callback', (req, res, next) => { + + // Perform the facebook login flow and pass the data back through the opener. + passport.authenticate('facebook', HandleAuthPopupCallback(req, res, next))(req, res, next); + }); +}; diff --git a/plugins/coral-plugin-facebook-auth/yarn.lock b/plugins/coral-plugin-facebook-auth/yarn.lock new file mode 100644 index 000000000..a2886dfbe --- /dev/null +++ b/plugins/coral-plugin-facebook-auth/yarn.lock @@ -0,0 +1,34 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +oauth@0.9.x: + version "0.9.15" + resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" + +passport-facebook@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/passport-facebook/-/passport-facebook-2.1.1.tgz#c39d0b52ae4d59163245a4e21a7b9b6321303311" + dependencies: + passport-oauth2 "1.x.x" + +passport-oauth2@1.x.x: + version "1.4.0" + resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.4.0.tgz#f62f81583cbe12609be7ce6f160b9395a27b86ad" + dependencies: + oauth "0.9.x" + passport-strategy "1.x.x" + uid2 "0.0.x" + utils-merge "1.x.x" + +passport-strategy@1.x.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" + +uid2@0.0.x: + version "0.0.3" + resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" + +utils-merge@1.x.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" diff --git a/routes/api/auth/index.js b/routes/api/auth/index.js index 12a3e8bbe..4926192de 100644 --- a/routes/api/auth/index.js +++ b/routes/api/auth/index.js @@ -1,5 +1,5 @@ const express = require('express'); -const {passport, HandleAuthCallback, HandleAuthPopupCallback} = require('../../../services/passport'); +const {passport, HandleAuthCallback} = require('../../../services/passport'); const authorization = require('../../../middleware/authorization'); const router = express.Router(); @@ -42,20 +42,4 @@ router.post('/local', (req, res, next) => { passport.authenticate('local', HandleAuthCallback(req, res, next))(req, res, next); }); -/** - * Facebook auth endpoint, this will redirect the user immediatly to facebook - * for authorization. - */ -router.get('/facebook', passport.authenticate('facebook', {display: 'popup', authType: 'rerequest', scope: ['public_profile']})); - -/** - * Facebook callback endpoint, this will send the user a html page designed to - * send back the user credentials upon sucesfull login. - */ -router.get('/facebook/callback', (req, res, next) => { - - // Perform the facebook login flow and pass the data back through the opener. - passport.authenticate('facebook', HandleAuthPopupCallback(req, res, next))(req, res, next); -}); - module.exports = router; diff --git a/services/passport.js b/services/passport.js index 09ded01e8..682d86ce1 100644 --- a/services/passport.js +++ b/services/passport.js @@ -4,10 +4,8 @@ const SettingsService = require('./settings'); const fetch = require('node-fetch'); const FormData = require('form-data'); const LocalStrategy = require('passport-local').Strategy; -const FacebookStrategy = require('passport-facebook').Strategy; const errors = require('../errors'); const debug = require('debug')('talk:passport'); -const plugins = require('./plugins'); //============================================================================== // SESSION SERIALIZATION @@ -354,37 +352,6 @@ passport.use(new LocalStrategy({ return ValidateUserLogin(loginProfile, user, done); })); -if (process.env.TALK_FACEBOOK_APP_ID && process.env.TALK_FACEBOOK_APP_SECRET && process.env.TALK_ROOT_URL) { - passport.use(new FacebookStrategy({ - clientID: process.env.TALK_FACEBOOK_APP_ID, - clientSecret: process.env.TALK_FACEBOOK_APP_SECRET, - callbackURL: `${process.env.TALK_ROOT_URL}/api/v1/auth/facebook/callback`, - passReqToCallback: true, - profileFields: ['id', 'displayName', 'picture.type(large)'] - }, async (req, accessToken, refreshToken, profile, done) => { - - let user; - try { - user = await UsersService.findOrCreateExternalUser(profile); - } catch (err) { - return done(err); - } - - return ValidateUserLogin(profile, user, done); - })); -} else { - console.error('Facebook cannot be enabled, missing one of TALK_FACEBOOK_APP_ID, TALK_FACEBOOK_APP_SECRET, TALK_ROOT_URL'); -} - -// Inject server route plugins. -plugins.get('server', 'passport').forEach((plugin) => { - debug(`added plugin '${plugin.plugin.name}'`); - - // Pass the passport.js instance to the plugin to allow it to inject it's - // functionality. - plugin.passport(passport); -}); - module.exports = { passport, ValidateUserLogin, diff --git a/yarn.lock b/yarn.lock index a0420ab91..c9e0c2463 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5372,10 +5372,6 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -oauth@0.9.x: - version "0.9.15" - resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" - object-assign@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" @@ -5589,27 +5585,12 @@ parseurl@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" -passport-facebook@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/passport-facebook/-/passport-facebook-2.1.1.tgz#c39d0b52ae4d59163245a4e21a7b9b6321303311" - dependencies: - passport-oauth2 "1.x.x" - passport-local@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee" dependencies: passport-strategy "1.x.x" -passport-oauth2@1.x.x: - version "1.4.0" - resolved "https://registry.yarnpkg.com/passport-oauth2/-/passport-oauth2-1.4.0.tgz#f62f81583cbe12609be7ce6f160b9395a27b86ad" - dependencies: - oauth "0.9.x" - passport-strategy "1.x.x" - uid2 "0.0.x" - utils-merge "1.x.x" - passport-strategy@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" @@ -7782,10 +7763,6 @@ uid-safe@2.1.3, uid-safe@~2.1.3: base64-url "1.3.3" random-bytes "~1.0.0" -uid2@0.0.x: - version "0.0.3" - resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" - undefsafe@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-0.0.3.tgz#ecca3a03e56b9af17385baac812ac83b994a962f" @@ -7855,7 +7832,7 @@ util@0.10.3, util@^0.10.3: dependencies: inherits "2.0.1" -utils-merge@1.0.0, utils-merge@1.x.x: +utils-merge@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8"