Skip to content

Commit

Permalink
Merge 656b046 into 6624a4a
Browse files Browse the repository at this point in the history
  • Loading branch information
akhilome committed Oct 1, 2018
2 parents 6624a4a + 656b046 commit b6d357b
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 2 deletions.
1 change: 1 addition & 0 deletions .env.example
@@ -1,3 +1,4 @@
PORT=[server_port_number]
DATABASE_URL=postgres://[username]@[server]:[port]/[database_name]
TEST_DATABASE_URL=postgres://[username]@[server]:[port]/[database_name]
JWT_SECRET=[your_supersecret_secret]
90 changes: 90 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -35,6 +35,7 @@
"body-parser": "^1.18.3",
"dotenv": "^6.0.0",
"express": "^4.16.3",
"jsonwebtoken": "^8.3.0",
"pg": "^7.4.3"
},
"devDependencies": {
Expand Down
34 changes: 34 additions & 0 deletions server/controllers/authController.js
Expand Up @@ -28,6 +28,40 @@ class AuthController {
return res.status(400).json({ error });
}
}

static async signin(req, res, next) {
const { email, password } = req;

try {
// Check if a user with the provided email exists
const userExists = (await pool.query('SELECT * FROM users WHERE email=$1', [email])).rowCount;
if (!userExists) {
return res.status(400).json({
status: 'error',
message: 'no user with that email exists',
});
}

const userDetails = (await pool.query('SELECT * FROM users WHERE email=$1', [email])).rows[0];
const correctPassword = await bcrpyt.compare(password, userDetails.password);

if (!correctPassword) {
return res.status(400).json({
status: 'error',
message: 'incorrect password',
});
}

// Append important payload to request object
req.userId = userDetails.id;
req.userName = userDetails.name;
req.userEmail = userDetails.email;
req.userStatus = userDetails.is_admin ? 'admin' : 'customer';
return next();
} catch (error) {
return res.status(400).json({ error });
}
}
}

export default AuthController;
58 changes: 58 additions & 0 deletions server/middleware/authHandler.js
@@ -0,0 +1,58 @@
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';

dotenv.config();

class AuthHandler {
static async generateAuthToken(req, res) {
const {
userId,
userName,
userEmail,
userStatus,
} = req;

const token = jwt.sign({
userId,
userName,
userEmail,
userStatus,
}, process.env.JWT_SECRET);

res.status(200).json({
status: 'success',
message: 'user logged in successfully',
auth_token: token,
});
}

static authorize(req, res, next) {
const token = req.header('x-auth');

try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.userId = decoded.userId;
req.userName = decoded.userName;
req.userEmail = decoded.userEmail;
req.userStatus = decoded.userStatus;
return next();
} catch (error) {
return res.status(401).json({
status: 'error',
message: 'you must be logged in to use this route',
});
}
}

static authorizeAdmin(req, res, next) {
if (req.userStatus !== 'admin') {
return res.status(401).json({
status: 'error',
message: 'only admins can use this route',
});
}
return next();
}
}

export default AuthHandler;
22 changes: 22 additions & 0 deletions server/middleware/sanitizer.js
Expand Up @@ -38,6 +38,28 @@ class Sanitize {
req.password = password.trim();
return next();
}

static signin(req, res, next) {
const { email, password } = req.body;

if (!email || !password) {
return res.status(400).json({
status: 'error',
message: 'some required fields missing',
});
}

if (!Validator.isEmail(email) || !Validator.isValidPassword(password)) {
return res.status(400).json({
status: 'error',
message: 'email or password not correctly formatted',
});
}

req.email = email.trim();
req.password = password.trim();
return next();
}
}

export default Sanitize;
3 changes: 3 additions & 0 deletions server/routes/authRouter.js
@@ -1,8 +1,11 @@
import { Router } from 'express';
import AuthController from '../controllers/authController';
import Sanitize from '../middleware/sanitizer';
import AuthHandler from '../middleware/authHandler';

const router = new Router();

router.post('/signup', Sanitize.signup, AuthController.signup);
router.post('/login', Sanitize.signin, AuthController.signin, AuthHandler.generateAuthToken);

export default router;
71 changes: 70 additions & 1 deletion tests/routes/auth.spec.js
Expand Up @@ -3,7 +3,7 @@ import 'chai/register-should';
import chaiHttp from 'chai-http';
import dirtyChai from 'dirty-chai';
import app from '../../server/index';
import { seedData, populateTables } from '../seed/seed';
import { seedData, populateTables, populateUsersTable } from '../seed/seed';

chai.use(chaiHttp);
chai.use(dirtyChai);
Expand Down Expand Up @@ -98,3 +98,72 @@ describe('POST /auth/signup', () => {
});
});
});

describe('POST /auth/login', () => {
beforeEach(populateTables);
beforeEach(populateUsersTable);

it('should sign an existing user in', (done) => {
chai.request(app)
.post('/api/v1/auth/login')
.send(seedData.users.validUser)
.end((err, res) => {
if (err) done(err);

res.status.should.eql(200);
res.body.should.be.an('object').which.has.keys(['status', 'message', 'auth_token']);
done();
});
});

it('should respond with a 400 if required fields are missing', (done) => {
chai.request(app)
.post('/api/v1/auth/login')
.send(seedData.users.invalidUserNoData)
.end((err, res) => {
if (err) done(err);

res.status.should.eql(400);
done();
});
});

it('should not sign user in if non-existent email is provided', (done) => {
chai.request(app)
.post('/api/v1/auth/login')
.send(seedData.users.invalidUser)
.end((err, res) => {
if (err) done(err);

res.status.should.eql(400);
res.body.should.not.have.keys(['auth_token']);
done();
});
});

it('should not sign user in if invalid password is provided', (done) => {
chai.request(app)
.post('/api/v1/auth/login')
.send(seedData.users.validUserInvalidPass)
.end((err, res) => {
if (err) done(err);

res.status.should.eql(400);
res.body.should.not.have.keys(['auth_token']);
done();
});
});

it('should not sign in user with improper input format', (done) => {
chai.request(app)
.post('/api/v1/auth/login')
.send({ email: 'invalid@email', password: 'well?' })
.end((err, res) => {
if (err) done(err);

res.status.should.eql(400);
res.body.should.not.have.keys(['auth_token']);
done();
});
});
});

0 comments on commit b6d357b

Please sign in to comment.