From a3b636923b83917a4f96f26861b4eadc855d4f92 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sat, 12 Apr 2014 21:37:55 -0400 Subject: [PATCH 01/23] Initial hackathon-starter generator --- minimizer.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 minimizer.js diff --git a/minimizer.js b/minimizer.js new file mode 100644 index 0000000000..91586ef590 --- /dev/null +++ b/minimizer.js @@ -0,0 +1,56 @@ +var fs = require('fs'); +var inquirer = require('inquirer'); +var _ = require('underscore'); + +inquirer.prompt({ + type: 'list', + name: 'category', + message: '☂ Hackathon Starter', + choices: ['Authentication', 'API Examples', 'Exit'], + filter: function(val) { + return val.toLowerCase(); + } +}, function(answer) { + if (answer.category === 'authentication') { + inquirer.prompt({ + type: 'checkbox', + message: 'Select Authentication Providers:', + name: 'auth', + choices: [ + { name: 'Facebook', checked: true }, + { name: 'GitHub', checked: true }, + { name: 'Google', checked: true }, + { name: 'Twitter', checked: true }, + { name: 'Local', checked: true }, + { name: 'LinkedIn' }, + { name: 'Instagram' } + ], + validate: function(answer) { + if (answer.length < 1) { + return "You must choose at least one authentication method."; + } + return true; + }, + filter: function(val) { + return _.map(val, function(auth) { + return auth.toLowerCase(); + }); + } + }, function(answer) { + if (_.contains(answer.auth, 'facebook')) { + var search = "var FacebookStrategy = require('passport-facebook').Strategy;"; + var body = fs.readFileSync('config/passport.js').toString(); + if (body.indexOf(search) < 0) { + body = body.split('\n'); + var idx = body.lastIndexOf("var passport = require('passport');"); + body.splice(idx + 1, 0, search); + var output = body.join('\n'); + fs.writeFileSync('example.js', output); + } + } + }); + } else if (answer.category === 'api examples') { + console.log('selected api examples'); + } +}) +; From 92c4d34ed78cd04cd78d9a1d5bb899b693655435 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sat, 12 Apr 2014 22:28:49 -0400 Subject: [PATCH 02/23] Facebook generator complete for passport.js --- minimizer.js | 63 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/minimizer.js b/minimizer.js index 91586ef590..e6b4f98103 100644 --- a/minimizer.js +++ b/minimizer.js @@ -1,6 +1,7 @@ var fs = require('fs'); var inquirer = require('inquirer'); var _ = require('underscore'); +var M = require('mstring') inquirer.prompt({ type: 'list', @@ -37,20 +38,74 @@ inquirer.prompt({ }); } }, function(answer) { + var passportFile = 'config/passport.js'; if (_.contains(answer.auth, 'facebook')) { var search = "var FacebookStrategy = require('passport-facebook').Strategy;"; - var body = fs.readFileSync('config/passport.js').toString(); + var strategy = M(function() { + /*** + // Sign in with Facebook. + + passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) { + if (req.user) { + User.findOne({ $or: [{ facebook: profile.id }, { email: profile.email }] }, function(err, existingUser) { + if (existingUser) { + req.flash('errors', { msg: 'There is already a Facebook account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); + done(err); + } else { + User.findById(req.user.id, function(err, user) { + user.facebook = profile.id; + user.tokens.push({ kind: 'facebook', accessToken: accessToken }); + user.profile.name = user.profile.name || profile.displayName; + user.profile.gender = user.profile.gender || profile._json.gender; + user.profile.picture = user.profile.picture || 'https://graph.facebook.com/' + profile.id + '/picture?type=large'; + user.save(function(err) { + req.flash('info', { msg: 'Facebook account has been linked.' }); + done(err, user); + }); + }); + } + }); + } else { + User.findOne({ facebook: profile.id }, function(err, existingUser) { + if (existingUser) return done(null, existingUser); + User.findOne({ email: profile._json.email }, function(err, existingEmailUser) { + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Facebook manually from Account Settings.' }); + done(err); + } else { + var user = new User(); + user.email = profile._json.email; + user.facebook = profile.id; + user.tokens.push({ kind: 'facebook', accessToken: accessToken }); + user.profile.name = profile.displayName; + user.profile.gender = profile._json.gender; + user.profile.picture = 'https://graph.facebook.com/' + profile.id + '/picture?type=large'; + user.profile.location = (profile._json.location) ? profile._json.location.name : ''; + user.save(function(err) { + done(err, user); + }); + } + }); + }); + } + })); + + ***/ + }); + var body = fs.readFileSync(passportFile).toString(); if (body.indexOf(search) < 0) { body = body.split('\n'); var idx = body.lastIndexOf("var passport = require('passport');"); body.splice(idx + 1, 0, search); + var idx2 = body.lastIndexOf("passport.deserializeUser(function(id, done) {"); + body.splice(idx2 + 6, 0, strategy); + var output = body.join('\n'); - fs.writeFileSync('example.js', output); + fs.writeFileSync(passportFile, output); } } }); } else if (answer.category === 'api examples') { console.log('selected api examples'); } -}) -; +}); From f7a9a6dbdddefba5a41ed800f004b92e4da19ee6 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sat, 12 Apr 2014 22:54:30 -0400 Subject: [PATCH 03/23] Added Facebook auth remover --- minimizer.js | 54 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/minimizer.js b/minimizer.js index e6b4f98103..4e97cbe95c 100644 --- a/minimizer.js +++ b/minimizer.js @@ -1,12 +1,13 @@ +var _ = require('underscore'); +var colors = require('colors'); var fs = require('fs'); var inquirer = require('inquirer'); -var _ = require('underscore'); var M = require('mstring') inquirer.prompt({ type: 'list', name: 'category', - message: '☂ Hackathon Starter', + message: 'Hackathon Starter Generator:', choices: ['Authentication', 'API Examples', 'Exit'], filter: function(val) { return val.toLowerCase(); @@ -38,14 +39,17 @@ inquirer.prompt({ }); } }, function(answer) { - var passportFile = 'config/passport.js'; - if (_.contains(answer.auth, 'facebook')) { - var search = "var FacebookStrategy = require('passport-facebook').Strategy;"; - var strategy = M(function() { - /*** - // Sign in with Facebook. + var passportConfig = 'config/passport.js'; + var userModel = 'models/User.js'; + var profileTemplate = 'views/account/profile.jade'; + var loginTemplate = 'views/account/login.jade'; - passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) { + var facebookStrategyRequire = "var FacebookStrategy = require('passport-facebook').Strategy;"; + var facebookStrategy = M(function() { + /*** + // Sign in with Facebook. + + passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) { if (req.user) { User.findOne({ $or: [{ facebook: profile.id }, { email: profile.email }] }, function(err, existingUser) { if (existingUser) { @@ -90,19 +94,35 @@ inquirer.prompt({ } })); - ***/ - }); - var body = fs.readFileSync(passportFile).toString(); - if (body.indexOf(search) < 0) { + ***/ + }); + + var body = fs.readFileSync(passportConfig).toString(); + + if (_.contains(answer.auth, 'facebook')) { + + if (body.indexOf(facebookStrategyRequire) < 0) { body = body.split('\n'); var idx = body.lastIndexOf("var passport = require('passport');"); - body.splice(idx + 1, 0, search); + body.splice(idx + 1, 0, facebookStrategyRequire); var idx2 = body.lastIndexOf("passport.deserializeUser(function(id, done) {"); - body.splice(idx2 + 6, 0, strategy); - + body.splice(idx2 + 6, 0, facebookStrategy); var output = body.join('\n'); - fs.writeFileSync(passportFile, output); + fs.writeFileSync(passportConfig, output); + console.log('Facebook authentication has been added.'); + } else { + console.log('Facebook authentication is already active.'); } + } else { + // If unchecked, delete Facebook auth completely. + body = body.split('\n'); + var idx = body.lastIndexOf(facebookStrategyRequire); + body.splice(idx, 1); + var idx2 = body.lastIndexOf('// Sign in with Facebook.'); + body.splice(idx2, 47); + var output = body.join('\n'); + fs.writeFileSync(passportConfig, output); + console.log('Facebook authentication has been deleted.'); } }); } else if (answer.category === 'api examples') { From 13816a39ae00982f1e78ef907891776101d18621 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sat, 12 Apr 2014 23:06:56 -0400 Subject: [PATCH 04/23] Added terminal colors, info messages + code refactoring. --- minimizer.js | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/minimizer.js b/minimizer.js index 4e97cbe95c..57662a42b3 100644 --- a/minimizer.js +++ b/minimizer.js @@ -2,7 +2,20 @@ var _ = require('underscore'); var colors = require('colors'); var fs = require('fs'); var inquirer = require('inquirer'); -var M = require('mstring') +var M = require('mstring'); + +colors.setTheme({ + silly: 'rainbow', + input: 'grey', + verbose: 'cyan', + prompt: 'grey', + info: 'green', + data: 'grey', + help: 'cyan', + warn: 'yellow', + debug: 'blue', + error: 'red' +}); inquirer.prompt({ type: 'list', @@ -29,7 +42,7 @@ inquirer.prompt({ ], validate: function(answer) { if (answer.length < 1) { - return "You must choose at least one authentication method."; + return 'You must choose at least one authentication method.'; } return true; }, @@ -40,7 +53,7 @@ inquirer.prompt({ } }, function(answer) { var passportConfig = 'config/passport.js'; - var userModel = 'models/User.js'; + var userModel = 'models/User.js'; var profileTemplate = 'views/account/profile.jade'; var loginTemplate = 'views/account/login.jade'; @@ -101,6 +114,8 @@ inquirer.prompt({ if (_.contains(answer.auth, 'facebook')) { + // Check if FacebookStrategy is already in use. If it isn't defined, + // append it to passport.js if (body.indexOf(facebookStrategyRequire) < 0) { body = body.split('\n'); var idx = body.lastIndexOf("var passport = require('passport');"); @@ -109,12 +124,13 @@ inquirer.prompt({ body.splice(idx2 + 6, 0, facebookStrategy); var output = body.join('\n'); fs.writeFileSync(passportConfig, output); - console.log('Facebook authentication has been added.'); + console.log('Facebook authentication has been added.'.info); } else { - console.log('Facebook authentication is already active.'); + // Otherwise, safely ignore it. No need to add it twice. + console.log('Facebook authentication is already active.'.warn); } } else { - // If unchecked, delete Facebook auth completely. + // If checkbox is unchecked, delete Facebook authentication entirely. body = body.split('\n'); var idx = body.lastIndexOf(facebookStrategyRequire); body.splice(idx, 1); @@ -122,10 +138,8 @@ inquirer.prompt({ body.splice(idx2, 47); var output = body.join('\n'); fs.writeFileSync(passportConfig, output); - console.log('Facebook authentication has been deleted.'); + console.log('Facebook authentication has been removed.'.error); } }); - } else if (answer.category === 'api examples') { - console.log('selected api examples'); } }); From 0512ff93f367e8ce9fa43122abf15c364252e748 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sat, 12 Apr 2014 23:23:12 -0400 Subject: [PATCH 05/23] Removed if-checks, all authentications enabled by default --- views/account/login.jade | 60 ++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/views/account/login.jade b/views/account/login.jade index dd73045a27..6171e469a8 100644 --- a/views/account/login.jade +++ b/views/account/login.jade @@ -5,36 +5,30 @@ block content legend Sign In input(type='hidden', name='_csrf', value=_csrf) .col-sm-8.col-sm-offset-2 - if secrets.localAuth - .form-group - label.control-label(for='email') Email - input.form-control(type='text', name='email', id='email', placeholder='Email', autofocus=true) - .form-group - label.control-label(for='password') Password - input.form-control(type='password', name='password', id='password', placeholder='Password') - .form-group - button.btn.btn-primary(type='submit') - i.fa.fa-unlock-alt - | Login - a.btn.btn-link(href='/forgot') Forgot your password? - hr - if secrets.facebookAuth - a.btn.btn-block.btn-facebook.btn-social(href='/auth/facebook') - i.fa.fa-facebook - | Sign in with Facebook - if secrets.twitterAuth - a.btn.btn-block.btn-twitter.btn-social(href='/auth/twitter') - i.fa.fa-twitter - | Sign in with Twitter - if secrets.googleAuth - a.btn.btn-block.btn-google-plus.btn-social(href='/auth/google') - i.fa.fa-google-plus - | Sign in with Google - if secrets.githubAuth - a.btn.btn-block.btn-github.btn-social(href='/auth/github') - i.fa.fa-github - | Sign in with GitHub - if secrets.linkedinAuth - a.btn.btn-block.btn-linkedin.btn-social(href='/auth/linkedin') - i.fa.fa-linkedin - | Sign in with LinkedIn + .form-group + label.control-label(for='email') Email + input.form-control(type='text', name='email', id='email', placeholder='Email', autofocus=true) + .form-group + label.control-label(for='password') Password + input.form-control(type='password', name='password', id='password', placeholder='Password') + .form-group + button.btn.btn-primary(type='submit') + i.fa.fa-unlock-alt + | Login + a.btn.btn-link(href='/forgot') Forgot your password? + hr + a.btn.btn-block.btn-facebook.btn-social(href='/auth/facebook') + i.fa.fa-facebook + | Sign in with Facebook + a.btn.btn-block.btn-twitter.btn-social(href='/auth/twitter') + i.fa.fa-twitter + | Sign in with Twitter + a.btn.btn-block.btn-google-plus.btn-social(href='/auth/google') + i.fa.fa-google-plus + | Sign in with Google + a.btn.btn-block.btn-github.btn-social(href='/auth/github') + i.fa.fa-github + | Sign in with GitHub + a.btn.btn-block.btn-linkedin.btn-social(href='/auth/linkedin') + i.fa.fa-linkedin + | Sign in with LinkedIn From 15a778d163c0eb9ecdad915c466407232365df0c Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sat, 12 Apr 2014 23:48:51 -0400 Subject: [PATCH 06/23] Added login.jade add/remove facebook auth --- minimizer.js | 86 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 24 deletions(-) diff --git a/minimizer.js b/minimizer.js index 57662a42b3..cc4d329dd2 100644 --- a/minimizer.js +++ b/minimizer.js @@ -52,11 +52,6 @@ inquirer.prompt({ }); } }, function(answer) { - var passportConfig = 'config/passport.js'; - var userModel = 'models/User.js'; - var profileTemplate = 'views/account/profile.jade'; - var loginTemplate = 'views/account/login.jade'; - var facebookStrategyRequire = "var FacebookStrategy = require('passport-facebook').Strategy;"; var facebookStrategy = M(function() { /*** @@ -109,21 +104,54 @@ inquirer.prompt({ ***/ }); + var facebookButton = M(function() { + /*** + a.btn.btn-block.btn-facebook.btn-social(href='/auth/facebook') + i.fa.fa-facebook + | Sign in with Facebook + ***/ + }); - var body = fs.readFileSync(passportConfig).toString(); + var passportConfigFile = 'config/passport.js'; + var userModelFile = 'models/User.js'; + var profileTemplateFile = 'views/account/profile.jade'; + var loginTemplateFile = 'views/account/login.jade'; + + var passportConfig = fs.readFileSync(passportConfigFile).toString(); + var loginTemplate = fs.readFileSync(loginTemplateFile).toString(); + var profileTemplate = fs.readFileSync(profileTemplateFile).toString(); + + var index, + output; if (_.contains(answer.auth, 'facebook')) { - // Check if FacebookStrategy is already in use. If it isn't defined, - // append it to passport.js - if (body.indexOf(facebookStrategyRequire) < 0) { - body = body.split('\n'); - var idx = body.lastIndexOf("var passport = require('passport');"); - body.splice(idx + 1, 0, facebookStrategyRequire); - var idx2 = body.lastIndexOf("passport.deserializeUser(function(id, done) {"); - body.splice(idx2 + 6, 0, facebookStrategy); - var output = body.join('\n'); - fs.writeFileSync(passportConfig, output); + // add Facebook authentication. + if (passportConfig.indexOf(facebookStrategyRequire) < 0) { + + // config/passport.js + passportConfig = passportConfig.split('\n'); + + index = passportConfig.lastIndexOf("var passport = require('passport');"); + passportConfig.splice(index + 1, 0, facebookStrategyRequire); + index = passportConfig.lastIndexOf('passport.deserializeUser(function(id, done) {'); + passportConfig.splice(index + 6, 0, facebookStrategy); + output = passportConfig.join('\n'); + fs.writeFileSync(passportConfigFile, output); + + // views/account/login.jade + loginTemplate = loginTemplate.split('\n'); + loginTemplate.push(facebookButton); + output = loginTemplate.join('\n'); + fs.writeFileSync(loginTemplateFile, output); + + // views/account/profile.jade + profileTemplate = profileTemplate.split('\n'); + profileTemplate.push(facebookButton); + output = loginTemplate.join('\n'); + fs.writeFileSync(loginTemplateFile, output); + + console.log('Facebook authentication has been added.'.info); } else { // Otherwise, safely ignore it. No need to add it twice. @@ -131,15 +159,25 @@ inquirer.prompt({ } } else { // If checkbox is unchecked, delete Facebook authentication entirely. - body = body.split('\n'); - var idx = body.lastIndexOf(facebookStrategyRequire); - body.splice(idx, 1); - var idx2 = body.lastIndexOf('// Sign in with Facebook.'); - body.splice(idx2, 47); - var output = body.join('\n'); - fs.writeFileSync(passportConfig, output); + passportConfig = passportConfig.split('\n'); + + index = passportConfig.lastIndexOf(facebookStrategyRequire); + passportConfig.splice(index, 1); + + index = passportConfig.lastIndexOf('// Sign in with Facebook.'); + passportConfig.splice(index, 47); + + output = passportConfig.join('\n'); + fs.writeFileSync(passportConfigFile, output); + + loginTemplate = loginTemplate.split('\n'); + index = loginTemplate.indexOf(" a.btn.btn-block.btn-facebook.btn-social(href='/auth/facebook')"); + loginTemplate.splice(index, 3); + output = loginTemplate.join('\n'); + fs.writeFileSync(loginTemplateFile, output); + console.log('Facebook authentication has been removed.'.error); } }); } -}); +}); \ No newline at end of file From 90de4eedba0867250318996c5c45a071c90ad370 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sat, 12 Apr 2014 23:55:23 -0400 Subject: [PATCH 07/23] Removed auth if-checks --- views/account/profile.jade | 45 +++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/views/account/profile.jade b/views/account/profile.jade index 39c73cb2d9..56797ec00b 100644 --- a/views/account/profile.jade +++ b/views/account/profile.jade @@ -75,32 +75,27 @@ block content .page-header h3 Linked Accounts - if secrets.googleAuth - if user.google - p: a.text-danger(href='/account/unlink/google') Unlink your Google account - else - p: a(href='/auth/google') Link your Google account + if user.google + p: a.text-danger(href='/account/unlink/google') Unlink your Google account + else + p: a(href='/auth/google') Link your Google account - if secrets.facebookAuth - if user.facebook - p: a.text-danger(href='/account/unlink/facebook') Unlink your Facebook account - else - p: a(href='/auth/facebook') Link your Facebook account + if user.facebook + p: a.text-danger(href='/account/unlink/facebook') Unlink your Facebook account + else + p: a(href='/auth/facebook') Link your Facebook account - if secrets.twitterAuth - if user.twitter - p: a.text-danger(href='/account/unlink/twitter') Unlink your Twitter account - else - p: a(href='/auth/twitter') Link your Twitter account + if user.twitter + p: a.text-danger(href='/account/unlink/twitter') Unlink your Twitter account + else + p: a(href='/auth/twitter') Link your Twitter account - if secrets.githubAuth - if user.github - p: a.text-danger(href='/account/unlink/github') Unlink your GitHub account - else - p: a(href='/auth/github') Link your GitHub account + if user.github + p: a.text-danger(href='/account/unlink/github') Unlink your GitHub account + else + p: a(href='/auth/github') Link your GitHub account - if secrets.linkedinAuth - if user.linkedin - p: a.text-danger(href='/account/unlink/linkedin') Unlink your LinkedIn account - else - p: a(href='/auth/linkedin') Link your LinkedIn account + if user.linkedin + p: a.text-danger(href='/account/unlink/linkedin') Unlink your LinkedIn account + else + p: a(href='/auth/linkedin') Link your LinkedIn account \ No newline at end of file From 09ef192298a27d1b999679757ae872964df49351 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 00:08:28 -0400 Subject: [PATCH 08/23] add/remove facebook from profile template complete --- minimizer.js | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/minimizer.js b/minimizer.js index cc4d329dd2..a3d2c0c8ca 100644 --- a/minimizer.js +++ b/minimizer.js @@ -111,6 +111,15 @@ inquirer.prompt({ | Sign in with Facebook ***/ }); + var facebookLinkUnlink = M(function() { + /*** + + if user.facebook + p: a.text-danger(href='/account/unlink/facebook') Unlink your Facebook account + else + p: a(href='/auth/facebook') Link your Facebook account + ***/ + }); var passportConfigFile = 'config/passport.js'; var userModelFile = 'models/User.js'; @@ -125,13 +134,9 @@ inquirer.prompt({ output; if (_.contains(answer.auth, 'facebook')) { - // Check if FacebookStrategy is already in use. If it isn't defined, - // add Facebook authentication. if (passportConfig.indexOf(facebookStrategyRequire) < 0) { - - // config/passport.js + // config/passport.js (add) passportConfig = passportConfig.split('\n'); - index = passportConfig.lastIndexOf("var passport = require('passport');"); passportConfig.splice(index + 1, 0, facebookStrategyRequire); index = passportConfig.lastIndexOf('passport.deserializeUser(function(id, done) {'); @@ -139,43 +144,47 @@ inquirer.prompt({ output = passportConfig.join('\n'); fs.writeFileSync(passportConfigFile, output); - // views/account/login.jade + // views/account/login.jade (add) loginTemplate = loginTemplate.split('\n'); loginTemplate.push(facebookButton); output = loginTemplate.join('\n'); fs.writeFileSync(loginTemplateFile, output); - // views/account/profile.jade + // views/account/profile.jade (add) profileTemplate = profileTemplate.split('\n'); - profileTemplate.push(facebookButton); - output = loginTemplate.join('\n'); - fs.writeFileSync(loginTemplateFile, output); - + index = profileTemplate.indexOf(' h3 Linked Accounts'); + profileTemplate.splice(index + 1, 0, facebookLinkUnlink); + output = profileTemplate.join('\n'); + fs.writeFileSync(profileTemplateFile, output); console.log('Facebook authentication has been added.'.info); } else { - // Otherwise, safely ignore it. No need to add it twice. console.log('Facebook authentication is already active.'.warn); } } else { - // If checkbox is unchecked, delete Facebook authentication entirely. + // config/passport.js (remove) passportConfig = passportConfig.split('\n'); - index = passportConfig.lastIndexOf(facebookStrategyRequire); passportConfig.splice(index, 1); - index = passportConfig.lastIndexOf('// Sign in with Facebook.'); passportConfig.splice(index, 47); - output = passportConfig.join('\n'); fs.writeFileSync(passportConfigFile, output); + // views/account/login.jade (remove) loginTemplate = loginTemplate.split('\n'); index = loginTemplate.indexOf(" a.btn.btn-block.btn-facebook.btn-social(href='/auth/facebook')"); - loginTemplate.splice(index, 3); + loginTemplate.splice(index, 4); output = loginTemplate.join('\n'); fs.writeFileSync(loginTemplateFile, output); + // views/account/profile.jade (remove) + profileTemplate = profileTemplate.split('\n'); + index = profileTemplate.indexOf(" if user.facebook"); + profileTemplate.splice(index - 1, 5); + output = profileTemplate.join('\n'); + fs.writeFileSync(profileTemplateFile, output); + console.log('Facebook authentication has been removed.'.error); } }); From 17ca02a1da359ae7d63ae24b2ab665de1fb09769 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 00:19:51 -0400 Subject: [PATCH 09/23] Facebook User model add/remove complete --- minimizer.js | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/minimizer.js b/minimizer.js index a3d2c0c8ca..4c238c07ed 100644 --- a/minimizer.js +++ b/minimizer.js @@ -120,6 +120,7 @@ inquirer.prompt({ p: a(href='/auth/facebook') Link your Facebook account ***/ }); + var facebookModel = ' facebook: String,'; var passportConfigFile = 'config/passport.js'; var userModelFile = 'models/User.js'; @@ -129,6 +130,7 @@ inquirer.prompt({ var passportConfig = fs.readFileSync(passportConfigFile).toString(); var loginTemplate = fs.readFileSync(loginTemplateFile).toString(); var profileTemplate = fs.readFileSync(profileTemplateFile).toString(); + var userModel = fs.readFileSync(userModelFile).toString(); var index, output; @@ -137,9 +139,9 @@ inquirer.prompt({ if (passportConfig.indexOf(facebookStrategyRequire) < 0) { // config/passport.js (add) passportConfig = passportConfig.split('\n'); - index = passportConfig.lastIndexOf("var passport = require('passport');"); + index = passportConfig.indexOf("var passport = require('passport');"); passportConfig.splice(index + 1, 0, facebookStrategyRequire); - index = passportConfig.lastIndexOf('passport.deserializeUser(function(id, done) {'); + index = passportConfig.indexOf('passport.deserializeUser(function(id, done) {'); passportConfig.splice(index + 6, 0, facebookStrategy); output = passportConfig.join('\n'); fs.writeFileSync(passportConfigFile, output); @@ -157,6 +159,13 @@ inquirer.prompt({ output = profileTemplate.join('\n'); fs.writeFileSync(profileTemplateFile, output); + // models/User.js + userModel = userModel.split('\n'); + index = userModel.indexOf(' tokens: Array,'); + userModel.splice(index -1 , 0, facebookModel); + output = userModel.join('\n'); + fs.writeFileSync(userModelFile, output); + console.log('Facebook authentication has been added.'.info); } else { console.log('Facebook authentication is already active.'.warn); @@ -164,9 +173,9 @@ inquirer.prompt({ } else { // config/passport.js (remove) passportConfig = passportConfig.split('\n'); - index = passportConfig.lastIndexOf(facebookStrategyRequire); + index = passportConfig.indexOf(facebookStrategyRequire); passportConfig.splice(index, 1); - index = passportConfig.lastIndexOf('// Sign in with Facebook.'); + index = passportConfig.indexOf('// Sign in with Facebook.'); passportConfig.splice(index, 47); output = passportConfig.join('\n'); fs.writeFileSync(passportConfigFile, output); @@ -185,6 +194,13 @@ inquirer.prompt({ output = profileTemplate.join('\n'); fs.writeFileSync(profileTemplateFile, output); + // models/User.js + userModel = userModel.split('\n'); + index = userModel.indexOf(' facebook: String,'); + userModel.splice(index, 1); + output = userModel.join('\n'); + fs.writeFileSync(userModelFile, output); + console.log('Facebook authentication has been removed.'.error); } }); From 6eb9b631ce59ef3f7507f5de5efe5fdfafba2169 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 00:28:11 -0400 Subject: [PATCH 10/23] Updated comment style to make it easier to generate the code using multiline strings --- config/passport.js | 39 +++++++++------------------------------ 1 file changed, 9 insertions(+), 30 deletions(-) diff --git a/config/passport.js b/config/passport.js index df37109678..fdfac47093 100755 --- a/config/passport.js +++ b/config/passport.js @@ -21,9 +21,7 @@ passport.deserializeUser(function(id, done) { }); }); -/** - * Sign in using Email and Password. - */ +// Sign in using Email and Password. passport.use(new LocalStrategy({ usernameField: 'email' }, function(email, password, done) { User.findOne({ email: email }, function(err, user) { @@ -53,9 +51,7 @@ passport.use(new LocalStrategy({ usernameField: 'email' }, function(email, passw * - Else create a new account. */ -/** - * Sign in with Facebook. - */ +// Sign in with Facebook. passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) { if (req.user) { @@ -102,9 +98,7 @@ passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, r } })); -/** - * Sign in with GitHub. - */ +// Sign in with GitHub. passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refreshToken, profile, done) { if (req.user) { @@ -152,9 +146,7 @@ passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refre } })); -/** - * Sign in with Twitter. - */ +// Sign in with Twitter. passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tokenSecret, profile, done) { if (req.user) { @@ -197,9 +189,7 @@ passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tok } })); -/** - * Sign in with Google. - */ +// Sign in with Google. passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refreshToken, profile, done) { if (req.user) { @@ -245,9 +235,7 @@ passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refre } })); -/** - * Sign in with LinkedIn. - */ +// Sign in with LinkedIn. passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, refreshToken, profile, done) { if (req.user) { @@ -298,10 +286,7 @@ passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, r } })); -/** - * Tumblr API - * Uses OAuth 1.0a Strategy. - */ +// Tumblr API setup. passport.use('tumblr', new OAuthStrategy({ requestTokenURL: 'http://www.tumblr.com/oauth/request_token', @@ -322,10 +307,7 @@ passport.use('tumblr', new OAuthStrategy({ } )); -/** - * Foursquare API - * Uses OAuth 2.0 Strategy. - */ +// Foursquare API setup. passport.use('foursquare', new OAuth2Strategy({ authorizationURL: 'https://foursquare.com/oauth2/authorize', @@ -345,10 +327,7 @@ passport.use('foursquare', new OAuth2Strategy({ } )); -/** - * Venmo API - * Uses OAuth 2.0 Strategy. - */ +// Venmo API setup. passport.use('venmo', new OAuth2Strategy({ authorizationURL: 'https://api.venmo.com/v1/oauth/authorize', From f3ffd683bd491643af41d625880bdcfcea8cdc85 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 00:48:24 -0400 Subject: [PATCH 11/23] Generator code refactoring --- minimizer.js | 61 ++++++++++++++++++++-------------------------------- 1 file changed, 23 insertions(+), 38 deletions(-) diff --git a/minimizer.js b/minimizer.js index 4c238c07ed..c9d057347e 100644 --- a/minimizer.js +++ b/minimizer.js @@ -127,79 +127,64 @@ inquirer.prompt({ var profileTemplateFile = 'views/account/profile.jade'; var loginTemplateFile = 'views/account/login.jade'; - var passportConfig = fs.readFileSync(passportConfigFile).toString(); - var loginTemplate = fs.readFileSync(loginTemplateFile).toString(); - var profileTemplate = fs.readFileSync(profileTemplateFile).toString(); - var userModel = fs.readFileSync(userModelFile).toString(); + var passportConfig = fs.readFileSync(passportConfigFile).toString().split('\n'); + var loginTemplate = fs.readFileSync(loginTemplateFile).toString().split('\n'); + var profileTemplate = fs.readFileSync(profileTemplateFile).toString().split('\n'); + var userModel = fs.readFileSync(userModelFile).toString().split('\n'); - var index, - output; + var index; if (_.contains(answer.auth, 'facebook')) { if (passportConfig.indexOf(facebookStrategyRequire) < 0) { - // config/passport.js (add) - passportConfig = passportConfig.split('\n'); + + // config/passport.js (+) index = passportConfig.indexOf("var passport = require('passport');"); passportConfig.splice(index + 1, 0, facebookStrategyRequire); index = passportConfig.indexOf('passport.deserializeUser(function(id, done) {'); passportConfig.splice(index + 6, 0, facebookStrategy); - output = passportConfig.join('\n'); - fs.writeFileSync(passportConfigFile, output); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); - // views/account/login.jade (add) - loginTemplate = loginTemplate.split('\n'); + // views/account/login.jade (+) loginTemplate.push(facebookButton); - output = loginTemplate.join('\n'); - fs.writeFileSync(loginTemplateFile, output); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); - // views/account/profile.jade (add) - profileTemplate = profileTemplate.split('\n'); + // views/account/profile.jade (+) index = profileTemplate.indexOf(' h3 Linked Accounts'); profileTemplate.splice(index + 1, 0, facebookLinkUnlink); - output = profileTemplate.join('\n'); - fs.writeFileSync(profileTemplateFile, output); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); - // models/User.js - userModel = userModel.split('\n'); + // models/User.js (+) index = userModel.indexOf(' tokens: Array,'); userModel.splice(index -1 , 0, facebookModel); - output = userModel.join('\n'); - fs.writeFileSync(userModelFile, output); + fs.writeFileSync(userModelFile, userModel.join('\n')); console.log('Facebook authentication has been added.'.info); } else { console.log('Facebook authentication is already active.'.warn); } } else { - // config/passport.js (remove) - passportConfig = passportConfig.split('\n'); + + // config/passport.js (-) index = passportConfig.indexOf(facebookStrategyRequire); passportConfig.splice(index, 1); index = passportConfig.indexOf('// Sign in with Facebook.'); passportConfig.splice(index, 47); - output = passportConfig.join('\n'); - fs.writeFileSync(passportConfigFile, output); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); - // views/account/login.jade (remove) - loginTemplate = loginTemplate.split('\n'); + // views/account/login.jade (-) index = loginTemplate.indexOf(" a.btn.btn-block.btn-facebook.btn-social(href='/auth/facebook')"); loginTemplate.splice(index, 4); - output = loginTemplate.join('\n'); - fs.writeFileSync(loginTemplateFile, output); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); - // views/account/profile.jade (remove) - profileTemplate = profileTemplate.split('\n'); + // views/account/profile.jade (-) index = profileTemplate.indexOf(" if user.facebook"); profileTemplate.splice(index - 1, 5); - output = profileTemplate.join('\n'); - fs.writeFileSync(profileTemplateFile, output); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); - // models/User.js - userModel = userModel.split('\n'); + // models/User.js (-) index = userModel.indexOf(' facebook: String,'); userModel.splice(index, 1); - output = userModel.join('\n'); - fs.writeFileSync(userModelFile, output); + fs.writeFileSync(userModelFile, userModel.join('\n')); console.log('Facebook authentication has been removed.'.error); } From cf99c0cb19716a9432984d31d23aad04a2b7cb34 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 11:41:39 -0400 Subject: [PATCH 12/23] Generator code cleanup --- minimizer.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/minimizer.js b/minimizer.js index c9d057347e..fd9dd76ec4 100644 --- a/minimizer.js +++ b/minimizer.js @@ -21,12 +21,9 @@ inquirer.prompt({ type: 'list', name: 'category', message: 'Hackathon Starter Generator:', - choices: ['Authentication', 'API Examples', 'Exit'], - filter: function(val) { - return val.toLowerCase(); - } + choices: ['Authentication', 'Exit'] }, function(answer) { - if (answer.category === 'authentication') { + if (answer.category === 'Authentication') { inquirer.prompt({ type: 'checkbox', message: 'Select Authentication Providers:', @@ -45,11 +42,6 @@ inquirer.prompt({ return 'You must choose at least one authentication method.'; } return true; - }, - filter: function(val) { - return _.map(val, function(auth) { - return auth.toLowerCase(); - }); } }, function(answer) { var facebookStrategyRequire = "var FacebookStrategy = require('passport-facebook').Strategy;"; @@ -134,7 +126,7 @@ inquirer.prompt({ var index; - if (_.contains(answer.auth, 'facebook')) { + if (_.contains(answer.auth, 'FCodeacebook')) { if (passportConfig.indexOf(facebookStrategyRequire) < 0) { // config/passport.js (+) From d082b2158b4989de6ad49f69b972b84b92988bdb Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 15:23:10 -0400 Subject: [PATCH 13/23] Added Google add/remove generator --- minimizer.js | 198 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 160 insertions(+), 38 deletions(-) diff --git a/minimizer.js b/minimizer.js index fd9dd76ec4..06921478c5 100644 --- a/minimizer.js +++ b/minimizer.js @@ -34,22 +34,32 @@ inquirer.prompt({ { name: 'Google', checked: true }, { name: 'Twitter', checked: true }, { name: 'Local', checked: true }, - { name: 'LinkedIn' }, - { name: 'Instagram' } + { name: 'LinkedIn', checked: true } ], validate: function(answer) { - if (answer.length < 1) { - return 'You must choose at least one authentication method.'; - } + if (answer.length < 1) return 'You must choose at least one authentication method.'; return true; } }, function(answer) { - var facebookStrategyRequire = "var FacebookStrategy = require('passport-facebook').Strategy;"; - var facebookStrategy = M(function() { - /*** - // Sign in with Facebook. + var index; - passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) { + var passportConfigFile = 'config/passport.js'; + var userModelFile = 'models/User.js'; + var profileTemplateFile = 'views/account/profile.jade'; + var loginTemplateFile = 'views/account/login.jade'; + + var passportConfig = fs.readFileSync(passportConfigFile).toString().split('\n'); + var loginTemplate = fs.readFileSync(loginTemplateFile).toString().split('\n'); + var profileTemplate = fs.readFileSync(profileTemplateFile).toString().split('\n'); + var userModel = fs.readFileSync(userModelFile).toString().split('\n'); + + if (_.contains(answer.auth, 'Facebook')) { + var facebookStrategyRequire = "var FacebookStrategy = require('passport-facebook').Strategy;"; + var facebookStrategy = M(function() { + /*** + // Sign in with Facebook. + + passport.use(new FacebookStrategy(secrets.facebook, function(req, accessToken, refreshToken, profile, done) { if (req.user) { User.findOne({ $or: [{ facebook: profile.id }, { email: profile.email }] }, function(err, existingUser) { if (existingUser) { @@ -94,39 +104,26 @@ inquirer.prompt({ } })); - ***/ - }); - var facebookButton = M(function() { - /*** - a.btn.btn-block.btn-facebook.btn-social(href='/auth/facebook') - i.fa.fa-facebook - | Sign in with Facebook - ***/ - }); - var facebookLinkUnlink = M(function() { - /*** + ***/ + }); + var facebookButton = M(function() { + /*** + a.btn.btn-block.btn-facebook.btn-social(href='/auth/facebook') + i.fa.fa-facebook + | Sign in with Facebook + ***/ + }); + var facebookLinkUnlink = M(function() { + /*** if user.facebook p: a.text-danger(href='/account/unlink/facebook') Unlink your Facebook account else p: a(href='/auth/facebook') Link your Facebook account - ***/ - }); - var facebookModel = ' facebook: String,'; + ***/ + }); + var facebookModel = ' facebook: String,'; - var passportConfigFile = 'config/passport.js'; - var userModelFile = 'models/User.js'; - var profileTemplateFile = 'views/account/profile.jade'; - var loginTemplateFile = 'views/account/login.jade'; - - var passportConfig = fs.readFileSync(passportConfigFile).toString().split('\n'); - var loginTemplate = fs.readFileSync(loginTemplateFile).toString().split('\n'); - var profileTemplate = fs.readFileSync(profileTemplateFile).toString().split('\n'); - var userModel = fs.readFileSync(userModelFile).toString().split('\n'); - - var index; - - if (_.contains(answer.auth, 'FCodeacebook')) { if (passportConfig.indexOf(facebookStrategyRequire) < 0) { // config/passport.js (+) @@ -147,7 +144,7 @@ inquirer.prompt({ // models/User.js (+) index = userModel.indexOf(' tokens: Array,'); - userModel.splice(index -1 , 0, facebookModel); + userModel.splice(index - 1, 0, facebookModel); fs.writeFileSync(userModelFile, userModel.join('\n')); console.log('Facebook authentication has been added.'.info); @@ -180,6 +177,131 @@ inquirer.prompt({ console.log('Facebook authentication has been removed.'.error); } + + if (_.contains(answer.auth, 'Google')) { + var googleStrategyRequire = "var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;"; + var googleStrategy = M(function() { + /*** + // Sign in with Google. + + passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refreshToken, profile, done) { + if (req.user) { + User.findOne({ $or: [{ google: profile.id }, { email: profile.email }] }, function(err, existingUser) { + if (existingUser) { + req.flash('errors', { msg: 'There is already a Google account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); + done(err); + } else { + User.findById(req.user.id, function(err, user) { + user.google = profile.id; + user.tokens.push({ kind: 'google', accessToken: accessToken }); + user.profile.name = user.profile.name || profile.displayName; + user.profile.gender = user.profile.gender || profile._json.gender; + user.profile.picture = user.profile.picture || profile._json.picture; + user.save(function(err) { + req.flash('info', { msg: 'Google account has been linked.' }); + done(err, user); + }); + }); + } + }); + } else { + User.findOne({ google: profile.id }, function(err, existingUser) { + if (existingUser) return done(null, existingUser); + User.findOne({ email: profile._json.email }, function(err, existingEmailUser) { + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Google manually from Account Settings.' }); + done(err); + } else { + var user = new User(); + user.email = profile._json.email; + user.google = profile.id; + user.tokens.push({ kind: 'google', accessToken: accessToken }); + user.profile.name = profile.displayName; + user.profile.gender = profile._json.gender; + user.profile.picture = profile._json.picture; + user.save(function(err) { + done(err, user); + }); + } + }); + }); + } + })); + + ***/ + }); + + var googleButton = M(function() { + /*** + a.btn.btn-block.btn-google-plus.btn-social(href='/auth/google') + i.fa.fa-google-plus + | Sign in with Google + ***/ + }); + var googleLinkUnlink = M(function() { + /*** + + if user.google + p: a.text-danger(href='/account/unlink/google') Unlink your Google account + else + p: a(href='/auth/google') Link your Google account + ***/ + }); + var googleModel = ' google: String,'; + + if (passportConfig.indexOf(googleStrategyRequire) < 0) { + + // config/passport.js (+) + index = passportConfig.indexOf("var passport = require('passport');"); + passportConfig.splice(index + 1, 0, googleStrategyRequire); + index = passportConfig.indexOf('passport.deserializeUser(function(id, done) {'); + passportConfig.splice(index + 6, 0, googleStrategy); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (+) + loginTemplate.push(googleButton); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (+) + index = profileTemplate.indexOf(' h3 Linked Accounts'); + profileTemplate.splice(index + 1, 0, googleLinkUnlink); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (+) + index = userModel.indexOf(' tokens: Array,'); + userModel.splice(index - 1, 0, googleModel); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('Google authentication has been added.'.info); + } else { + console.log('Google authentication is already active.'.warn); + } + } else { + + // config/passport.js (-) + index = passportConfig.indexOf(googleStrategyRequire); + passportConfig.splice(index, 1); + index = passportConfig.indexOf('// Sign in with Google.'); + passportConfig.splice(index, 46); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (-) + index = loginTemplate.indexOf(" a.btn.btn-block.btn-google-plus.btn-social(href='/auth/google')"); + loginTemplate.splice(index, 4); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (-) + index = profileTemplate.indexOf(" if user.google"); + profileTemplate.splice(index - 1, 5); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (-) + index = userModel.indexOf(' google: String,'); + userModel.splice(index, 1); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('Google authentication has been removed.'.error); + } }); } }); \ No newline at end of file From a4b08aed2eca5ff4329925ad18f8e6b3e0a3b10b Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 16:26:26 -0400 Subject: [PATCH 14/23] Prettified generator CLI with unicode icons and multi-line message. --- minimizer.js | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/minimizer.js b/minimizer.js index 06921478c5..c1962b60fa 100644 --- a/minimizer.js +++ b/minimizer.js @@ -21,20 +21,31 @@ inquirer.prompt({ type: 'list', name: 'category', message: 'Hackathon Starter Generator:', - choices: ['Authentication', 'Exit'] + choices: ['☂ Authentication', '☱ Exit'] }, function(answer) { - if (answer.category === 'Authentication') { + if (answer.category.match('Authentication')) { inquirer.prompt({ type: 'checkbox', - message: 'Select Authentication Providers:', + message: 'Select Authentication Providers', name: 'auth', choices: [ + new inquirer.Separator(M(function() { + /*** + + ╔═════════════════════════════════════════════════════════════════╗ + ║ THIS TOOL IS STILL IN EXPERIMENTAL STAGE! USE AT YOUR OWN RISK. ║ + ║ ALWAYS USE VERSION CONTROL SYSTEM SO YOU COULD REVERT CHANGES. ║ + ╚═════════════════════════════════════════════════════════════════╝ + + ***/ + })), { name: 'Facebook', checked: true }, { name: 'GitHub', checked: true }, { name: 'Google', checked: true }, { name: 'Twitter', checked: true }, { name: 'Local', checked: true }, - { name: 'LinkedIn', checked: true } + { name: 'LinkedIn', checked: true }, + new inquirer.Separator('Press ctrl+ to quit'), ], validate: function(answer) { if (answer.length < 1) return 'You must choose at least one authentication method.'; @@ -147,9 +158,9 @@ inquirer.prompt({ userModel.splice(index - 1, 0, facebookModel); fs.writeFileSync(userModelFile, userModel.join('\n')); - console.log('Facebook authentication has been added.'.info); + console.log('✓ Facebook authentication has been added.'.info); } else { - console.log('Facebook authentication is already active.'.warn); + console.log('✓ Facebook authentication is already active.'.warn); } } else { @@ -175,7 +186,7 @@ inquirer.prompt({ userModel.splice(index, 1); fs.writeFileSync(userModelFile, userModel.join('\n')); - console.log('Facebook authentication has been removed.'.error); + console.log('✗ Facebook authentication has been removed.'.error); } if (_.contains(answer.auth, 'Google')) { @@ -272,9 +283,9 @@ inquirer.prompt({ userModel.splice(index - 1, 0, googleModel); fs.writeFileSync(userModelFile, userModel.join('\n')); - console.log('Google authentication has been added.'.info); + console.log('✓ Google authentication has been added.'.info); } else { - console.log('Google authentication is already active.'.warn); + console.log('✓ Google authentication is already active.'.warn); } } else { @@ -300,7 +311,7 @@ inquirer.prompt({ userModel.splice(index, 1); fs.writeFileSync(userModelFile, userModel.join('\n')); - console.log('Google authentication has been removed.'.error); + console.log('✗ Google authentication has been removed.'.error); } }); } From 4552323b1ecf779137ce861e6e7d77ec9579772a Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 16:39:25 -0400 Subject: [PATCH 15/23] Added GitHub authentication generator --- minimizer.js | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) diff --git a/minimizer.js b/minimizer.js index c1962b60fa..24b7d846b6 100644 --- a/minimizer.js +++ b/minimizer.js @@ -20,7 +20,7 @@ colors.setTheme({ inquirer.prompt({ type: 'list', name: 'category', - message: 'Hackathon Starter Generator:', + message: 'Hackathon Starter:', choices: ['☂ Authentication', '☱ Exit'] }, function(answer) { if (answer.category.match('Authentication')) { @@ -48,7 +48,7 @@ inquirer.prompt({ new inquirer.Separator('Press ctrl+ to quit'), ], validate: function(answer) { - if (answer.length < 1) return 'You must choose at least one authentication method.'; + if (answer.length < 1) return 'You must choose at least one authentication provider.'; return true; } }, function(answer) { @@ -189,6 +189,133 @@ inquirer.prompt({ console.log('✗ Facebook authentication has been removed.'.error); } + if (_.contains(answer.auth, 'GitHub')) { + var githubStrategyRequire = "var GitHubStrategy = require('passport-github').Strategy;"; + var githubStrategy = M(function() { + /*** + // Sign in with GitHub. + + passport.use(new GitHubStrategy(secrets.github, function(req, accessToken, refreshToken, profile, done) { + if (req.user) { + User.findOne({ $or: [{ github: profile.id }, { email: profile.email }] }, function(err, existingUser) { + if (existingUser) { + req.flash('errors', { msg: 'There is already a GitHub account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); + done(err); + } else { + User.findById(req.user.id, function(err, user) { + user.github = profile.id; + user.tokens.push({ kind: 'github', accessToken: accessToken }); + user.profile.name = user.profile.name || profile.displayName; + user.profile.picture = user.profile.picture || profile._json.avatar_url; + user.profile.location = user.profile.location || profile._json.location; + user.profile.website = user.profile.website || profile._json.blog; + user.save(function(err) { + req.flash('info', { msg: 'GitHub account has been linked.' }); + done(err, user); + }); + }); + } + }); + } else { + User.findOne({ github: profile.id }, function(err, existingUser) { + if (existingUser) return done(null, existingUser); + User.findOne({ email: profile._json.email }, function(err, existingEmailUser) { + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with GitHub manually from Account Settings.' }); + done(err); + } else { + var user = new User(); + user.email = profile._json.email; + user.github = profile.id; + user.tokens.push({ kind: 'github', accessToken: accessToken }); + user.profile.name = profile.displayName; + user.profile.picture = profile._json.avatar_url; + user.profile.location = profile._json.location; + user.profile.website = profile._json.blog; + user.save(function(err) { + done(err, user); + }); + } + }); + }); + } + })); + + ***/ + }); + + var githubButton = M(function() { + /*** + a.btn.btn-block.btn-github.btn-social(href='/auth/github') + i.fa.fa-github + | Sign in with GitHub + ***/ + }); + var githubLinkUnlink = M(function() { + /*** + + if user.github + p: a.text-danger(href='/account/unlink/github') Unlink your GitHub account + else + p: a(href='/auth/github') Link your GitHub account + ***/ + }); + var githubModel = ' github: String,'; + + if (passportConfig.indexOf(githubStrategyRequire) < 0) { + + // config/passport.js (+) + index = passportConfig.indexOf("var passport = require('passport');"); + passportConfig.splice(index + 1, 0, githubStrategyRequire); + index = passportConfig.indexOf('passport.deserializeUser(function(id, done) {'); + passportConfig.splice(index + 6, 0, githubStrategy); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (+) + loginTemplate.push(githubButton); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (+) + index = profileTemplate.indexOf(' h3 Linked Accounts'); + profileTemplate.splice(index + 1, 0, githubLinkUnlink); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (+) + index = userModel.indexOf(' tokens: Array,'); + userModel.splice(index - 1, 0, githubModel); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✓ GitHub authentication has been added.'.info); + } else { + console.log('✓ GitHub authentication is already active.'.warn); + } + } else { + + // config/passport.js (-) + index = passportConfig.indexOf(githubStrategyRequire); + passportConfig.splice(index, 1); + index = passportConfig.indexOf('// Sign in with GitHub.'); + passportConfig.splice(index, 46); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (-) + index = loginTemplate.indexOf(" a.btn.btn-block.btn-github.btn-social(href='/auth/github')"); + loginTemplate.splice(index, 4); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (-) + index = profileTemplate.indexOf(" if user.github"); + profileTemplate.splice(index - 1, 5); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (-) + index = userModel.indexOf(' github: String,'); + userModel.splice(index, 1); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✗ GitHub authentication has been removed.'.error); + } + if (_.contains(answer.auth, 'Google')) { var googleStrategyRequire = "var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;"; var googleStrategy = M(function() { From 43effc7ee42cfb70c1b1641c413f8002b35489f7 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 16:47:56 -0400 Subject: [PATCH 16/23] Added Twitter authentication generator --- minimizer.js | 127 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 124 insertions(+), 3 deletions(-) diff --git a/minimizer.js b/minimizer.js index 24b7d846b6..2b2d9911f5 100644 --- a/minimizer.js +++ b/minimizer.js @@ -295,7 +295,7 @@ inquirer.prompt({ index = passportConfig.indexOf(githubStrategyRequire); passportConfig.splice(index, 1); index = passportConfig.indexOf('// Sign in with GitHub.'); - passportConfig.splice(index, 46); + passportConfig.splice(index, 48); fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); // views/account/login.jade (-) @@ -304,7 +304,7 @@ inquirer.prompt({ fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); // views/account/profile.jade (-) - index = profileTemplate.indexOf(" if user.github"); + index = profileTemplate.indexOf(' if user.github'); profileTemplate.splice(index - 1, 5); fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); @@ -429,7 +429,7 @@ inquirer.prompt({ fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); // views/account/profile.jade (-) - index = profileTemplate.indexOf(" if user.google"); + index = profileTemplate.indexOf(' if user.google'); profileTemplate.splice(index - 1, 5); fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); @@ -440,6 +440,127 @@ inquirer.prompt({ console.log('✗ Google authentication has been removed.'.error); } + + if (_.contains(answer.auth, 'Twitter')) { + var twitterStrategyRequire = "var TwitterStrategy = require('passport-twitter').Strategy;"; + var twitterStrategy = M(function() { + /*** + // Sign in with Twitter. + + passport.use(new TwitterStrategy(secrets.twitter, function(req, accessToken, tokenSecret, profile, done) { + if (req.user) { + User.findOne({ twitter: profile.id }, function(err, existingUser) { + if (existingUser) { + req.flash('errors', { msg: 'There is already a Twitter account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); + done(err); + } else { + User.findById(req.user.id, function(err, user) { + user.twitter = profile.id; + user.tokens.push({ kind: 'twitter', accessToken: accessToken, tokenSecret: tokenSecret }); + user.profile.name = user.profile.name || profile.displayName; + user.profile.location = user.profile.location || profile._json.location; + user.profile.picture = user.profile.picture || profile._json.profile_image_url; + user.save(function(err) { + req.flash('info', { msg: 'Twitter account has been linked.' }); + done(err, user); + }); + }); + } + }); + + } else { + User.findOne({ twitter: profile.id }, function(err, existingUser) { + if (existingUser) return done(null, existingUser); + var user = new User(); + // Twitter will not provide an email address. Period. + // But a person’s twitter username is guaranteed to be unique + // so we can "fake" a twitter email address as follows: + user.email = profile.username + "@twitter.com"; + user.twitter = profile.id; + user.tokens.push({ kind: 'twitter', accessToken: accessToken, tokenSecret: tokenSecret }); + user.profile.name = profile.displayName; + user.profile.location = profile._json.location; + user.profile.picture = profile._json.profile_image_url; + user.save(function(err) { + done(err, user); + }); + }); + } + })); + ***/ + }); + + var twitterButton = M(function() { + /*** + a.btn.btn-block.btn-google-plus.btn-social(href='/auth/google') + i.fa.fa-google-plus + | Sign in with Google + ***/ + }); + var twitterLinkUnlink = M(function() { + /*** + + if user.google + p: a.text-danger(href='/account/unlink/google') Unlink your Google account + else + p: a(href='/auth/google') Link your Google account + ***/ + }); + var twitterModel = ' twitter: String,'; + + if (passportConfig.indexOf(twitterStrategyRequire) < 0) { + + // config/passport.js (+) + index = passportConfig.indexOf("var passport = require('passport');"); + passportConfig.splice(index + 1, 0, twitterStrategyRequire); + index = passportConfig.indexOf('passport.deserializeUser(function(id, done) {'); + passportConfig.splice(index + 6, 0, twitterStrategy); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (+) + loginTemplate.push(twitterButton); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (+) + index = profileTemplate.indexOf(' h3 Linked Accounts'); + profileTemplate.splice(index + 1, 0, twitterLinkUnlink); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (+) + index = userModel.indexOf(' tokens: Array,'); + userModel.splice(index - 1, 0, twitterModel); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✓ Twitter authentication has been added.'.info); + } else { + console.log('✓ Twitter authentication is already active.'.warn); + } + } else { + + // config/passport.js (-) + index = passportConfig.indexOf(twitterStrategyRequire); + passportConfig.splice(index, 1); + index = passportConfig.indexOf('// Sign in with Twitter.'); + passportConfig.splice(index, 43); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (-) + index = loginTemplate.indexOf(" a.btn.btn-block.btn-twitter.btn-social(href='/auth/twitter')"); + loginTemplate.splice(index, 4); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (-) + index = profileTemplate.indexOf(' if user.twitter'); + profileTemplate.splice(index - 1, 5); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (-) + index = userModel.indexOf(' twitter: String,'); + userModel.splice(index, 1); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✗ Twitter authentication has been removed.'.error); + } }); } }); \ No newline at end of file From 093ca98466836115133c1382a95a931a9fbea7fa Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 16:53:11 -0400 Subject: [PATCH 17/23] Added LinkedIn authentication generator --- minimizer.js | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/minimizer.js b/minimizer.js index 2b2d9911f5..57549514d2 100644 --- a/minimizer.js +++ b/minimizer.js @@ -561,6 +561,135 @@ inquirer.prompt({ console.log('✗ Twitter authentication has been removed.'.error); } + + if (_.contains(answer.auth, 'LinkedIn')) { + var linkedinStrategyRequire = "var LinkedInStrategy = require('passport-linkedin-oauth2').Strategy;"; + var linkedinStrategy = M(function() { + /*** + // Sign in with LinkedIn. + + passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, refreshToken, profile, done) { + if (req.user) { + User.findOne({ $or: [ + { linkedin: profile.id }, + { email: profile._json.emailAddress } + ] }, function(err, existingUser) { + if (existingUser) { + req.flash('errors', { msg: 'There is already a LinkedIn account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); + done(err); + } else { + User.findById(req.user.id, function(err, user) { + user.linkedin = profile.id; + user.tokens.push({ kind: 'linkedin', accessToken: accessToken }); + user.profile.name = user.profile.name || profile.displayName; + user.profile.location = user.profile.location || profile._json.location.name; + user.profile.picture = user.profile.picture || profile._json.pictureUrl; + user.profile.website = user.profile.website || profile._json.publicProfileUrl; + user.save(function(err) { + req.flash('info', { msg: 'LinkedIn account has been linked.' }); + done(err, user); + }); + }); + } + }); + } else { + User.findOne({ linkedin: profile.id }, function(err, existingUser) { + if (existingUser) return done(null, existingUser); + User.findOne({ email: profile._json.emailAddress }, function(err, existingEmailUser) { + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with LinkedIn manually from Account Settings.' }); + done(err); + } else { + var user = new User(); + user.linkedin = profile.id; + user.tokens.push({ kind: 'linkedin', accessToken: accessToken }); + user.email = profile._json.emailAddress; + user.profile.name = profile.displayName; + user.profile.location = profile._json.location.name; + user.profile.picture = profile._json.pictureUrl; + user.profile.website = profile._json.publicProfileUrl; + user.save(function(err) { + done(err, user); + }); + } + }); + }); + } + })); + ***/ + }); + + var linkedinButton = M(function() { + /*** + a.btn.btn-block.btn-linkedin.btn-social(href='/auth/linkedin') + i.fa.fa-linkedin + | Sign in with LinkedIn + ***/ + }); + var linkedinLinkUnlink = M(function() { + /*** + + if user.linkedin + p: a.text-danger(href='/account/unlink/linkedin') Unlink your LinkedIn account + else + p: a(href='/auth/linkedin') Link your LinkedIn account + ***/ + }); + var linkedinModel = ' linkedin: String,'; + + if (passportConfig.indexOf(linkedinStrategyRequire) < 0) { + + // config/passport.js (+) + index = passportConfig.indexOf("var passport = require('passport');"); + passportConfig.splice(index + 1, 0, linkedinStrategyRequire); + index = passportConfig.indexOf('passport.deserializeUser(function(id, done) {'); + passportConfig.splice(index + 6, 0, linkedinStrategy); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (+) + loginTemplate.push(linkedinButton); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (+) + index = profileTemplate.indexOf(' h3 Linked Accounts'); + profileTemplate.splice(index + 1, 0, linkedinLinkUnlink); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (+) + index = userModel.indexOf(' tokens: Array,'); + userModel.splice(index - 1, 0, linkedinModel); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✓ LinkedIn authentication has been added.'.info); + } else { + console.log('✓ LinkedIn authentication is already active.'.warn); + } + } else { + + // config/passport.js (-) + index = passportConfig.indexOf(linkedinStrategyRequire); + passportConfig.splice(index, 1); + index = passportConfig.indexOf('// Sign in with LinkedIn.'); + passportConfig.splice(index, 51); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (-) + index = loginTemplate.indexOf(" a.btn.btn-block.btn-linkedin.btn-social(href='/auth/linkedin')"); + loginTemplate.splice(index, 4); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (-) + index = profileTemplate.indexOf(' if user.linkedin'); + profileTemplate.splice(index - 1, 5); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (-) + index = userModel.indexOf(' linkedin: String,'); + userModel.splice(index, 1); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✗ LinkedIn authentication has been removed.'.error); + } }); } }); \ No newline at end of file From 263af71d90cc3d21c1a401025e264a4ec6edebab Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 17:19:11 -0400 Subject: [PATCH 18/23] Removed if-check for local auth on change password form --- views/account/profile.jade | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/views/account/profile.jade b/views/account/profile.jade index 56797ec00b..3f5fdbe812 100644 --- a/views/account/profile.jade +++ b/views/account/profile.jade @@ -41,26 +41,24 @@ block content i.fa.fa-magnet | Update Profile + .page-header + h3 Change Password - if secrets.localAuth - .page-header - h3 Change Password - - form.form-horizontal(action='/account/password', method='POST') - input(type='hidden', name='_csrf', value=_csrf) - .form-group - label.col-sm-3.control-label(for='password') New Password - .col-sm-4 - input.form-control(type='password', name='password', id='password') - .form-group - label.col-sm-3.control-label(for='confirmPassword') Confirm Password - .col-sm-4 - input.form-control(type='password', name='confirmPassword', id='confirmPassword') - .form-group - .col-sm-offset-3.col-sm-4 - button.btn.btn.btn-primary(type='submit') - i.fa.fa-keyboard-o - | Change Password + form.form-horizontal(action='/account/password', method='POST') + input(type='hidden', name='_csrf', value=_csrf) + .form-group + label.col-sm-3.control-label(for='password') New Password + .col-sm-4 + input.form-control(type='password', name='password', id='password') + .form-group + label.col-sm-3.control-label(for='confirmPassword') Confirm Password + .col-sm-4 + input.form-control(type='password', name='confirmPassword', id='confirmPassword') + .form-group + .col-sm-offset-3.col-sm-4 + button.btn.btn.btn-primary(type='submit') + i.fa.fa-keyboard-o + | Change Password .page-header h3 Delete Account From 935a8f61c0fb4c48a8d6575bb04fe7b4e117c7a1 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Sun, 13 Apr 2014 17:21:02 -0400 Subject: [PATCH 19/23] Added Local authentication generator --- minimizer.js | 251 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 251 insertions(+) diff --git a/minimizer.js b/minimizer.js index 57549514d2..46167cf834 100644 --- a/minimizer.js +++ b/minimizer.js @@ -690,6 +690,257 @@ inquirer.prompt({ console.log('✗ LinkedIn authentication has been removed.'.error); } + + if (_.contains(answer.auth, 'LinkedIn')) { + var linkedinStrategyRequire = "var LinkedInStrategy = require('passport-linkedin-oauth2').Strategy;"; + var linkedinStrategy = M(function() { + /*** + // Sign in with LinkedIn. + + passport.use(new LinkedInStrategy(secrets.linkedin, function(req, accessToken, refreshToken, profile, done) { + if (req.user) { + User.findOne({ $or: [ + { linkedin: profile.id }, + { email: profile._json.emailAddress } + ] }, function(err, existingUser) { + if (existingUser) { + req.flash('errors', { msg: 'There is already a LinkedIn account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); + done(err); + } else { + User.findById(req.user.id, function(err, user) { + user.linkedin = profile.id; + user.tokens.push({ kind: 'linkedin', accessToken: accessToken }); + user.profile.name = user.profile.name || profile.displayName; + user.profile.location = user.profile.location || profile._json.location.name; + user.profile.picture = user.profile.picture || profile._json.pictureUrl; + user.profile.website = user.profile.website || profile._json.publicProfileUrl; + user.save(function(err) { + req.flash('info', { msg: 'LinkedIn account has been linked.' }); + done(err, user); + }); + }); + } + }); + } else { + User.findOne({ linkedin: profile.id }, function(err, existingUser) { + if (existingUser) return done(null, existingUser); + User.findOne({ email: profile._json.emailAddress }, function(err, existingEmailUser) { + if (existingEmailUser) { + req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with LinkedIn manually from Account Settings.' }); + done(err); + } else { + var user = new User(); + user.linkedin = profile.id; + user.tokens.push({ kind: 'linkedin', accessToken: accessToken }); + user.email = profile._json.emailAddress; + user.profile.name = profile.displayName; + user.profile.location = profile._json.location.name; + user.profile.picture = profile._json.pictureUrl; + user.profile.website = profile._json.publicProfileUrl; + user.save(function(err) { + done(err, user); + }); + } + }); + }); + } + })); + ***/ + }); + + var linkedinButton = M(function() { + /*** + a.btn.btn-block.btn-linkedin.btn-social(href='/auth/linkedin') + i.fa.fa-linkedin + | Sign in with LinkedIn + ***/ + }); + var linkedinLinkUnlink = M(function() { + /*** + + if user.linkedin + p: a.text-danger(href='/account/unlink/linkedin') Unlink your LinkedIn account + else + p: a(href='/auth/linkedin') Link your LinkedIn account + ***/ + }); + var linkedinModel = ' linkedin: String,'; + + if (passportConfig.indexOf(linkedinStrategyRequire) < 0) { + + // config/passport.js (+) + index = passportConfig.indexOf("var passport = require('passport');"); + passportConfig.splice(index + 1, 0, linkedinStrategyRequire); + index = passportConfig.indexOf('passport.deserializeUser(function(id, done) {'); + passportConfig.splice(index + 6, 0, linkedinStrategy); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (+) + loginTemplate.push(linkedinButton); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (+) + index = profileTemplate.indexOf(' h3 Linked Accounts'); + profileTemplate.splice(index + 1, 0, linkedinLinkUnlink); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (+) + index = userModel.indexOf(' tokens: Array,'); + userModel.splice(index - 1, 0, linkedinModel); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✓ LinkedIn authentication has been added.'.info); + } else { + console.log('✓ LinkedIn authentication is already active.'.warn); + } + } else { + + // config/passport.js (-) + index = passportConfig.indexOf(linkedinStrategyRequire); + passportConfig.splice(index, 1); + index = passportConfig.indexOf('// Sign in with LinkedIn.'); + passportConfig.splice(index, 51); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (-) + index = loginTemplate.indexOf(" a.btn.btn-block.btn-linkedin.btn-social(href='/auth/linkedin')"); + loginTemplate.splice(index, 4); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (-) + index = profileTemplate.indexOf(' if user.linkedin'); + profileTemplate.splice(index - 1, 5); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (-) + index = userModel.indexOf(' linkedin: String,'); + userModel.splice(index, 1); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✗ LinkedIn authentication has been removed.'.error); + } + + if (_.contains(answer.auth, 'Local')) { + var localStrategyRequire = "var LocalStrategy = require('passport-local').Strategy;"; + var localStrategy = M(function() { + /*** + // Sign in using Email and Password. + + passport.use(new LocalStrategy({ usernameField: 'email' }, function(email, password, done) { + User.findOne({ email: email }, function(err, user) { + if (!user) return done(null, false, { message: 'Email ' + email + ' not found'}); + user.comparePassword(password, function(err, isMatch) { + if (isMatch) { + return done(null, user); + } else { + return done(null, false, { message: 'Invalid email or password.' }); + } + }); + }); + })); + ***/ + }); + + var localLoginForm = M(function() { + /*** + form(method='POST') + legend Sign In + input(type='hidden', name='_csrf', value=_csrf) + .col-sm-8.col-sm-offset-2 + .form-group + label.control-label(for='email') Email + input.form-control(type='text', name='email', id='email', placeholder='Email', autofocus=true) + .form-group + label.control-label(for='password') Password + input.form-control(type='password', name='password', id='password', placeholder='Password') + .form-group + button.btn.btn-primary(type='submit') + i.fa.fa-unlock-alt + | Login + a.btn.btn-link(href='/forgot') Forgot your password? + hr + ***/ + }); + + var localChangePassword = M(function() { + /*** + .page-header + h3 Change Password + + form.form-horizontal(action='/account/password', method='POST') + input(type='hidden', name='_csrf', value=_csrf) + .form-group + label.col-sm-3.control-label(for='password') New Password + .col-sm-4 + input.form-control(type='password', name='password', id='password') + .form-group + label.col-sm-3.control-label(for='confirmPassword') Confirm Password + .col-sm-4 + input.form-control(type='password', name='confirmPassword', id='confirmPassword') + .form-group + .col-sm-offset-3.col-sm-4 + button.btn.btn.btn-primary(type='submit') + i.fa.fa-keyboard-o + | Change Password + ***/ + }); + + var localModel = ' password: String,'; + + if (passportConfig.indexOf(localStrategyRequire) < 0) { + + // config/passport.js (+) + index = passportConfig.indexOf("var passport = require('passport');"); + passportConfig.splice(index + 1, 0, localStrategyRequire); + index = passportConfig.indexOf('passport.deserializeUser(function(id, done) {'); + passportConfig.splice(index + 6, 0, localStrategy); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (+) + index = profileTemplate.indexOf('block content'); + profileTemplate.splice(index + 1, 0, localLoginForm); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (+) + index = profileTemplate.indexOf(' | Update Profile'); + profileTemplate.splice(index + 1, 0, localChangePassword); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (+) + index = userModel.indexOf('email: { type: String, unique: true, lowercase: true },'); + userModel.splice(index, 0, localModel); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✓ Local authentication has been added.'.info); + } else { + console.log('✓ Local authentication is already active.'.warn); + } + } else { + + // config/passport.js (-) + index = passportConfig.indexOf(localStrategyRequire); + passportConfig.splice(index, 1); + index = passportConfig.indexOf('// Sign in using Email and Password.'); + passportConfig.splice(index, 15); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // views/account/login.jade (-) + index = loginTemplate.indexOf(" form(method='POST')"); + loginTemplate.splice(index, 15); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // views/account/profile.jade (-) + index = profileTemplate.indexOf(' h3 Change Password'); + profileTemplate.splice(index - 1, 19); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // models/User.js (-) + index = userModel.indexOf(' password: String,'); + userModel.splice(index, 1); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✗ Local authentication has been removed.'.error); + } }); } }); \ No newline at end of file From 8eda44f5363921eb62a4de40695cf64d8f2b39ed Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Mon, 14 Apr 2014 01:36:24 -0400 Subject: [PATCH 20/23] Renamed minimizer.js to generator.js --- minimizer.js => generator.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename minimizer.js => generator.js (100%) diff --git a/minimizer.js b/generator.js similarity index 100% rename from minimizer.js rename to generator.js From 1c841e283f6e858ea2af768177ce2ff7193a253a Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Mon, 14 Apr 2014 02:30:28 -0400 Subject: [PATCH 21/23] Instagram auth generator! --- generator.js | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/generator.js b/generator.js index 46167cf834..6fd6aa22ab 100644 --- a/generator.js +++ b/generator.js @@ -45,6 +45,7 @@ inquirer.prompt({ { name: 'Twitter', checked: true }, { name: 'Local', checked: true }, { name: 'LinkedIn', checked: true }, + { name: 'Instagram' }, new inquirer.Separator('Press ctrl+ to quit'), ], validate: function(answer) { @@ -56,6 +57,8 @@ inquirer.prompt({ var passportConfigFile = 'config/passport.js'; var userModelFile = 'models/User.js'; + var appFile = 'app.js'; + var secretsFile = 'config/secrets.js'; var profileTemplateFile = 'views/account/profile.jade'; var loginTemplateFile = 'views/account/login.jade'; @@ -63,6 +66,8 @@ inquirer.prompt({ var loginTemplate = fs.readFileSync(loginTemplateFile).toString().split('\n'); var profileTemplate = fs.readFileSync(profileTemplateFile).toString().split('\n'); var userModel = fs.readFileSync(userModelFile).toString().split('\n'); + var app = fs.readFileSync(appFile).toString().split('\n'); + var secrets = fs.readFileSync(secretsFile).toString().split('\n'); if (_.contains(answer.auth, 'Facebook')) { var facebookStrategyRequire = "var FacebookStrategy = require('passport-facebook').Strategy;"; @@ -941,6 +946,155 @@ inquirer.prompt({ console.log('✗ Local authentication has been removed.'.error); } + + if (_.contains(answer.auth, 'Instagram')) { + var instagramRoutes = M(function() { + /*** + app.get('/auth/instagram', passport.authenticate('instagram')); + app.get('/auth/instagram/callback', passport.authenticate('instagram', { failureRedirect: '/login' }), function(req, res) { + res.redirect(req.session.returnTo || '/'); + }); + ***/ + }); + var instagramSecrets = M(function() { + /*** + instagram: { + clientID: process.env.INSTAGRAM_ID || 'Your Client ID', + clientSecret: process.env.INSTAGRAM_SECRET || 'Your Client Secret', + callbackURL: '/auth/instagram/callback', + passReqToCallback: true + }, + + ***/ + }); + var instagramStrategyRequire = "var InstagramStrategy = require('passport-instagram').Strategy;"; + var instagramStrategy = M(function() { + /*** + // Sign in with Instagram. + + passport.use(new InstagramStrategy(secrets.instagram,function(req, accessToken, refreshToken, profile, done) { + if (req.user) { + User.findOne({ $or: [{ instagram: profile.id }, { email: profile.email }] }, function(err, existingUser) { + if (existingUser) { + req.flash('errors', { msg: 'There is already an Instagram account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); + done(err); + } else { + User.findById(req.user.id, function(err, user) { + user.instagram = profile.id; + user.tokens.push({ kind: 'instagram', accessToken: accessToken }); + user.profile.name = user.profile.name || profile.displayName; + user.profile.picture = user.profile.picture || profile._json.data.profile_picture; + user.profile.website = user.profile.website || profile._json.data.website; + user.save(function(err) { + req.flash('info', { msg: 'Instagram account has been linked.' }); + done(err, user); + }); + }); + } + }); + } else { + User.findOne({ instagram: profile.id }, function(err, existingUser) { + if (existingUser) return done(null, existingUser); + + var user = new User(); + user.instagram = profile.id; + user.tokens.push({ kind: 'instagram', accessToken: accessToken }); + user.profile.name = profile.displayName; + user.email = ''; + user.profile.website = profile._json.data.website; + user.profile.picture = profile._json.data.profile_picture; + user.save(function(err) { + done(err, user); + }); + }); + } + })); + ***/ + }); + + var instagramButton = M(function() { + /*** + a.btn.btn-block.btn-instagram.btn-social(href='/auth/instagram') + i.fa.fa-instagram + | Sign in with Instagram + ***/ + }); + var instagramLinkUnlink = M(function() { + /*** + if user.instagram + p: a.text-danger(href='/account/unlink/instagram') Unlink your Instagram account + else + p: a(href='/auth/linkedin') Link your Instagram account + ***/ + }); + var instagramModel = ' instagram: String,'; + + if (passportConfig.indexOf(instagramStrategyRequire) < 0) { + + // Add Instagram to passport.js + index = passportConfig.indexOf("var passport = require('passport');"); + passportConfig.splice(index + 1, 0, instagramStrategyRequire); + index = passportConfig.indexOf('passport.deserializeUser(function(id, done) {'); + passportConfig.splice(index + 6, 0, instagramStrategy); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // Add Instagram to login.jade + loginTemplate.push(instagramButton); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // Add Instagram to profile.jade + index = profileTemplate.indexOf(' h3 Linked Accounts'); + profileTemplate.splice(index + 1, 0, instagramLinkUnlink); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // Add Instagram to User.js + index = userModel.indexOf(' tokens: Array,'); + userModel.splice(index - 1, 0, instagramModel); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + // Add Instagram to app.js + index = app.indexOf(' * OAuth routes for sign-in.'); + app.splice(index + 2, 0, instagramRoutes); + fs.writeFileSync(appFile, app.join('\n')); + + // Add Instagram to secrets.js + index = secrets.indexOf('module.exports = {'); + secrets.splice(index + 1, 0, instagramSecrets); + fs.writeFileSync(secretsFile, secrets.join('\n')); + + var exec = require('child_process').exec; + child = exec('npm install passport-instagram').stderr.pipe(process.stderr); + + console.log('✓ Instagram authentication has been added.'.info); + } else { + console.log('✓ Instagram authentication is already active.'.warn); + } + } else { + + // Remove Instagram from passport.js + index = passportConfig.indexOf(instagramStrategyRequire); + passportConfig.splice(index, 1); + index = passportConfig.indexOf('// Sign in with Instagram.'); + passportConfig.splice(index, 40); + fs.writeFileSync(passportConfigFile, passportConfig.join('\n')); + + // Remove Instagram from login.jade + index = loginTemplate.indexOf(" a.btn.btn-block.btn-instagram.btn-social(href='/auth/instagram')"); + loginTemplate.splice(index, 4); + fs.writeFileSync(loginTemplateFile, loginTemplate.join('\n')); + + // Remove Instagram from profile.jade + index = profileTemplate.indexOf(' if user.instagram'); + profileTemplate.splice(index - 1, 5); + fs.writeFileSync(profileTemplateFile, profileTemplate.join('\n')); + + // Remove Instagram from User.js + index = userModel.indexOf(' instagram: String,'); + userModel.splice(index, 1); + fs.writeFileSync(userModelFile, userModel.join('\n')); + + console.log('✗ Instagram authentication has been removed.'.error); + } }); } }); \ No newline at end of file From 7ffe7efce97993a9e952bbec1ada8162581754bf Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Mon, 14 Apr 2014 02:39:31 -0400 Subject: [PATCH 22/23] Minor adjustments to instagram generator, changed log color of already active auth methods to grey. (from yellow) --- generator.js | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/generator.js b/generator.js index 6fd6aa22ab..159fcad537 100644 --- a/generator.js +++ b/generator.js @@ -32,10 +32,10 @@ inquirer.prompt({ new inquirer.Separator(M(function() { /*** - ╔═════════════════════════════════════════════════════════════════╗ - ║ THIS TOOL IS STILL IN EXPERIMENTAL STAGE! USE AT YOUR OWN RISK. ║ - ║ ALWAYS USE VERSION CONTROL SYSTEM SO YOU COULD REVERT CHANGES. ║ - ╚═════════════════════════════════════════════════════════════════╝ + ╔═════════════════════════════════════════════════════════════════════╗ + ║ THIS TOOL IS STILL IN EXPERIMENTAL STAGE! USE AT YOUR OWN RISK. ║ + ║ ALWAYS USE VERSION CONTROL SYSTEM SO YOU COULD REVERT THE CHANGES. ║ + ╚═════════════════════════════════════════════════════════════════════╝ ***/ })), @@ -46,7 +46,7 @@ inquirer.prompt({ { name: 'Local', checked: true }, { name: 'LinkedIn', checked: true }, { name: 'Instagram' }, - new inquirer.Separator('Press ctrl+ to quit'), + new inquirer.Separator('Press ctrl+ to quit') ], validate: function(answer) { if (answer.length < 1) return 'You must choose at least one authentication provider.'; @@ -165,7 +165,7 @@ inquirer.prompt({ console.log('✓ Facebook authentication has been added.'.info); } else { - console.log('✓ Facebook authentication is already active.'.warn); + console.log('✓ Facebook authentication is already active.'.data); } } else { @@ -292,7 +292,7 @@ inquirer.prompt({ console.log('✓ GitHub authentication has been added.'.info); } else { - console.log('✓ GitHub authentication is already active.'.warn); + console.log('✓ GitHub authentication is already active.'.data); } } else { @@ -417,7 +417,7 @@ inquirer.prompt({ console.log('✓ Google authentication has been added.'.info); } else { - console.log('✓ Google authentication is already active.'.warn); + console.log('✓ Google authentication is already active.'.data); } } else { @@ -538,7 +538,7 @@ inquirer.prompt({ console.log('✓ Twitter authentication has been added.'.info); } else { - console.log('✓ Twitter authentication is already active.'.warn); + console.log('✓ Twitter authentication is already active.'.data); } } else { @@ -667,7 +667,7 @@ inquirer.prompt({ console.log('✓ LinkedIn authentication has been added.'.info); } else { - console.log('✓ LinkedIn authentication is already active.'.warn); + console.log('✓ LinkedIn authentication is already active.'.data); } } else { @@ -796,7 +796,7 @@ inquirer.prompt({ console.log('✓ LinkedIn authentication has been added.'.info); } else { - console.log('✓ LinkedIn authentication is already active.'.warn); + console.log('✓ LinkedIn authentication is already active.'.data); } } else { @@ -918,7 +918,7 @@ inquirer.prompt({ console.log('✓ Local authentication has been added.'.info); } else { - console.log('✓ Local authentication is already active.'.warn); + console.log('✓ Local authentication is already active.'.data); } } else { @@ -1009,6 +1009,7 @@ inquirer.prompt({ }); } })); + ***/ }); @@ -1062,12 +1063,9 @@ inquirer.prompt({ secrets.splice(index + 1, 0, instagramSecrets); fs.writeFileSync(secretsFile, secrets.join('\n')); - var exec = require('child_process').exec; - child = exec('npm install passport-instagram').stderr.pipe(process.stderr); - console.log('✓ Instagram authentication has been added.'.info); } else { - console.log('✓ Instagram authentication is already active.'.warn); + console.log('✓ Instagram authentication is already active.'.data); } } else { From 92dc9526f5e92e7a9cd5dce13c44d9fda8e8e369 Mon Sep 17 00:00:00 2001 From: Sahat Yalkabov Date: Mon, 14 Apr 2014 02:44:21 -0400 Subject: [PATCH 23/23] Instagram generator indentation and whitespace fixes --- generator.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/generator.js b/generator.js index 159fcad537..7e0f6104df 100644 --- a/generator.js +++ b/generator.js @@ -1015,17 +1015,18 @@ inquirer.prompt({ var instagramButton = M(function() { /*** - a.btn.btn-block.btn-instagram.btn-social(href='/auth/instagram') - i.fa.fa-instagram - | Sign in with Instagram + a.btn.btn-block.btn-instagram.btn-social(href='/auth/instagram') + i.fa.fa-instagram + | Sign in with Instagram ***/ }); var instagramLinkUnlink = M(function() { /*** - if user.instagram - p: a.text-danger(href='/account/unlink/instagram') Unlink your Instagram account - else - p: a(href='/auth/linkedin') Link your Instagram account + + if user.instagram + p: a.text-danger(href='/account/unlink/instagram') Unlink your Instagram account + else + p: a(href='/auth/linkedin') Link your Instagram account ***/ }); var instagramModel = ' instagram: String,'; @@ -1055,7 +1056,7 @@ inquirer.prompt({ // Add Instagram to app.js index = app.indexOf(' * OAuth routes for sign-in.'); - app.splice(index + 2, 0, instagramRoutes); + app.splice(index + 3, 0, instagramRoutes); fs.writeFileSync(appFile, app.join('\n')); // Add Instagram to secrets.js