Skip to content
Open
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
7 changes: 5 additions & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
MONGODB_URI =
PORT = 3000
MONGODB_URI = mongodb+srv://Randee:oAuthIsRad@cluster0-ge8gh.mongodb.net/test?retryWrites=true&w=majority
PORT = 3000
SECRET = johnIsASquib

module.exports()
38 changes: 38 additions & 0 deletions _tests_/server.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// need to test
//post / signup
//post /signup
// get / users

const serverApp = require('../lib/server.js');
const supergoose = require('@code-fellows/supergoose');

//create our mock server from the imported server
const mockRequest = supergoose(serverApp.server);

//database is set up? not needed

describe('happy path', () => {
it('can create a user ', async () => {
//post to signup
//sed username, password, (fname, lname)
let response = await mockRequest.post('/signup').send({
username: 'rUser',
password: 'rPass',
fname: 'Rill',
lname: 'Riggs'
});

//check the response
// it should have the right status (201)
//it should have a new record create in the response.body
// this new record should have an _id
//this new record should have a password created that doesn't match rpass
expect(response.status).toBe(201);
expect(response.body._id).toBeDefined();
expect(response.body.password).toBeDefined();
expect(response.body.password).not.toBe('rPass');

})
})


6 changes: 6 additions & 0 deletions _tests_/user-model.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// user schema

//test
//Create - password is hashed

// Read -
31 changes: 31 additions & 0 deletions docs/swagger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const generateSwagger = (app) => {
const expressSwagger = require('express-swagger-generator')(app);

let options = {
swaggerDefinition: {
info: {
description: 'Access CRUD operations for fruits and vegetables',
title: 'My API Server',
version: '1.0.0',
},
host: 'localhost:3000',
basePath: '/',
produces: ['application/json', 'text/html'],
schemes: ['http'],
securityDefinitions: {
JWT: {
type: 'apiKey',
in: 'header',
name: 'Authorization',
description: '',
},
},
},
basedir: __dirname, //app absolute path
files: ['../lib/server.js', '../lib/routes/*'], //Path to the API handlers
};

expressSwagger(options);
};

module.exports = generateSwagger;
7 changes: 4 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';
const server = require('./lib/server')
const server = require('./lib/server.js')
require('dotenv').config();

//need mongodbURI to pass in as a second argument
server.start(process.env.PORT)


server.start(process.env.PORT, process.env.MONGODB_URI);
65 changes: 65 additions & 0 deletions lib/middleware/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict';

console.log('auth.js file was hit');

const Model = require('../models/model.js');
const userSchema = require('../models/user-schema.js');
const UserModel = new Model(userSchema);

const base64Decoder = (encodedString) => {
console.log('encodedString', encodedString);
let data = {
username: '',
password: '',
};

let decodedSting = Buffer.from(encodedString, 'base64').toString();
let dataPieces = decodedSting.split(':');

data.username = dataPieces[0];
data.password = dataPieces[1];

return data;
}

const getUserFromCredentials = async (userData) => {
let possibleUser = await UserModel.readByQuery({
username: userData.username,
});

for (let i = 0; i <possibleUser.length; i++){
let isSame = possibleUser[i].comparePasswords(userData.password);

if (isSame) {
return possibleUser[i];
}
}
return userData;
}

const auth = async (req, res, next) => {
if (!req.headers.authorization) {
next({err: 401, msg: 'Missing auth headers'});
return;
}
let authPieces = req.headers.authorization.split(' ');

if (authPieces.length === 2) {
if(authPieces[0] === 'Basic') {
let authData = base64Decoder(authPieces[1]);
req.user = await getUserFromCredentials(authData);

next();
return;
} else if (authPieces[0] === 'Bearer') {
let tokenData = UserModel.verifyToken(authPieces[1]);
req.user = await UserModel.read(tokenData._id);
next();
return;
}
}

next({ err: 401, msg: 'Missing correct Auth Header'});
}

module.exports = auth;
22 changes: 18 additions & 4 deletions lib/models/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,33 @@ class Model {
return await record.save();
}

