Skip to content

Commit

Permalink
ch(improve): improve social login implementation
Browse files Browse the repository at this point in the history
- edit login by google implementation
- edit login by facebook implementation
- add test
- [Finish #170476231]
  • Loading branch information
NiyongaboEric committed Dec 31, 2019
1 parent 1aaa309 commit c73c1b7
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 125 deletions.
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"ejs": "^2.6.1",
"email-templates": "^6.0.3",
"errorhandler": "^1.5.0",
"eslint-plugin-import": "^2.18.2",
"express": "^4.17.1",
"express-jwt": "^5.3.1",
"express-session": "^1.15.6",
Expand Down Expand Up @@ -67,7 +66,6 @@
"@babel/core": "^7.7.2",
"@babel/node": "^7.7.0",
"@babel/plugin-transform-runtime": "^7.6.2",
"@babel/polyfill": "^7.7.0",
"@babel/preset-env": "^7.7.1",
"@babel/register": "^7.7.0",
"@babel/runtime": "^7.7.2",
Expand Down
36 changes: 7 additions & 29 deletions src/controllers/UserController.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import NotificationService from '../services/NotificationService';
const { hashPassword } = HashPassword;
const { getAUser } = UserService;
dotenv.config();

/**
* @exports
* @class UserController
Expand Down Expand Up @@ -81,39 +82,15 @@ class UserController {
return Response.successMessage(req, res, 'Successfuly login', token, 200);
}

/**
* User can be able to sign up
* @param {object} accessToken response
* @param {object} refreshToken response
* @param {object} profile objet
* @param {object} done callback
* @param {string} type signupType
* @returns {object} object
*/
static async facebookCallBack(accessToken, refreshToken, profile, done) {
UserHelper.socialCallBack(accessToken, refreshToken, profile, 'facebook', done);
}

/**
* User can be able to sign up
* @param {object} accessToken response
* @param {object} refreshToken response
* @param {object} profile objet
* @param {object} done callback
* @returns {object} object
*/
static async googleCallBack(accessToken, refreshToken, profile, done) {
UserHelper.socialCallBack(accessToken, refreshToken, profile, 'google', done);
}

/**
* User can be able to sign up
* @param {object} req request object
* @param {object} res response object
* @returns {object} object
*/
static OAuthfacebook(req, res) {
UserHelper.OAuthSocial(req, res, 'Logged in with facebook successfully');
static async OAuthfacebook(req, res) {
const result = await UserHelper.socialCallBack(req, res, 'facebook');
UserHelper.OAuthSocial(req, res, result, 'Logged in with facebook successfully');
}

/**
Expand All @@ -122,8 +99,9 @@ class UserController {
* @param {object} res response object
* @returns {object} object
*/
static OAuthgoogle(req, res) {
UserHelper.OAuthSocial(req, res, 'Logged in with google successfully');
static async OAuthgoogle(req, res) {
const result = await UserHelper.socialCallBack(req, res, 'google');
UserHelper.OAuthSocial(req, res, result, 'Logged in with google successfully');
}

/**
Expand Down
37 changes: 14 additions & 23 deletions src/helpers/UserHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,56 +25,47 @@ class UserHelper {

/**
* User can be able to via social media
* @param {object} accessToken response
* @param {object} refreshToken response
* @param {object} profile objet
* @param {object} req objet
* @param {object} res objet
* @param {object} signupTypeParameter string
* @param {object} done callback
* @returns {object} object
*/
static async socialCallBack(accessToken, refreshToken, profile, signupTypeParameter, done) {
static async socialCallBack(req, res, signupTypeParameter) {
try {
const data = {
id: profile.id,
firstName: profile.name.givenName,
lastName: profile.name.familyName,
email: profile.emails[0].value,
signupType: signupTypeParameter,
isVerified: true
};
const {
email, firstName, lastName, signupType, isVerified
} = data;
firstName, lastName, email
} = req.body;

users.findOrCreate({
const socialAuthUser = await users.findOrCreate({
where: { email },
defaults: {
firstName,
lastName,
email,
password: null,
signupType,
isVerified
signupType: signupTypeParameter,
isVerified: true
}
});
done(null, data);
return socialAuthUser[0].dataValues;
} catch (error) {
done(error, false, error.message);
return Response.errorMessage(req, res, error.message, 500);
}
}

/**
* oauth social
* @param {object} req request object
* @param {object} res response object
* @param {object} result response object
* @param {object} successSocialSignUp success message
* @returns {object} object
*/
static OAuthSocial(req, res, successSocialSignUp) {
const token = AuthenticateToken.signToken(req.user);
static OAuthSocial(req, res, result, successSocialSignUp) {
const token = AuthenticateToken.signToken(result);
const {
id, email, firstName, lastName, signupType, isVerified
} = req.user;
} = result;
const data = {
id, email, firstName, lastName, signupType, isVerified, token
};
Expand Down
14 changes: 14 additions & 0 deletions src/middlewares/Validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,5 +358,19 @@ class Validate {
check('notificationIds.*', 'The notification IDs should be integer').isInt()
];
}

/**
* Validate Social login info
* @static
* @returns {object} errors
*/
static validateSocialLogin() {
return [
check('id', 'User Id is required and number').isInt(),
check('email', 'Email should be valid').isEmail(),
check('lastName', 'Last name is required and string').isString(),
check('firstName', 'First name is required and string').isString()
];
}
}
export default Validate;
14 changes: 4 additions & 10 deletions src/routes/api/socialRoute.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import passport from 'passport';
import { Router } from 'express';
import '../../services/passport';
import socialErrorHandler from '../../middlewares/socialErrorHandler';
import UserController from '../../controllers/UserController';
import Validate from '../../middlewares/Validate';
import checkInputDataError from '../../middlewares/checkInputDataError';

const router = Router();
router.use(passport.initialize());
router.use(passport.session());
passport.serializeUser((user, done) => {
done(null, user);
});

/**
* @swagger
Expand Down Expand Up @@ -81,6 +75,6 @@ passport.serializeUser((user, done) => {
*/


router.post('/facebook', passport.authenticate('facebook-token'), socialErrorHandler, UserController.OAuthfacebook);
router.post('/google', passport.authenticate('google-plus-token'), socialErrorHandler, UserController.OAuthgoogle);
router.post('/facebook', Validate.validateSocialLogin(), checkInputDataError, UserController.OAuthfacebook);
router.post('/google', Validate.validateSocialLogin(), checkInputDataError, UserController.OAuthgoogle);
module.exports = router;
28 changes: 14 additions & 14 deletions src/services/passport.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import passport from 'passport';
import FacebookTokenStrategy from 'passport-facebook-token';
import GooglePlusTokenStrategy from 'passport-google-plus-token';
import dotenv from 'dotenv';
import User from '../controllers/UserController';
// import passport from 'passport';
// import FacebookTokenStrategy from 'passport-facebook-token';
// import GooglePlusTokenStrategy from 'passport-google-plus-token';
// import dotenv from 'dotenv';
// import User from '../controllers/UserController';

dotenv.config();
passport.use('facebook-token', new FacebookTokenStrategy({
clientID: process.env.FACEBOOK_APP_ID,
clientSecret: process.env.FACEBOOK_APP_SECRET
}, User.facebookCallBack));
// dotenv.config();
// passport.use('facebook-token', new FacebookTokenStrategy({
// clientID: process.env.FACEBOOK_APP_ID,
// clientSecret: process.env.FACEBOOK_APP_SECRET
// }, User.facebookCallBack));

passport.use(new GooglePlusTokenStrategy({
clientID: process.env.GOOGLE_APP_ID,
clientSecret: process.env.GOOGLE_APP_SECRET
}, User.googleCallBack));
// passport.use(new GooglePlusTokenStrategy({
// clientID: process.env.GOOGLE_APP_ID,
// clientSecret: process.env.GOOGLE_APP_SECRET
// }, User.googleCallBack));
56 changes: 11 additions & 45 deletions src/tests/020-socialControllers.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,34 @@
import chaiHttp from 'chai-http';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { describe, it } from 'mocha';
import profile from './mock/googleProfile';
import UserController from '../controllers/UserController';
import chai from 'chai';
import app from '../index';
import mockData from './mock/mockData';


const chai = require('chai');

const { socialUser, socialToken } = mockData;
const { facebook_feke_user, google_feke_user } = mockData;

chai.use(chaiHttp);
chai.use(sinonChai);
const { expect } = chai;


describe('Oauthentication CallBack', () => {
afterEach(() => {
sinon.restore();
});


it('Should return status 200', async () => {
const req = {
user: {
socialUser
}
};

const res = { status() { }, json() { }, };
sinon.stub(res, 'status').returnsThis();
await UserController.OAuthgoogle(req, res);
expect(res.status).to.have.been.calledWith(200);
});
it('Should not signin with google a user with wrong token', (done) => {
describe('Social login test', () => {
it('Expect to signin with google account', (done) => {
chai.request(app)
.post('/api/v1/auth/google')
.send(socialToken)
.send(google_feke_user)
.end((err, res) => {
expect(res.status).eql(400);
expect(res.body.message).eql('Authentication error');
expect(res.status).eql(200);
expect(res.body.message).eql('Logged in with google successfully');
done(err);
});
});
it('Should not signin with facebook a user with wrong token', (done) => {
it('Expect to signin with facebook account', (done) => {
chai.request(app)
.post('/api/v1/auth/facebook')
.send(socialToken)
.send(facebook_feke_user)
.end((err, res) => {
expect(res.status).eql(400);
expect(res.body.message).eql('Authentication error');
expect(res.status).eql(200);
expect(res.body.message).eql('Logged in with facebook successfully');
done(err);
});
});
it('OAuthCallback should return User object', async (done) => {
const accessToken = 'xx-xx-xx';
const refreshToken = 'xx-xx-xx';
const cb = sinon.spy();
UserController.googleCallBack(accessToken, refreshToken, profile, cb);
expect(cb.withArgs({
socialUser
}));
done();
});
});
6 changes: 4 additions & 2 deletions src/tests/090-accommodationTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import chai from 'chai';
import app from '../index';
import mockData from './mock/mockData';
import accommodationMockData from './mock/accommodationMockData';
import jwt from 'jsonwebtoken';

const { expect } = chai;
const fakeToken = 'hhhhbjhvjhfgjfjfjfjfgjhvjjgjhjhjgjgj';
let TripAdminToken, superAdminToken, TripAdminSignupToken, unVerifiedTripAdminToken, userToken;
let TripAdminToken, superAdminToken, TripAdminSignupToken, unVerifiedTripAdminToken, userToken, decode;
const {
users,
usersSignin,
Expand Down Expand Up @@ -203,11 +204,12 @@ describe('Trip Administartor should be able to create accomodation facilities',
.send(users)
.end((err, res) => {
TripAdminSignupToken = res.body.data;
decode = jwt.verify(TripAdminSignupToken, process.env.JWT_KEY);
done(err);
});
});
before((done) => {
chai.request(app).get(`/api/v1/auth/verify-email/13/${TripAdminSignupToken}`).end((err, res) => {
chai.request(app).get(`/api/v1/auth/verify-email/${decode.id}/${TripAdminSignupToken}`).end((err, res) => {
res.should.have.status(200);
res.body.should.be.an('object');
done();
Expand Down
12 changes: 12 additions & 0 deletions src/tests/mock/mockData.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,18 @@ const mockData = {
},
approveRequest: {
reason: 'yes we agree with your reason'
},
facebook_feke_user: {
id: 10,
email: 'feke@facebook.domain',
firstName: 'Patrick',
lastName: 'john',
},
google_feke_user: {
id: 10,
email: 'feke@google.domain',
firstName: 'Patrick',
lastName: 'john',
}
};

Expand Down

0 comments on commit c73c1b7

Please sign in to comment.