Skip to content

Commit

Permalink
feat: enable social login
Browse files Browse the repository at this point in the history
  • Loading branch information
Mireille Niwemuhuza authored and Mireille Niwemuhuza committed Jun 16, 2019
1 parent 9b39eea commit e2e39ba
Show file tree
Hide file tree
Showing 16 changed files with 434 additions and 56 deletions.
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ TEST_DATABASE_URL=
AUTHOSHAVEN_USER=
AUTHOSHAVEN_PASS=
BASE_URL=

FCBK_ID=
FCBK_APP_SECRET=

TWITTER_ID=
TWITTER_APP_SECRET=

GOOGLE_ID=
GOOGLE_SECRET=
SECRET_KEY =
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ node_js:
before_script:
- yarn add sequelize-cli
- sequelize db:migrate
- yarn destroy
- sequelize db:seed:all
cache: yarn
script:
Expand Down
10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"test:local": "yarn undo && yarn migrate && yarn seed && yarn test",
"migrate": "sequelize db:migrate",
"undo": "sequelize db:migrate:undo",
"seed": "sequelize db:seed:all"
"seed": "sequelize db:seed:all",
"destroy": "babel-node src/helpers/destroyUser"
},
"author": "Andela Simulations Programme",
"license": "MIT",
Expand All @@ -27,8 +28,9 @@
"errorhandler": "^1.5.0",
"express": "^4.16.3",
"express-jwt": "^5.3.1",
"express-session": "^1.15.6",
"express-session": "^1.16.2",
"handlebars": "^4.1.2",
"install": "^0.12.2",
"joi": "^14.3.1",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.11",
Expand All @@ -37,7 +39,11 @@
"morgan": "^1.9.1",
"nodemailer": "^6.2.1",
"passport": "^0.4.0",
"passport-auth0": "^1.1.0",
"passport-facebook": "^3.0.0",
"passport-google-oauth20": "^2.0.0",
"passport-local": "^1.0.0",
"passport-twitter": "^1.0.4",
"pg": "^7.11.0",
"pg-hstore": "^2.3.3",
"regenerator-runtime": "^0.13.2",
Expand Down
84 changes: 84 additions & 0 deletions src/api/controllers/socialLogin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import models from '../../sequelize/models';
import tokenGeneration from '../../helpers/Token.helper';

const userInfo = {
async googleLogin(req, res) {
const { displayName } = req.user;
const newUser = await models.User.create({
firstName: req.user.name.givenName,
lastName: req.user.name.familyName,
email: req.user.emails[0].value,
image: req.user.photos[0].value,
provider: req.user.provider,
verified: req.user.emails[0].verified,
socialId: req.user.id,
});
if (newUser) {
const {
id, firstName, lastName, email, provider
} = newUser.dataValues;
const token = await tokenGeneration.generateToken(newUser.dataValues);
return res.status(201).json({
status: 201,
message: `Welcome to Authors Haven ${displayName} `,
data: {
token, id, firstName, lastName, email, provider
},
});
}
},

async facebookLogin(req, res) {
const { displayName } = req.user;
const names = displayName.split(' ');
const newUser = await models.User.create({
firstName: names[0],
lastName: names[1],
email: req.user.emails[0].value,
image: req.user.photos[0].value,
provider: req.user.provider,
verified: true,
socialId: req.user.id,
});
if (newUser) {
const {
id, firstName, lastName, email, provider
} = newUser.dataValues;
const token = await tokenGeneration.generateToken(newUser.dataValues);
return res.status(201).json({
message: `Welcome to Authors Haven ${displayName} `,
data: {
token, id, firstName, lastName, email, provider
},
});
}
},
async twitterLogin(req, res) {
const {
displayName
} = req.user;
const names = displayName.split(' ');
const newUser = await models.User.create({
firstName: names[0],
lastName: names[1],
username: req.user.username,
image: req.user.photos[0].value,
provider: req.user.provider,
verified: true,
socialId: req.user.id,
});
if (newUser) {
const {
id, firstName, lastName, email, provider
} = newUser.dataValues;
const token = await tokenGeneration.generateToken(newUser.dataValues);
return res.status(201).json({
message: `Welcome to Authors Haven ${displayName} `,
data: {
token, id, firstName, lastName, email, provider
},
});
}
},
};
export default userInfo;
22 changes: 22 additions & 0 deletions src/api/routes/authRouter.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { Router } from 'express';
import passport from 'passport';
import authController from '../controllers/auth';
import validateBody from '../../middleware/validateBody';
import userValidation from '../../middleware/validUser';
import validateGender from '../../middleware/validateGender';
import Auth from '../../middleware/auth';
import dropToken from '../../middleware/droppedToken';
import socialLogin from '../controllers/socialLogin';
import socialAccount from '../../middleware/socialAccountExists';
import socialMiddleware from '../../middleware/socialTest';

const authRouter = Router();

