Skip to content

Commit

Permalink
[ft-#166240824] Enable Social Login via Google, Facebook and twitter
Browse files Browse the repository at this point in the history
  • Loading branch information
Ramadhan authored and Ramadhan0 committed Jun 20, 2019
1 parent f5a11d0 commit 83334cd
Show file tree
Hide file tree
Showing 15 changed files with 486 additions and 93 deletions.
19 changes: 17 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ TOKEN_KEY=TheTokenKey
MAILER_API_KEY=theTokenOfTheMailerService
MAILER_EMAIL=theEmailUsedByTheMailerAsSender
API_BASE_URL=theApiBaseUrlToInsertInTheLinksSharedInEmails
TOKEN_KEY=TheTokenKey

BCRYPT_HASH_ROUNDS=theBcryptSaltingRounds



FACEBOOK_CLIENT_ID=theFacebookAppId
FACEBOOK_CLIENT_SECRET=theFacebookAppSecret
FACEBOOK_CALLBACK=http://www.example.com/users/facebook/callback

GOOGLE_CLIENT_ID=theGoogleAppId
GOOGLE_CLIENT_SECRET=theGoogleAppSecret
GOOGLE_CALLBACK=http://www.example.com/users/google/callback


TWITTER_CLIENT_ID=theTwitterClientId
TWITTER_CONSUMER_SECRET=theTwitterConsumerSecret
TWITTER_CALLBACK=http://www.example.com/users/twitter/callback

TWITTER_SESSION_KEY=theTwitterSessionKey
6 changes: 3 additions & 3 deletions .hound.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
fail_on_violations: true
eslint:
enabled: true
config_file: .eslintrc.json
ignore_file: .eslintignore
enabled: true
config_file: .eslintrc.json
ignore_file: .eslintignore
146 changes: 77 additions & 69 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,71 +1,79 @@
{
"name": "express-authorshaven",
"version": "1.0.0",
"description": "A Social platform for the creative at heart",
"main": "index.js",
"scripts": {
"start": "babel-node src/index.js",
"test": "NODE_ENV=test nyc --reporter=html --reporter=text --reporter=lcov ./node_modules/.bin/mocha ./tests/* --require @babel/register --timeout 4000 --exit",
"dev": "NODE_ENV=development nodemon --exec babel-node src/index.js",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"eslint": "eslint . --cache --fix",
"precommit": "lint-staged"
},
"lint-staged": {
"*.js": [
"eslint --cache --fix",
"git add"
]
},
"devDependencies": {
"chai": "^4.2.0",
"chai-http": "^4.3.0",
"coveralls": "^3.0.3",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.17.3",
"husky": "^2.4.1",
"lint-staged": "^8.2.1",
"mocha": "^6.1.4",
"nodemon": "^1.19.1",
"nyc": "^14.1.1"
},
"dependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.5",
"@babel/node": "^7.4.5",
"@babel/polyfill": "^7.4.4",
"@babel/preset-env": "^7.4.5",
"@babel/register": "^7.4.4",
"@hapi/joi": "^15.0.3",
"@sendgrid/mail": "^6.4.0",
"babel-plugin-istanbul": "^5.1.2",
"bcrypt": "^3.0.6",
"body-parser": "^1.18.3",
"cors": "^2.8.5",
"dotenv": "^8.0.0",
"expect": "^24.8.0",
"express": "^4.16.4",
"joi": "^14.3.1",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.11",
"morgan": "^1.9.1",
"pg": "^7.11.0",
"sequelize": "^5.8.7",
"swagger-node-express": "^2.1.3",
"swagger-ui-express": "^4.0.6"
},
"nyc": {
"per-file": true,
"check-coverage": true,
"branches": 80,
"lines": 80,
"functions": 100,
"statements": 80,
"exclude": [
"src/index.js"
]
},
"author": "Andela Simulations Programme",
"license": "MIT"
"name": "express-authorshaven",
"version": "1.0.0",
"description": "A Social platform for the creative at heart",
"main": "index.js",
"scripts": {
"start": "babel-node src/index.js",
"test": "NODE_ENV=test nyc --reporter=html --reporter=text --reporter=lcov ./node_modules/.bin/mocha ./tests/* --require @babel/register --timeout 4000 --exit",
"dev": "NODE_ENV=development nodemon --exec babel-node src/index.js",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"eslint": "eslint . --cache --fix",
"precommit": "lint-staged"
},
"lint-staged": {
"*.js": [
"eslint --cache --fix",
"git add"
]
},
"devDependencies": {
"chai": "^4.2.0",
"chai-http": "^4.3.0",
"coveralls": "^3.0.3",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.17.3",
"husky": "^2.4.1",
"lint-staged": "^8.2.1",
"mocha": "^6.1.4",
"nodemon": "^1.19.1",
"nyc": "^14.1.1"
},
"dependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.5",
"@babel/node": "^7.4.5",
"@babel/polyfill": "^7.4.4",
"@babel/preset-env": "^7.4.5",
"@babel/register": "^7.4.4",
"@hapi/joi": "^15.0.3",
"@sendgrid/mail": "^6.4.0",
"babel-plugin-istanbul": "^5.1.2",
"bcrypt": "^3.0.6",
"body-parser": "^1.18.3",
"cors": "^2.8.5",
"dotenv": "^8.0.0",
"expect": "^24.8.0",
"express": "^4.16.4",
"express-session": "^1.16.2",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.11",
"morgan": "^1.9.1",
"passport": "^0.4.0",
"passport-facebook": "^3.0.0",
"passport-google-oauth": "^2.0.0",
"passport-pinterest": "^1.0.0",
"passport-twitter": "^1.0.4",
"pg": "^7.11.0",
"sequelize": "^5.8.7",
"sinon": "^7.3.2",
"sinon-chai": "^3.3.0",
"swagger-node-express": "^2.1.3",
"swagger-ui-express": "^4.0.6",
"joi": "^14.3.1"
},
"nyc": {
"per-file": true,
"check-coverage": true,
"branches": 80,
"lines": 80,
"functions": 100,
"statements": 80,
"exclude": [
"src/configs/environments.js"
]
},
"author": "Andela Simulations Programme",
"license": "MIT"
}
2 changes: 1 addition & 1 deletion src/api/controllers/authController.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import bcrypt, { hashSync, genSaltSync } from 'bcrypt';
import models from '../models/index';
import models from '../models';
import generateToken from '../../helpers/tokens/generate.token';
import sendResult from '../../helpers/results/send.auth';
import status from '../../helpers/constants/status.codes';
Expand Down
117 changes: 117 additions & 0 deletions src/api/controllers/socialAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import module from '../models';
import generateToken from '../../helpers/tokens/generate.token';
import status from '../../helpers/constants/status.codes';

