Skip to content

Commit

Permalink
feat(setup-authentication): implement jwt strategy for authentication.
Browse files Browse the repository at this point in the history
- Ensure user receives jwt on successful registration and signin
- Ensure user is registered
- Ensure user signs in

[Maintains #166789989]
  • Loading branch information
elemanhillary committed Jul 5, 2019
1 parent a3cd7a0 commit 03c0022
Show file tree
Hide file tree
Showing 28 changed files with 493 additions and 95 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DATABASE_URL=postgres://wmqxaqjr:tTHUvMlywe2owxdrqEaofzdC2RKF4XYp@raja.db.elephantsql.com:5432/wmqxaqjr
DATABASE_TEST=postgres://wmqxaqjr:tTHUvMlywe2owxdrqEaofzdC2RKF4XYp@raja.db.elephantsql.com:5432/wmqxaqjr
SECRET=qwertyuiop[]\';lkjhgfdsa`zxcvbnm,./+_)(*&^%$#@!)
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
"sourceType": "module"
},
"rules": {
"one-var": 0,
"one-var": 0,
"one-var-declaration-per-line": 0,
"new-cap": 0,
"consistent-return": 0,
"no-param-reassign": 0,
"comma-dangle": 0,
"curly": ["error", "multi-line"],
"import/no-unresolved": [2, { "commonjs": true }],
"import/prefer-default-export": 0,
"no-shadow": ["error", { "allow": ["req", "res", "err"] }],
"valid-jsdoc": ["error", {
"requireReturn": true,
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Logs
logs
*.log
.dist
.DS_Store

npm-debug.log*

seeders
# Runtime data
pids
*.pid
Expand Down
4 changes: 2 additions & 2 deletions .sequelizerc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'path';
const path = require('path');

module.exports = {
"config": path.resolve('./config', 'config.js'),
"config": path.resolve('./config', 'index.js'),
"models-path": path.resolve('./models'),
"migrations-path": path.resolve('./migrations')
};
9 changes: 7 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
language: node_js
node_js:
node_js:
- "stable"
services:
- postgresql
before_script:
- psql -c 'CREATE DATABASE users_test;' -U postgres
- npm i sequelize -g
- sequelize db:migrate
after_success:
- npm test
- npm run coveralls

10 changes: 5 additions & 5 deletions config/index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import dotenv from 'dotenv';
const dotenv = require('dotenv');

dotenv.config();

module.exports = {
development: {
DATABASE_URL: 'DATABASE_URL',
use_env_variable: 'DATABASE_URL',
dialect: 'postgres',
logging: false
},
test: {
DATABASE_URL: 'DATABASE_TEST',
use_env_variable: 'DATABASE_TEST',
dialect: 'postgres',
logging: false,
logging: false
},
production: {
DATABASE_URL: 'DATABASE_URL',
use_env_variable: 'DATABASE_URL',
dialect: 'postgres',
logging: false
}
Expand Down
60 changes: 60 additions & 0 deletions controllers/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import db from '../models';
import Auth from '../helpers/auth';

export const signup = async (req, res) => {
const {
userName, firstName, lastName, email
} = req.body;
const password = Auth.hashPassword(req.body.password);
const transaction = await db.sequelize.transaction();
try {
const newUser = await db.User.create({
userName, firstName, lastName, email, password
}, {
transaction
});
await transaction.commit();
if (newUser) {
res.status(201).json({
success: 1,
token: Auth.genToken(userName, firstName, lastName, email),
user: {
username: newUser.userName,
email: newUser.email
},
message: 'created successfully'
});
}
} catch (ex) {
await transaction.rollback();
res.json({ message: `${ex.errors[0].path.toLowerCase()} already exists` });
}
};

export const signin = async (req, res) => {
const { email, password } = req.body;
const transaction = await db.sequelize.transaction();
try {
const user = await db.User.findOne({ where: { email } }, { transaction });
await transaction.commit();
if (!user) {
return res.status(401).json({ message: 'user doesnot exit' });
}
const passBool = Auth.comparePassword(password, user.password);
if (passBool) {
return res.status(200).json({
success: 1,
token: Auth.genToken(user.userName, user.firstName, user.lastName, user.email),
user: {
username: user.userName,
email: user.email
},
message: 'logged in'
});
}
return res.status(401).json({ message: 'wrong username or password' });
} catch (ex) {
await transaction.rollback();
res.json({ message: ex });
}
};
2 changes: 2 additions & 0 deletions controllers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './auth';
export * from './welcomeController';
18 changes: 7 additions & 11 deletions controllers/welcomeController.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import dotenv from "dotenv";
import db from "../models/databaseUrl";
dotenv.config();
import dotenv from 'dotenv';

class WelcomeController {
static welcome(req, res) {
res.status(200).json({
msg: "Welcome to Authors Haven API, a #92Explorers Product."
});
}
}
dotenv.config();

export default WelcomeController;
export const welcome = (req, res) => {
res.status(200).json({
message: 'Welcome to Authors Haven API, a #92Explorers Product.'
});
};
27 changes: 27 additions & 0 deletions helpers/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import dontenv from 'dotenv';

dontenv.config();

class Auth {
static hashPassword(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(10));
}

static comparePassword(password, hashedPassword) {
return bcrypt.compareSync(password, hashedPassword);
}

static genToken(username, firstname, lastname, email) {
const token = jwt.sign({
email,
username,
firstname,
lastname,
}, process.env.SECRET, { expiresIn: '24h' });
return token;
}
}

export default Auth;
3 changes: 1 addition & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '@babel/polyfill';
import express from 'express';
import bodyParser from 'body-parser';
import cors from 'cors';
Expand Down Expand Up @@ -25,8 +26,6 @@ app.get('/swagger.json', (req, res) => {
});
app.use('/docs', swaggerUI.serve, swaggerUI.setup(swagger.swaggerSpec));

require('./models/User');

app.use(router);

// / catch 404 and forward to error handler
Expand Down
35 changes: 35 additions & 0 deletions middlewares/checkToken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';

dotenv.config();

export const checkToken = (req, res, next) => {
let token = req.headers.authorization || req.headers['x-access-token'];
if (token === undefined) {
return res.json({
message: 'token undefined',
});
}

if (token.startsWith('Bearer ')) {
token = token.slice(7, token.length);
}

if (token) {
jwt.verify(token, process.env.SECRET, (error, decoded) => {
if (error) {
return res.status(401).json({
success: false,
message: error.message,
});
}
req.decoded = decoded;
next();
});
} else {
return res.status(401).json({
success: false,
message: 'token empty',
});
}
};
1 change: 1 addition & 0 deletions middlewares/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './checkToken';
39 changes: 39 additions & 0 deletions migrations/20190704183748-create-user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstName: {
type: Sequelize.STRING
},
lastName: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING,
unique: true
},
userName: {
type: Sequelize.STRING,
unique: true,
},
provider: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
}),
down: queryInterface => queryInterface.dropTable('Users')
};
11 changes: 11 additions & 0 deletions models/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default (sequelize, DataTypes) => {
const User = sequelize.define('User', {
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
email: DataTypes.STRING,
userName: DataTypes.STRING,
provider: DataTypes.STRING,
password: DataTypes.STRING
}, {});
return User;
};
24 changes: 0 additions & 24 deletions models/databaseUrl.js

This file was deleted.

32 changes: 18 additions & 14 deletions models/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import fs from 'fs';
import path from 'path';
import Sequelize from 'sequelize';
import configs from '../config/index'; // Importing configuration file
import configs from '../config'; // Importing configuration file

const path = require('path');

require('dotenv').config(); // Enabling the use of the env variables

Expand All @@ -11,23 +12,27 @@ const config = configs[env]; // Config variable takes in our new configuration
const db = {};

let sequelize;
if (config.DATABASE_URL) {
sequelize = new Sequelize(process.env[config.DATABASE_URL], config);
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
sequelize = new Sequelize(
config.database,
config.username,
config.password,
config
);
}

fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = sequelize['import'](path.join(__dirname, file));
fs.readdirSync(__dirname)
.filter(
file => file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js'
)
.forEach((file) => {
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});

Object.keys(db).forEach(modelName => {
Object.keys(db).forEach((modelName) => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
Expand All @@ -37,4 +42,3 @@ db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

Loading

0 comments on commit 03c0022

Please sign in to comment.