Expand All @@ -19,12 +23,30 @@ const {
const { usernameExists, emailExists } = userValidation;
const { verifyToken } = Auth;

const { google, twitter } = socialAccount;

authRouter.get('/signout', verifyToken, dropToken, SignOut);

// social login test routes
authRouter.post('/login/google/test', socialMiddleware, google, socialLogin.googleLogin);
authRouter.post('/login/facebook/test', socialMiddleware, google, socialLogin.facebookLogin);
authRouter.post('/login/twitter/test', socialMiddleware, twitter, socialLogin.twitterLogin);

// social login

authRouter.get('/login/google', passport.authenticate('google', { scope: ['profile', 'email'], }));
authRouter.get('/login/google/redirect', passport.authenticate('google', { session: false }), google, socialLogin.googleLogin);

authRouter.get('/login/facebook', passport.authenticate('facebook', { scope: ['email'] }),);
authRouter.get('/login/facebook/redirect', passport.authenticate('facebook', { session: false }), google, socialLogin.facebookLogin);

authRouter.get('/login/twitter', passport.authenticate('twitter', { scope: ['profile', 'email'] }),);
authRouter.get('/login/twitter/redirect', passport.authenticate('twitter', { session: false }), twitter, socialLogin.twitterLogin);

authRouter.post('/signup', validateBody('signup'), validateGender, usernameExists, emailExists, register);
authRouter.get('/verify', verifyAccount);
authRouter.post('/reset', RequestPasswordReset);
authRouter.get('/reset/:token', ConfirmPasswordReset);
authRouter.patch('/reset/:aprvToken', ApplyPasswordReset);

export default authRouter;
1 change: 1 addition & 0 deletions src/api/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ api.use('/user', userRouter);
api.use('/profiles', profilesRouter);
api.use('/articles', articlesRouter);


export default api;
40 changes: 40 additions & 0 deletions src/config/passportSetup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import passport from 'passport';
import GoogleStrategy from 'passport-google-oauth20';
import FacebookStrategy from 'passport-facebook';
import TwitterTokenStrategy from 'passport-twitter';
import dotenv from 'dotenv';

dotenv.config();

passport.use(new GoogleStrategy(
{
clientID: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
includeEmail: true,
callbackURL: '/api/auth/login/google/redirect'
},
(accessToken, refreshToken, profile, done) => {
done(null, profile);
}
),);
passport.use(new FacebookStrategy(
{
clientID: process.env.FCBK_ID,
clientSecret: process.env.FCBK_APP_SECRET,
callbackURL: '/api/auth/login/facebook/redirect',
profileFields: ['id', 'displayName', 'photos', 'email']
},
(accessToken, refreshToken, profile, done) => {
done(null, profile);
}
),);
passport.use(new TwitterTokenStrategy(
{
consumerKey: process.env.TWITTER_ID,
consumerSecret: process.env.TWITTER_APP_SECRET,
callbackURL: 'http://localhost:3001/api/auth/login/twitter/redirect'
},
(token, tokenSecret, profile, done) => {
done(null, profile);
}
));
6 changes: 6 additions & 0 deletions src/helpers/destroyUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import models from '../sequelize/models';

models.User.destroy({
where: {},
truncate: false
});
11 changes: 11 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import 'regenerator-runtime';
import express from 'express';
import dotenv from 'dotenv';
import swaggerUi from 'swagger-ui-express';
import passport from 'passport';
import session from 'express-session';
import api from './api/routes/index';
import globalMiddleware from './middleware/globalMiddleware';
import './config/passportSetup';
import swaggerDoc from '../swagger.json';
import db from './sequelize/models/index';

Expand All @@ -15,8 +18,16 @@ const port = process.env.PORT || 3000;
const app = express();

globalMiddleware(app);

app.use(session({
secret: process.env.SECRET,
saveUninitialized: true
}));

app.use('/api', api);
app.use('/', swaggerUi.serve, swaggerUi.setup(swaggerDoc));
app.use(passport.initialize());
app.use(passport.session());


sequelize.sync().then(() => {
Expand Down
59 changes: 59 additions & 0 deletions src/middleware/socialAccountExists.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import models from '../sequelize/models';
import tokenGeneration from '../helpers/Token.helper';

const userExists = {
async google(req, res, next) {
const { emails, displayName } = req.user;
const currentUser = await models.User.findAll({
where: {
email: emails[0].value,
},
});
if (currentUser.length > 0) {
const token = await tokenGeneration.generateToken(currentUser[0].dataValues);
const {
id, firstName, lastName, email, socialId, provider
} = currentUser[0].dataValues;
return res.status(200).json({
message: `Welcome to Authors Haven ${displayName} `,
data: {
token,
id,
firstName,
lastName,
email,
socialId,
provider
},
});
}
next();
},
async twitter(req, res, next) {
const { displayName } = req.user;
const currentUser = await models.User.findAll({
where: {
socialId: req.user.id,
},
});
if (currentUser.length > 0) {
const token = await tokenGeneration.generateToken(currentUser[0].dataValues);
const {
id, firstName, lastName, socialId, provider
} = currentUser[0].dataValues;
return res.status(200).json({
message: `Welcome to Authors Haven ${displayName} `,
data: {
token,
id,
firstName,
lastName,
socialId,
provider,
},
});
}
next();
},
};
export default userExists;
16 changes: 16 additions & 0 deletions src/middleware/socialTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default (req, res, next) => {
req.user = {
id: req.body.id,
displayName: 'Mireille Niwemuhuza',
name: { familyName: 'Niwemuhuza', givenName: 'Mireille' },
emails:
[{ value: req.body.email, verified: true }],
photos:
[{
value:
'https://lh4.googleusercontent.com/-FZSypt5JyRU/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3repeJYC3C7JQWReWg7zqfLcOxh0Qg/mo/photo.jpg'
}],
provider: 'google'
};
next();
};
23 changes: 0 additions & 23 deletions src/middleware/userExists.js

This file was deleted.

23 changes: 0 additions & 23 deletions src/middleware/usernameExists.js

This file was deleted.

Loading

0 comments on commit e2e39ba

Please sign in to comment.