Skip to content

Commit

Permalink
Refactoring and handling env vars
Browse files Browse the repository at this point in the history
  • Loading branch information
vipulrawat committed Oct 15, 2018
1 parent 746dc64 commit f9dd7cc
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 26 deletions.
5 changes: 3 additions & 2 deletions packages/api/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import mongoose from 'mongoose';
import request from 'request-promise-native';

import app from '../src';
import { PORT, DB_URL } from '../config';
import { PORT } from '../config';
import tokenService from '../src/services/token';
import { User } from '../src/db';

Expand Down Expand Up @@ -35,7 +35,8 @@ let server;
const serverUrl = `http://localhost:${PORT}`;

beforeAll((done) => {
mongoose.connect(DB_URL, { useNewUrlParser: true }, () => {
jest.setTimeout(10000);
mongoose.connect(process.env.DB_URL, { useNewUrlParser: true }, () => {
server = app.listen(PORT, () => {
// save current contents of users collection, and insert some dummy users
User.find({})
Expand Down
2 changes: 1 addition & 1 deletion packages/api/config/ci.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
PORT: 5000,
DB_URL: 'mongodb://localhost:27017/test-delta',
EMAIL_VERIFICATION_TOKEN_EXPIRY: 15 * 60, // 15 minutes
LOGIN_TOKEN_EXPIRY: '28d',
LOGIN_COOKIE_EXPIRY: 4 * 7 * 24 * 60 * 60 * 1000, // 4 weeks
...process.env,
};
2 changes: 1 addition & 1 deletion packages/api/config/dev.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
PORT: 5000,
DB_URL: 'mongodb://localhost:27017/test-delta',
EMAIL_VERIFICATION_TOKEN_EXPIRY: 15 * 60, // 15 minutes
LOGIN_TOKEN_EXPIRY: '28d',
LOGIN_COOKIE_EXPIRY: 4 * 7 * 24 * 60 * 60 * 1000, // 4 weeks
...process.env,
};
2 changes: 1 addition & 1 deletion packages/api/config/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// The env var 'MODE' should be in ['PROD', 'DEV', 'CI']
const mode = process.env.MODE || 'DEV';

require('dotenv').load();
// eslint-disable-next-line import/no-dynamic-require
module.exports = require(`./${mode.toLowerCase()}`);
2 changes: 1 addition & 1 deletion packages/api/config/prod.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module.exports = {
PORT: 5000,
DB_URL: 'mongodb://localhost:27017/test-delta',
EMAIL_VERIFICATION_TOKEN_EXPIRY: 15 * 60, // 15 minutes
LOGIN_TOKEN_EXPIRY: '28d',
LOGIN_COOKIE_EXPIRY: 4 * 7 * 24 * 60 * 60 * 1000, // 4 weeks
...process.env,
};
8 changes: 8 additions & 0 deletions packages/api/constants/ERR_MSGS.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
invalidToken: 'TOKEN_INVALID',
missingToken: 'TOKEN_MISSING',
expiredToken: 'TOKEN_EXPIRED',
missingEmail: 'EMAIL_MISSING',
invalidEmail: 'EMAIL_INVALID',
internalServerError: 'Server error',
};
4 changes: 4 additions & 0 deletions packages/api/constants/TOKEN_TYPES.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
emailToken: 'EMAIL_VERIFICATION',
loginToken: 'LOGIN',
};
23 changes: 12 additions & 11 deletions packages/api/src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,38 @@ const config = require('../../config');
const tokenService = require('../services/token');
const mailService = require('../services/mail');
const models = require('../db');

const ERR_MSGS = require('../../constants/ERR_MSGS');
const TOKEN_TYPES = require('../../constants/TOKEN_TYPES');
// Checks that the user who sent the request has a valid token
// If authenticated, the decoded token is made available to next middleware at req.decoded
// else, a 400 response is returned, and the next middleware is not called
function isAuthenticated(req, res, next) {
const token = req.headers.Authorization || req.query.token
|| req.cookies.token || req.body.token;
if (!token) {
res.status(400).json({ error: 'TOKEN_MISSING' });
res.status(400).json({ error: ERR_MSGS.invalidToken });
} else {
tokenService.decode(token)
.then((decoded) => {
req.decoded = decoded;
next();
})
.catch(e => res.status(400).json({
error: e.name === 'TokenExpiredError' ? 'TOKEN_EXPIRED' : 'TOKEN_INVALID',
error: e.name === 'TokenExpiredError' ? ERR_MSGS.expiredToken : ERR_MSGS.invalidToken,
}));
}
}

routes.post('/generateToken', (req, res) => {
if (!Reflect.has(req.body, 'email')) {
res.status(400).json({ error: 'EMAIL_MISSING' });
res.status(400).json({ error: ERR_MSGS.missingEmail });
} else if (typeof req.body.email !== 'string' || !isEmail(req.body.email)) {
res.status(400).json({ error: 'EMAIL_INVALID' });
res.status(400).json({ error: ERR_MSGS.invalidEmail });
} else {
const { email } = req.body;
tokenService.generate({
email,
tokenType: 'EMAIL_VERIFICATION',
tokenType: TOKEN_TYPES.emailToken,
}, config.EMAIL_VERIFICATION_TOKEN_EXPIRY)
.then(token => mailService.sendMail(email, token))
.then(() => res.json({ tokenStatus: 'success', email }))
Expand All @@ -50,9 +51,9 @@ routes.post('/generateToken', (req, res) => {

routes.post('/verifyToken', isAuthenticated, (req, res) => {
const { email, tokenType } = req.decoded;
if (tokenType === 'EMAIL_VERIFICATION') {
if (tokenType === TOKEN_TYPES.emailToken) {
// give the user a longer-lived token that can be used for future auto-login
tokenService.generate({ email, tokenType: 'LOGIN' }, config.LOGIN_TOKEN_EXPIRY)
tokenService.generate({ email, tokenType: TOKEN_TYPES.loginToken }, config.LOGIN_TOKEN_EXPIRY)
.then((generatedToken) => {
res.cookie('token', generatedToken, {
// not adding a maxAge property to the cookie causes it to be
Expand All @@ -69,9 +70,9 @@ routes.post('/verifyToken', isAuthenticated, (req, res) => {
})
.catch(() => {
// TODO: error logging
res.status(500).json({ error: 'Server error' });
res.status(500).json({ error: ERR_MSGS.internalServerError });
});
} else if (tokenType === 'LOGIN') {
} else if (tokenType === TOKEN_TYPES.loginToken) {
// user already has a login token, so just acknowledge the sign-in
res.json({
authentication: 'success',
Expand All @@ -80,7 +81,7 @@ routes.post('/verifyToken', isAuthenticated, (req, res) => {
} else {
// We sent a token containing invalid data to the user
winston.error(`!IMP: From: ${req.ip} - ${req.method} - ${req.originalUrl} - User Email: ${email}`);
res.status(400).json({ error: 'TOKEN_INVALID' });
res.status(400).json({ error: ERR_MSGS.invalidToken });
}
});

Expand Down
6 changes: 4 additions & 2 deletions packages/api/src/services/mail.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const sgMail = require('@sendgrid/mail');
import sgMail from '@sendgrid/mail';

sgMail.setApiKey('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');
const { SENDGRID_API_KEY } = require('../../config');

sgMail.setApiKey(SENDGRID_API_KEY);

const sendMail = (email, token) => {
const msg = {
Expand Down
8 changes: 5 additions & 3 deletions packages/api/src/services/token.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const jwt = require('jsonwebtoken');
import jwt from 'jsonwebtoken';

const { JWT_SECRET } = require('../../config');

function generate(payload, expiresIn = '1d') {
return new Promise((resolve, reject) => {
jwt.sign(payload, process.env.JWT_SECRET, { expiresIn }, (err, token) => {
jwt.sign(payload, JWT_SECRET, { expiresIn }, (err, token) => {
if (err) reject(err);
else resolve(token);
});
Expand All @@ -11,7 +13,7 @@ function generate(payload, expiresIn = '1d') {

function decode(token) {
return new Promise((resolve, reject) => {
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
jwt.verify(token, JWT_SECRET, (err, decoded) => {
if (err) reject(err);
else resolve(decoded);
});
Expand Down
7 changes: 4 additions & 3 deletions packages/api/winston.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const winston = require('winston');
import winston from 'winston';

const path = require('path');

const options = {
Expand All @@ -7,7 +8,7 @@ const options = {
filename: path.join(__dirname, '/logs/app.log'),
handleExceptions: true,
json: true,
maxsize: 5242880, // 5MB
maxsize: 5 * 1024 * 1024, // 5MB
maxFiles: 5,
colorize: false,
},
Expand All @@ -27,7 +28,7 @@ const logger = winston.createLogger({
});

logger.stream = {
write(message, encoding) { // eslint-disable-line
write(message) {
logger.info(message);
},
};
Expand Down
54 changes: 53 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,43 @@
dependencies:
any-observable "^0.3.0"

"@sendgrid/client@^6.3.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/@sendgrid/client/-/client-6.3.0.tgz#25c34b11bec392ab43ca7e52fb35e4105fb00901"
integrity sha512-fTy8vRpA9Whtf8ULQr/0vkSZaQvGQ97rY5N5PrevKRtugJMsJqFMKO0pwzEWeqITSg71aMMTj57QTgw3SjZvnQ==
dependencies:
"@sendgrid/helpers" "^6.3.0"
"@types/request" "^2.0.3"
request "^2.81.0"

"@sendgrid/helpers@^6.3.0":
version "6.3.0"
resolved "https://registry.yarnpkg.com/@sendgrid/helpers/-/helpers-6.3.0.tgz#1b1798af22aa7a4c98257fab3dd2a6a6afd8b467"
integrity sha512-uTFcmhCDFg/2Uhz+z/cLwyLHH0UsblG49hKwdR7nKbWsGKWv4js7W32FlPdXqy2C/plTJ20vcPLgKM1m3F/MjQ==
dependencies:
chalk "^2.0.1"
deepmerge "^2.1.1"

"@sendgrid/mail@^6.3.1":
version "6.3.1"
resolved "https://registry.yarnpkg.com/@sendgrid/mail/-/mail-6.3.1.tgz#e5003af167ca4dd358f04075aad4cfc30cef6c34"
integrity sha512-5zIeAV9iU+0hQkrOQ/D4RB2MfpK+lNbOortIfQdCh95aMDF/TRc9WB8FGNhmQrx9YMuJTms5eiBklF0Fi/dbVg==
dependencies:
"@sendgrid/client" "^6.3.0"
"@sendgrid/helpers" "^6.3.0"

"@types/caseless@*":
version "0.12.1"
resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.1.tgz#9794c69c8385d0192acc471a540d1f8e0d16218a"
integrity sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A==

"@types/form-data@*":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e"
integrity sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==
dependencies:
"@types/node" "*"

"@types/jss@^9.5.6":
version "9.5.6"
resolved "https://registry.yarnpkg.com/@types/jss/-/jss-9.5.6.tgz#96e1d246ddfbccc4867494077c714773cf29acde"
Expand Down Expand Up @@ -890,6 +927,21 @@
"@types/prop-types" "*"
csstype "^2.2.0"

"@types/request@^2.0.3":
version "2.47.1"
resolved "https://registry.yarnpkg.com/@types/request/-/request-2.47.1.tgz#25410d3afbdac04c91a94ad9efc9824100735824"
integrity sha512-TV3XLvDjQbIeVxJ1Z3oCTDk/KuYwwcNKVwz2YaT0F5u86Prgc4syDAp6P96rkTQQ4bIdh+VswQIC9zS6NjY7/g==
dependencies:
"@types/caseless" "*"
"@types/form-data" "*"
"@types/node" "*"
"@types/tough-cookie" "*"

"@types/tough-cookie@*":
version "2.3.3"
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.3.tgz#7f226d67d654ec9070e755f46daebf014628e9d9"
integrity sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ==

"@webassemblyjs/[email protected]":
version "1.7.8"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.8.tgz#f31f480debeef957f01b623f27eabc695fa4fe8f"
Expand Down Expand Up @@ -2732,7 +2784,7 @@ deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=

deepmerge@^2.0.1:
deepmerge@^2.0.1, deepmerge@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
Expand Down

0 comments on commit f9dd7cc

Please sign in to comment.