Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[166240824] Enable Social Login via Google and Facebook #19

Merged
merged 6 commits into from
Jun 25, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
MemunaHaruna marked this conversation as resolved.
Show resolved Hide resolved

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ramadhan0 this file shouldn't be in your PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, me and Premise we were switching between .eslintrc.json and .eslintrc.js in order to ensure how those files were working.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, let's undo the changes here since it's not related to your PR.

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;

/**
*
Ramadhan0 marked this conversation as resolved.
Show resolved Hide resolved
*
* @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,
MemunaHaruna marked this conversation as resolved.
Show resolved Hide resolved
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,
MemunaHaruna marked this conversation as resolved.
Show resolved Hide resolved
uniqueId: req.user.id
});
const token = generateToken({ username: req.user.displayName, id: req.user.id },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here it is nor the id from the req.user but from the table's dataValues

process.env.TOKEN_KEY);
return res.status(status.CREATED).json({ user: { ...existingUser.get(), token } });
}
const token = generateToken({
Ramadhan0 marked this conversation as resolved.
Show resolved Hide resolved
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 },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same here

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: {
Ramadhan0 marked this conversation as resolved.
Show resolved Hide resolved
type: DataTypes.STRING
type: DataTypes.STRING,
allowNull: true
},
email: {
Ramadhan0 marked this conversation as resolved.
Show resolved Hide resolved
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