Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const usersRouter = require('./routes/api/users');

const middleware = require('./utils/middleware');
const logger = require('./utils/logger');
const loginRouter = require('./routes/api/login');
const loginRouter = require('./routes/api/auth');
const softwaresRouter = require('./routes/api/softwares');

const app = express();
Expand Down Expand Up @@ -59,7 +59,7 @@ app.use(
// Routes
app.use(rootRouter);
app.use('/api/users', usersRouter);
app.use('/api/login', loginRouter);
app.use('/api/auth', loginRouter);
app.use('/api/softwares', softwaresRouter);

app.use(middleware.unknownEndPoint);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const User = require('../../models/user');
const config = require('../../utils/config');
const { getReqAuthToken, verifyAuthToken } = require('../../utils/jwtUtils');

const postLogin = async (req, res) => {
const postAuth = async (req, res) => {
const { body } = req;

// Return the user before update so can view last login.
Expand All @@ -14,9 +15,10 @@ const postLogin = async (req, res) => {
},
);

const passwordCorrect = user === null
? false
: await bcrypt.compare(body.password, user.passwordHash);
const passwordCorrect =
user === null
? false
: await bcrypt.compare(body.password, user.passwordHash);

if (!(user && passwordCorrect)) {
return res.status('401').json({
Expand All @@ -39,6 +41,22 @@ const postLogin = async (req, res) => {
});
};

const getAuth = async (req, res) => {
const token = getReqAuthToken(req);
const decodedToken = await verifyAuthToken(token);

if (!decodedToken) {
return res.status(401).json({
error: 'Missing or Invalid Token',
});
}

return res.status(200).json({
...decodedToken,
});
};

module.exports = {
postLogin,
postAuth,
getAuth,
};
2 changes: 2 additions & 0 deletions requests/api/auth/get_auth.rest
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GET http://localhost:3001/api/auth/
Authorization: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InNhbXBsZSIsImlkIjoiNWY4ZGEzODcwODlmZmY0ZGIwNmJjMGFmIiwiaWF0IjoxNjA0MTUxOTk5fQ.Wh_KEZVodkyPtZMzrAck1Z1mhIoLu2XoEcCH6G6kMAI
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
POST http://localhost:3001/api/login/
POST http://localhost:3001/api/auth/
Content-Type: application/json

{
Expand Down
8 changes: 8 additions & 0 deletions routes/api/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const authRouter = require('express').Router();
const authController = require('../../controllers/api/authController');

authRouter.post('/', authController.postAuth);

authRouter.get('/', authController.getAuth);

module.exports = authRouter;
6 changes: 0 additions & 6 deletions routes/api/login.js

This file was deleted.

12 changes: 6 additions & 6 deletions tests/api/login/login.test.js → tests/api/auth/auth.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ beforeEach(async () => {
await initialiseADefaultUserInDb();
});

describe('Login Controller', () => {
describe('POST request to /api/login/', () => {
describe('Auth Controller', () => {
describe('POST request to /api/auth/', () => {
test('When the username is incorrect, return status code 401 and json with error Invalid username and/or password message', async () => {
const loginUser = {
username: 'Sampl',
password: 'SamplePassword',
};

const response = await api
.post('/api/login')
.post('/api/auth')
.send(loginUser)
.expect(401)
.expect('Content-Type', /application\/json/);
Expand All @@ -35,7 +35,7 @@ describe('Login Controller', () => {
};

const response = await api
.post('/api/login')
.post('/api/auth')
.send(loginUser)
.expect(401)
.expect('Content-Type', /application\/json/);
Expand All @@ -50,7 +50,7 @@ describe('Login Controller', () => {
};

const response = await api
.post('/api/login')
.post('/api/auth')
.send(loginUser)
.expect(401)
.expect('Content-Type', /application\/json/);
Expand All @@ -65,7 +65,7 @@ describe('Login Controller', () => {
};

const response = await api
.post('/api/login')
.post('/api/auth')
.send(loginUser)
.expect(200)
.expect('Content-Type', /application\/json/);
Expand Down
37 changes: 37 additions & 0 deletions utils/jwtUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const jwt = require('jsonwebtoken');
const config = require('./config');
const User = require('../models/user');

const getReqAuthToken = (req) => {
const authorization = req.get('Authorization');

if (authorization && authorization.toLowerCase().startsWith('bearer ')) {
return authorization.substring(7);
}

return null;
};

/**
* Verify the input jwt token.
* Returns null if invalid/missing token or the user id in the payload is not found in database.
* If the token is valid and the user id is valid, returns the decoded token.
* @param {String} authToken The jwt user token
*/
const verifyAuthToken = async (authToken) => {
const decodedAuthToken = !authToken
? null
: jwt.verify(authToken, config.JWT_SECRET);

// Return null, it is an invalid token or the user id is not found in database.
if (!authToken || !(await User.findById(decodedAuthToken.id))) {
return null;
}

return decodedAuthToken;
};

module.exports = {
getReqAuthToken,
verifyAuthToken,
};
26 changes: 7 additions & 19 deletions utils/middleware.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,23 @@
/**
* The custom middlewares
*/
const jwt = require('jsonwebtoken');
const logger = require('./logger');
const config = require('./config');
const User = require('../models/user');

const getTokenFrom = (req) => {
const authorization = req.get('Authorization');

if (authorization && authorization.toLowerCase().startsWith('bearer ')) {
return authorization.substring(7);
}

return null;
};
const { getReqAuthToken, verifyAuthToken } = require('./jwtUtils');

/**
* Middleware to protect API endpoints through token validation.
* Returns json error message with status code 401 if token is missing or invalid.
* If token is valid, add decodedToken Object Property to req.
* Returns json error message with status code 401 if token is missing, invalid
* or user id in payload is invalid.
* If token is valid and user id is valid, add decodedToken Object Property to req.
* @param {Object} req request Object
* @param {Object} res response Object
* @param {Function} next Function which can be called to pass controls to the next handler
*/
const tokenValidation = async (req, res, next) => {
const token = getTokenFrom(req);
const decodedToken = !token ? null : jwt.verify(token, config.JWT_SECRET);
const token = getReqAuthToken(req);
const decodedToken = await verifyAuthToken(token);

// Only registered users can post to softwares API endpoint
if (!token || !decodedToken.id || !(await User.findById(decodedToken.id))) {
if (!decodedToken) {
return res.status(401).json({
error: 'Missing or Invalid Token',
});
Expand Down