const { User } = module;

/**
*
*
* @class socialLogin
*/
class socialLogin {
/**
* signup || login via facebook
*
* @static
* @param {object} req - request object
* @param {object} res - response object
* @memberof socialLogin
* @returns {object} - the response body
*
*/
static async facebookAuth(req, res) {
let existingUser = await User.findOne({
where: {
uniqueId: req.user.id
}
});
if (!existingUser) {
existingUser = await User.create({
username: req.user.name.familyName,
profileImage: req.user.photos[0].value,
provider: req.user.provider,
uniqueId: req.user.id
});
}
const token = generateToken({
id: existingUser.dataValues.id,
username: existingUser.dataValues.username
},
process.env.TOKEN_KEY);
return res.status(status.CREATED).json({ user: { ...existingUser.get(), token } });
}

/**
* signup || login via Google
*
* @static
* @param {object} req - request object
* @param {object} res - request response
* @memberof socialLogin
* @returns {object} - the response body
*
*/
static async googleAuth(req, res) {
let existingUser = await User.findOne({
where: {
uniqueId: req.user.id
}
});
if (!existingUser) {
existingUser = User.create({
username: req.user.displayName,
email: req.user.emails[0].value,
profileImage: req.user.photos[0].value,
provider: req.user.provider,
uniqueId: req.user.id
});
const token = generateToken({ username: req.user.displayName, id: req.user.id },
process.env.TOKEN_KEY);
return res.status(status.CREATED).json({ user: { ...existingUser.get(), token } });
}
const token = generateToken({
id: existingUser.dataValues.id,
username: existingUser.dataValues.username
},
process.env.TOKEN_KEY);
return res.status(status.CREATED).json({ user: { ...existingUser.get(), token } });
}

/**
* signup || login user via Twitter
*
* @static
* @param {object} req - request object
* @param {object} res - request response
* @memberof socialLogin
* @returns {object} - the response body
*
*/
static async twitterAuth(req, res) {
let existingUser = await User.findOne({
where: {
uniqueId: req.user.id
}
});
if (!existingUser) {
existingUser = User.create({
username: req.user.username,
profileImage: req.user.photos[0].value,
provider: req.user.provider,
uniqueId: req.user.id
});
const token = generateToken({ username: req.user.displayName, id: req.user.id },
process.env.TOKEN_KEY);
return res.status(status.CREATED).json({ user: { ...existingUser.get(), token } });
}
const token = generateToken({
id: existingUser.dataValues.id,
username: existingUser.dataValues.username
},
process.env.TOKEN_KEY);
return res.status(status.CREATED).json({ user: { ...existingUser.get(), token } });
}
}

export default socialLogin;
22 changes: 18 additions & 4 deletions src/api/models/user.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
const user = (sequelize, DataTypes) => {
const User = sequelize.define('Users', {
username: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: true
},
email: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: true
},
password: {
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: true
},
profileImage: {
type: DataTypes.STRING,
allowNull: true
},
provider: {
type: DataTypes.STRING,
allowNull: true
},
uniqueId: {
type: DataTypes.STRING,
allowNull: true
},
verified: {
type: DataTypes.BOOLEAN,
Expand All @@ -22,7 +37,6 @@ const user = (sequelize, DataTypes) => {

return queryResult;
};

return User;
};

Expand Down
22 changes: 22 additions & 0 deletions src/api/routes/authRouter.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
import { Router } from 'express';
import ValidateUser from '../../middlewares/ValidateUser';
import authController from '../controllers/authController';
import socialAuthController from '../controllers/socialAuth';
import env from '../../configs/environments';
import social from '../../middlewares/social/social';

const authRouter = new Router();

authRouter.post('/signup', ValidateUser.validateSignup, authController.signup);
authRouter.post('/login', authController.login);

authRouter.post('/mockFacebook', social, socialAuthController.facebookAuth);
authRouter.post('/mockGoogle', social, socialAuthController.googleAuth);
authRouter.post('/mockTwitter', social, socialAuthController.twitterAuth);

authRouter.get('/facebook', env.passport.authenticate('facebook'));
authRouter.get('/facebook/callback',
env.passport.authenticate('facebook'),
socialAuthController.facebookAuth);

authRouter.get('/google', env.passport.authenticate('google', { scope: ['email', 'profile'] }));
authRouter.get('/google/callback',
env.passport.authenticate('google'),
socialAuthController.googleAuth);

authRouter.get('/twitter', env.passport.authenticate('twitter', { scope: ['email', 'profile'] }));
authRouter.get('/twitter/callback',
env.passport.authenticate('twitter'),
socialAuthController.twitterAuth);

export default authRouter;
Loading

0 comments on commit 83334cd

Please sign in to comment.