Skip to content

Commit

Permalink
ch(login): logout user
Browse files Browse the repository at this point in the history
- add route
- add controller
- update migration
- save token on signup
- save token on social login
- save token on signin
- [Finishes #170650327]
  • Loading branch information
NiyongaboEric committed Jan 15, 2020
1 parent 3f958d9 commit bc2d471
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 21 deletions.
26 changes: 19 additions & 7 deletions src/controllers/UserController.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@ class UserController {
try {
const { firstName, lastName, email } = req.body;
const newUser = await UserService.signUp(req, firstName, lastName, email);
const { password, ...data } = newUser.dataValues;
const token = AuthenticateToken.signToken(data);
data.token = token;
const { password, token, ...data } = newUser.dataValues;
const newToken = AuthenticateToken.signToken(data);
emailHelper.verifyEmailHelper(req, newUser.dataValues);
return Response.successMessage(req, res, 'User created successfully, Please check your email', token, 201);
return Response.successMessage(req, res, 'User created successfully, Please check your email', newToken, 201);
} catch (err) {
return Response.errorMessage(req, res, 'Server Error', 500);
}
Expand Down Expand Up @@ -76,9 +75,9 @@ class UserController {
*/
static async signin(req, res) {
const { result } = req;
const { password: pwd, ...data } = result.dataValues;
const token = AuthenticateToken.signToken(data);
return Response.successMessage(req, res, 'Successfuly login', token, 200);
const { password: pwd, token, ...data } = result.dataValues;
const newToken = AuthenticateToken.signToken(data);
return Response.successMessage(req, res, 'Successfuly login', newToken, 200);
}

/**
Expand Down Expand Up @@ -220,6 +219,19 @@ class UserController {
const notification = await NotificationService.viewNotification(req);
return Response.successMessage(req, res, 'Available notification', notification, HttpStatus.OK);
}

/**
* User can Logout
* @description PATCH /api/v1/users/logout
* @static
* @param {object} req request object
* @param {object} res response object
* @returns {object} Logout
*/
static async logout(req, res) {
await UserService.saveUserToken(req.user.token);
return Response.successMessage(req, res, 'Logout successfully', undefined, HttpStatus.OK);
}
}

export default UserController;
11 changes: 11 additions & 0 deletions src/database/migrations/20200110164415-add-user-token.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn('users', 'token', {
type: Sequelize.TEXT
})
},
down: (queryInterface, Sequelize) => {
return queryInterface.removeColumn('users', 'token')
}
};
27 changes: 27 additions & 0 deletions src/database/migrations/20200114223019-create-blacklist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('blacklists', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
token: {
type: Sequelize.TEXT
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('blacklists');
}
};
10 changes: 10 additions & 0 deletions src/database/models/blacklist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const blacklist = sequelize.define('blacklist', {
token: DataTypes.TEXT
}, {});
blacklist.associate = function(models) {
// associations can be defined here
};
return blacklist;
};
3 changes: 2 additions & 1 deletion src/database/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ module.exports = (sequelize, DataTypes) => {
password: DataTypes.STRING,
isVerified: DataTypes.BOOLEAN,
signupType: DataTypes.STRING,
roleId: DataTypes.INTEGER
roleId: DataTypes.INTEGER,
token: DataTypes.TEXT
}, {});
users.associate = function(models) {
users.hasMany(models.tripRequests,
Expand Down
18 changes: 6 additions & 12 deletions src/helpers/UserHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import dotenv from 'dotenv';
import Response from './Response';
import { users } from '../database/models';
import AuthenticateToken from './AuthenticateToken';
import UserService from '../services/UserService';

dotenv.config();

Expand Down Expand Up @@ -57,8 +58,9 @@ class UserHelper {
isVerified
}
});

done(null, socialUser[0].dataValues);
const { password, token, ...userInfo } = socialUser[0].dataValues;
const newToken = AuthenticateToken.signToken(userInfo);
done(null, newToken);
} catch (error) {
done(error, false, error.message);
}
Expand All @@ -68,18 +70,10 @@ class UserHelper {
* oauth social
* @param {object} req request object
* @param {object} res response object
* @param {object} successSocialSignUp success message
* @returns {object} object
*/
static OAuthSocial(req, res, successSocialSignUp) {
const token = AuthenticateToken.signToken(req.user);
const {
id, email, firstName, lastName, signupType, isVerified
} = req.user;
const data = {
id, email, firstName, lastName, signupType, isVerified, token
};
const socialInfo = JSON.stringify({ signupType, token });
static OAuthSocial(req, res) {
const socialInfo = JSON.stringify({ signupType: req.user.signupType, token: req.user });
return res.redirect(`${process.env.REDIRECT_SOCIAL_AUTH_DATA_URL}/?info=${socialInfo}`);
}
}
Expand Down
34 changes: 34 additions & 0 deletions src/middlewares/advancedVerifyoken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import jwt from 'jsonwebtoken';
import Response from '../helpers/Response';
import UserService from '../services/UserService';
/**
* verify token
* @param {object} req request object
* @param {object} res response object
* @param {object} next response object
* @returns {object} data
* next
*/
const verifyToken = (req, res, next) => {
const token = !req.headers.token ? req.params.token : req.headers.token;
if (!token) {
return Response.errorMessage(req, res, 'Please, insert the token', 401);
}
jwt.verify(
token, process.env.JWT_KEY,
async (err, result) => {
if (err) {
return Response.errorMessage(req, res, err, 401);
}
const isTokenExist = await UserService.blacklistToken(token);
if (isTokenExist) {
return Response.errorMessage(req, res, 'You have provided an invalid token', 401);
}
result.token = token;
req.user = result;
next();
}
);
};

export default verifyToken;
2 changes: 2 additions & 0 deletions src/routes/api/userRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import UserController from '../../controllers/UserController';
import verifyToken from '../../middlewares/verifyToken';
import checkInputDataError from '../../middlewares/checkInputDataError';
import customValidator from '../../middlewares/customValidator';
import advancedVerifyoken from '../../middlewares/advancedVerifyoken';

const userRoute = express.Router();

Expand Down Expand Up @@ -151,4 +152,5 @@ const { isImage } = customValidator;
userRoute.put('/profile-settings', verifyToken, profileUpdateRules(), checkInputDataError, isImage, isManager, isUserVerified, UserController.updateProfile);
userRoute.get('/view-profile', verifyToken, isUserVerified, UserController.viewProfile);
userRoute.get('/notification', verifyToken, isUserVerified, UserController.viewNotification);
userRoute.patch('/logout', advancedVerifyoken, UserController.logout);
export default userRoute;
30 changes: 29 additions & 1 deletion src/services/UserService.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { users, userProfile } from '../database/models';
import { users, userProfile, blacklist } from '../database/models';
import CommonQueries from './CommonQueries';
import HashPassword from '../helpers/HashPassword';
/**
Expand Down Expand Up @@ -169,6 +169,34 @@ class UserService {

return userData;
}

/**
* Save user token in blacklist table
* @static
* @param {Object} token the request object
* @returns {Object} response
*/
static async saveUserToken(token) {
const saveInBlackList = {
token
};
const result = await CommonQueries.create(blacklist, saveInBlackList);
return result;
}

/**
* Is token blacklisted
* @static
* @param {Object} token the request object
* @returns {Object} response
*/
static async blacklistToken(token) {
const isTokenBlacklisted = {
where: { token }
};
const result = await CommonQueries.findOne(blacklist, isTokenBlacklisted);
return result;
}
}

export default UserService;

0 comments on commit bc2d471

Please sign in to comment.