Skip to content

Commit

Permalink
Merge 1246074 into a9d5ca7
Browse files Browse the repository at this point in the history
  • Loading branch information
dinorhythms committed Aug 22, 2019
2 parents a9d5ca7 + 1246074 commit feb2bc7
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 3 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"mocha": true
},
"rules": {
"linebreak-style": 0,
"one-var": 0,
"one-var-declaration-per-line": 0,
"new-cap": 0,
Expand Down
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ node_js:

services:
- postgresql
- redis-server

before_script:
- psql -c 'create database test;' -U postgres
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"debug": "^4.1.1",
"dotenv": "^8.0.0",
"express": "^4.17.1",
"ioredis": "^4.14.0",
"jsonwebtoken": "^8.5.1",
"pg": "^7.12.1",
"pg-hstore": "^2.3.3",
Expand Down
23 changes: 23 additions & 0 deletions src/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Redis from 'ioredis';
import models from '../database/models';
import authHelper from '../utils/authHelper';
import response from '../utils/response';
Expand Down Expand Up @@ -62,7 +63,29 @@ const signIn = async (req, res) => {
}
};

/**
* User logout Function
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @returns {Object} - custom response
*/

const logout = async (req, res) => {
const redis = new Redis();
const token = req.headers.authorization.split(' ')[1];

try {
await redis.set(token, token, 'EX', 604800000);
return response(res, 200, 'success', { message: messages.loggedOut });
} catch (e) {
return response(res, 500, 'error', {
errors: e
});
}
};

export default {
signUp,
signIn,
logout
};
38 changes: 38 additions & 0 deletions src/middlewares/blacklistMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Redis from 'ioredis';
import response from '../utils/response';
import messages from '../utils/messages';

const redis = new Redis();

/**
* Token Blacklist Middleware
* @param {Object} req - Express request object
* @param {Oject} res - Express response object
* @param {Function} next - Express next middleware function
* @returns {Object} - Returns Object
*/
const checkBlacklist = async (req, res, next) => {
const token = req.headers.authorization && req.headers.authorization.split(' ')[1];

if (!token) {
return response(res, 401, 'error', {
message: messages.noToken
});
}

try {
const checkedToken = await redis.get(token);
if (checkedToken) {
return response(res, 500, 'error', {
message: messages.blacklisted
});
}
next();
} catch (e) {
return response(res, 500, 'error', {
errors: e
});
}
};

export default checkBlacklist;
26 changes: 25 additions & 1 deletion src/routes/api/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
import userController from '../../controllers/userController';
import validate from '../../middlewares/validator';
import { signUpSchema, signInSchema } from '../../validation/userSchema';
import checkBlacklist from '../../middlewares/blacklistMiddleware';

const {
signUp, signIn
signUp,
signIn,
logout
} = userController;

const userRoute = (router) => {
Expand Down Expand Up @@ -91,5 +94,26 @@ const userRoute = (router) => {
*/

.post(validate(signInSchema), signIn);
router.route('/user/logout')

/**
* @swagger
* /api/v1/user/logout:
* post:
* tags:
* - Users
* description: Logout user
* produces:
* - application/json
* responses:
* 200:
* description: Logged out successfully
* 401:
* description: Token missing, you need a token to have access
* 500:
* description: You are already logged out!
*/

.post(checkBlacklist, logout);
};
export default userRoute;
59 changes: 57 additions & 2 deletions src/test/routes/auth.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
app, chai, expect, sinon
app, chai, expect, sinon, messages
} from '../testHelpers/config';
import models from '../../database/models';
import mockData from '../mockData';
Expand All @@ -9,7 +9,7 @@ const { User } = models;
const { userMock } = mockData;

const BASE_URL = '/api/v1';

let user = null;
describe('AUTH', () => {
describe('POST /user/signup', () => {
const signupEndpoint = `${BASE_URL}/user/signup`;
Expand All @@ -20,6 +20,7 @@ describe('AUTH', () => {
.send(userMock.validUser)
.end((err, res) => {
const { data } = res.body;
user = data.user;
expect(data.user).property('token');
expect(data.user).property('email');
done(err);
Expand Down Expand Up @@ -116,4 +117,58 @@ describe('AUTH', () => {
});
});
});
describe('POST /user/logout', () => {
const logoutEndpoint = `${BASE_URL}/user/logout`;
it('should logout user', (done) => {
chai
.request(app)
.post(logoutEndpoint)
.set('Content-Type', 'application/json')
.set('Authorization', `Bearer ${user.token}`)
.end((err, res) => {
const { data } = res.body;
expect(res.status).to.equal(200);
expect(data).property('message');
done(err);
});
});
it('should throw no token error', (done) => {
chai
.request(app)
.post(logoutEndpoint)
.set('Content-Type', 'application/json')
.end((err, res) => {
const { data } = res.body;
expect(res.status).to.equal(401);
expect(data).property('message');
expect(res.body.data.message).to.equal(messages.noToken);
done(err);
});
});
it('should throw bad token error', (done) => {
chai
.request(app)
.post(logoutEndpoint)
.set('Content-Type', 'application/json')
.set('Authorization', `Bearer ${user.token} wrong`)
.end((err, res) => {
expect(res.status).to.equal(500);
done(err);
});
});
it('should throw user already logged out', (done) => {
chai
.request(app)
.post(logoutEndpoint)
.set('Content-Type', 'application/json')
.set('Authorization', `Bearer ${user.token}`)
.end((err, res) => {
const { data } = res.body;
expect(res.status).to.equal(500);
expect(data).property('message');
expect(res.body.data.message).to.equal(messages.blacklisted);
done(err);
});
});
});
});
3 changes: 3 additions & 0 deletions src/utils/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const messages = {
validPassword: 'Minimum of 6 letters, a character and number required',
label: 'error',
joiError: 'child \"body\" fails because [child \"error\" fails because ',
noToken: 'Token missing, you need a token to have access',
blacklisted: 'The token has been blacklisted',
loggedOut: 'Logged out successfully'
};

export default messages;

0 comments on commit feb2bc7

Please sign in to comment.