async read() {
async read(_id) {
let record = await this.schema.findOne({_id})
return record
}

async readByQuery(query) {
let records = await this.schema.find(query);
return records;
}

async update(_id, data) {
let updateInfo = await this.schema.updateOne({_id}, data);
// need to finsih this here
// if(updateInfo && updateInfo.)
if (updateInfo && updateInfo.Modified === 1) {
let record = await this.read(_id);
return record;
}
return false;
}

async delete() {
async delete(_id) {
let deletedInfo = await this.schema.deleteOne({_id});
if (deletedInfo.deletedCount === 1) return true;
return false;
}

verifyToken(token) {
return this.schema.verifyToken(token);
}
}

Expand Down
24 changes: 22 additions & 2 deletions lib/models/user-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,37 @@

const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');

const schema = mongoose.Schema({
username: {type: 'String', unique: true, required: true},
password: {type: 'String', required: true},
fname: {type: 'String'},
lname: {type: 'String'}
})
});

schema.pre('save', async function () {
// hash password every time we create a user
this.password = await bcrypt.hash(this.password, 10);
})
});

schema.methods.generateToken = function () {
let timeout = Date.now() + 5000000;

return jwt.sign(
{ exp: timeout, data: { _id: this._id}},
process.env.SECRET,
);
}

schema.methods.comparePasswords = async function (plainTextPass) {
return await bcrypt.compare(plainTextPass, this.password);
};

schema.statics.verifyToken = function (token) {
let tokenContents = jwt.verify(token, process.env.SECRET);
return tokenContents.data;
}


module.exports = mongoose.model('users', schema);
99 changes: 40 additions & 59 deletions lib/routes/auth-routes.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,59 @@
'use strict';

console.log(' on auth-routes file');

//esoteric resources
const express = require('express');
const bycrypt = requre('bcrypt')
const bcrypt = require('bcrypt')

//internal resources
const Model = require('./models/model.js');
const userSchema = require('./models/user-schema.js');
const Model = require('../models/model.js');
const userSchema = require('../models/user-schema.js');
const auth = require('../middleware/auth.js');

//variables
const router = express.Router();
const UserModel = new Model(userSchema);

//route-wide middleware

const base64Decoder = (encodeStr) => {
let data = {
username: '',
password: ''
};
let decodedStr = new Buffer.from(encodeStr, 'base64').toString();
let dataPieces = decodedStr.split(':');
data.username = dataPieces[0];
data.password = dataPieces[1];
return data
}

//routes

router.post('/signup', async (req, res) => {
//create a user from either data in
//req.headers.authroization || req.body
//both are the same, but req.headers will be the same as signin
let basicAuth = req.headers.authorization.split(' ');
if (basicAuth.length === 2 && basicAuth[0] === 'Basic') {
let userData = base64Decoder(basicAuth[1])
let user = await UserModel.create({...userData, ... req.body});
res.send(user);
router.post('/signup', auth, async (req, res, next) => {
console.log('inside sighup route');
console.log(req.user);
if (req.user.username && !req.user._id) {
let user = await UserModel.create({ ...req.user, ...req.body})
let token = user.generateToken();
res.status(201);
res.send({ user, token });
return;
}else {
console.log('in else');
next({ err: 401, msg: 'Shoot! User already exists. Did you know you had a twin?!'});
}
res.end();
})

router.post('/signin', async (req, res) => {
// get user data from encoded basic auth
let basicAuth = req.headers.authorization.split(' ');
if (basicAuth.length === 2 && basicAuth[0] === 'Basic') {
let userData = base64Decoder(basicAuth[1])

let possibleUser = await UserModel.readByQuery({username: userData.username,})

for (let i = 0; i < possibleUser.length; i++) {
let isSame = await bycrypt.compare(
userData.password,
possibleUser[i].password,
)
if (isSame) {
req.user = possibleUser[i];
break;
}
if
}

if (req.user){
res.status(200)
res.send('Found You!');
} else {
res.status(401);
res.send('Oups, cant find you!');
}

res.send('in progress');
router.post('/signin', auth, async (req, res, next) => {
if (req.user._id) {
res.status(200);
let token = req.user.generateToken()
res.send({ user: req.user, token: token});
return;
} else {
next ({ err: 401, msg: 'User not found' })
}
res.end('signin');
})
});

router.get('/hidden', auth, async (req, res, next) => {
console.log(req.user);
if (req.user._id) {
res.status(200);
res.send('Top Secret Info Here! Only logged in users have access. Log yourself in, buddy!');
} else {
next({ err: 401, msg: 'Not Logged in or invalid token.' });
}
});


//error handling

//exports
module.exports = router;
Loading