Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Password change user story #66

Merged
merged 13 commits into from
Feb 25, 2021
15 changes: 10 additions & 5 deletions gamersnet_backend/persistence/tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,19 @@ async function updateUserToken(id, token) {
}

async function tokenValid(cookie) {
let db = await MongoDB.open();

let tokens = db.collection('tokens');
await connect();

let result = await tokens.find({ token: cookie, expires: {$gte: new Date().getTime()}});


return result.toArray();
}

module.exports = {addUserToken, TOKEN_LIFE_SPAN, updateUserToken, tokenValid};
async function getUserIDFromToken(token) {
await connect();

let result = await tokens.findOne({token: token});

return result;
}

module.exports = {addUserToken, TOKEN_LIFE_SPAN, updateUserToken, tokenValid, getUserIDFromToken};
15 changes: 14 additions & 1 deletion gamersnet_backend/persistence/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,17 @@ async function addUser(username, hashedPassword) {
return await users.insertOne({username: username, password: hashedPassword});
}

module.exports = {addUser, getUserByUsername};
async function updateUserPassword(id, hashedPassword) {
await connect();

return await users.findOneAndUpdate(
{_id: id},
{
$set: {
password: hashedPassword
}
}
);
}

module.exports = {addUser, getUserByUsername, updateUserPassword};
20 changes: 2 additions & 18 deletions gamersnet_backend/routes/posts/createPost.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ let ObjectId = require('mongodb').ObjectID;
let {addPost} = require('../../persistence/posts');
let {verifyUserLoggedIn} = require('../utilities/tokenUtility')


// this function handles the /post/createPost/ endpoint
async function createPost(request, response) {
let body = request.body;
let cookie = request.headers.cookie;

let loggedIn = await verifyUserLoggedIn(cookie, body.userID);
let loggedIn = await verifyUserLoggedIn(cookie);

if(body.userID && loggedIn && body.description && body.gameTimeUTC && body.gameName) {

Expand All @@ -26,21 +25,6 @@ async function createPost(request, response) {
} else {
response.status(400).end();
}

}

async function testCookie(request, response) {
let body = request.body;
let cookie = request.headers.cookie;

let loggedIn = await verifyUserLoggedIn('e3db2b14396953cb10607c2c36fbdfee');

if(loggedIn) {
response.status(200).end();
} else {
response.status(404).end();
}

}

module.exports = {createPost, testCookie};
module.exports = {createPost};
11 changes: 1 addition & 10 deletions gamersnet_backend/routes/posts/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
const router = require('express').Router();

// include each route handler
let {createPost, testCookie} = require('./createPost');
let {createPost} = require('./createPost');
let {listAllPosts, listValidPosts} = require('./getPosts');

// specify the routes under /posts/ and pass them off to each function
// check http://localhost:3000/posts to see it working
router.get('/', (req, res) => {
res.json({
'/posts': 'Working!'
});
});

router.post('/createPost', createPost);
router.get('/testCookie', testCookie);
router.get('/listAllPosts', listAllPosts);
router.get('/listValidPosts', listValidPosts);

Expand Down
12 changes: 8 additions & 4 deletions gamersnet_backend/routes/users/authenticate.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ let bcrypt = require('bcrypt');

let {TOKEN_LIFE_SPAN, updateUserToken} = require('../../persistence/tokens');
let {getUserByUsername} = require('../../persistence/users');
let generateToken = require('./generateToken');
let alphaNumericize = require('../utilities/alphaNumericize');
let makeHash = require('../utilities/makeHash');

function verifyUsernameRequirements(username) {
if (username == false) return false;
Expand Down Expand Up @@ -35,11 +36,14 @@ async function authenticate(request, response) {

if (correctPassword) {
// make a new token and update it
let tokenNew = await generateToken(result._id);
await updateUserToken(result._id, tokenNew);
let tokenNew = await makeHash(result._id);
let alphaNumericToken = alphaNumericize(tokenNew);

await updateUserToken(result._id, alphaNumericToken);


// give client token
response.cookie('token', tokenNew, {maxAge: TOKEN_LIFE_SPAN, httpOnly: false});
response.cookie('token', alphaNumericToken, {maxAge: TOKEN_LIFE_SPAN, httpOnly: false});
response.status(204).end();
} else {
response.status(401).end();
Expand Down
50 changes: 50 additions & 0 deletions gamersnet_backend/routes/users/changePassword.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';

let {getUserIDFromToken, updateUserToken, TOKEN_LIFE_SPAN} = require('../../persistence/tokens');
let {updateUserPassword} = require('../../persistence/users');
const alphaNumericize = require('../utilities/alphaNumericize');
let makeHash = require('../utilities/makeHash');
let {verifyUserLoggedIn} = require('../utilities/tokenUtility');

function verifyPasswordRequirements(password) {
if (password == false) return false;

return true;
}

async function changePassword(request, response) {
let body = request.body;
let cookies = request.get('Cookie');

if (!cookies || !verifyPasswordRequirements(body.password)) {
response.status(400).end();

return;
}

let token = cookies.split('=')[1];
let isValid = await verifyUserLoggedIn(token);

if (isValid) {
let hashPassword = await makeHash(body.password);

let tokenDocument = await getUserIDFromToken(token);
let userID = tokenDocument.userID;

await updateUserPassword(userID, hashPassword);

let newToken = await makeHash(userID);
let alphaNumericToken = alphaNumericize(newToken);

await updateUserToken(userID, alphaNumericToken);

response.cookie('token', alphaNumericToken, {maxAge: TOKEN_LIFE_SPAN, httpOnly: false});
response.status(204).end();
} else {
response.status(401).end();
}


}

module.exports = changePassword;
33 changes: 17 additions & 16 deletions gamersnet_backend/routes/users/createAccount.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
'use strict';

let bcrypt = require('bcrypt');

let {addUserToken, TOKEN_LIFE_SPAN} = require('../../persistence/tokens');
let {addUser, getUserByUsername} = require('../../persistence/users');
let generateToken = require('./generateToken');
let alphaNumericize = require('../utilities/alphaNumericize');
let makeHash = require('../utilities/makeHash');

function verifyUsernameRequirements(username) {
if (username == false) return false;
Expand Down Expand Up @@ -34,19 +33,21 @@ async function createAccount(request, response) {

if (validUsername && usernameNotUsed && validPassword) {
// hash password
await bcrypt.hash(body.password, 10, async (error, passwordHash) => {
// add new account to database and get the unique id of inserted account
let result = await addUser(body.username, passwordHash);
let id = result.insertedId;

// create new token and add it to database
let token = await generateToken(id);
await addUserToken(id, token);

// send the client said token
response.cookie('token', token, {maxAge: TOKEN_LIFE_SPAN, httpOnly: false});
response.status(204).end();
});
let hashedPassword = await makeHash(body.password);

// add new account to database and get the unique id of inserted account
let result = await addUser(body.username, hashedPassword);
let id = result.insertedId;

let token = await makeHash(id);
let alphaNumericToken = alphaNumericize(token);

await addUserToken(id, alphaNumericToken);


// send the client said token
response.cookie('token', alphaNumericToken, {maxAge: TOKEN_LIFE_SPAN, httpOnly: false});
response.status(204).end();
} else {
response.status(400).end();
}
Expand Down
19 changes: 0 additions & 19 deletions gamersnet_backend/routes/users/generateToken.js

This file was deleted.

2 changes: 2 additions & 0 deletions gamersnet_backend/routes/users/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ let app = require('express').Router();
// include each route handler
let createAccount = require('./createAccount');
let authenticate = require('./authenticate');
let changePassword = require('./changePassword');

app.post('/createAccount', createAccount)
app.post('/changePassword', changePassword)
app.post('/authenticate', authenticate)

// return the above routes
Expand Down
12 changes: 12 additions & 0 deletions gamersnet_backend/routes/utilities/alphaNumericize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use strict';

let crypto = require('crypto');

function alphaNumericize(input) {
// turn it into an alpha numeric string by computing MD5
let output = crypto.createHash('md5').update(input).digest('hex');

return output;
}

module.exports = alphaNumericize;
13 changes: 13 additions & 0 deletions gamersnet_backend/routes/utilities/makeHash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
'use strict';

let bcrypt = require('bcrypt');

async function makeHash(input) {
let returnHash;

await bcrypt.hash(input.toString(), 10).then((hash) => returnHash = hash);

return returnHash;
}

module.exports = makeHash;
43 changes: 35 additions & 8 deletions gamersnet_frontend/src/components/password/index.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,51 @@
import React from 'react';
import APIFetch from '../../api';

import './styles.css';

export default class Password extends React.Component {
constructor(props) {
super(props);

this.state = {data: ''}
this.state = {newPassword: '', message: ''};

this.inputPassword = this.inputPassword.bind(this);
this.handle = this.handle.bind(this);
}

inputPassword(event) {
this.setState({newPassword: event.target.value});
}

componentDidMount() {
let fetchData = APIFetch('page2');
fetchData.then((data) => {
this.setState({data: JSON.stringify(data)});
});
handle(event) {
this.setState({message: 'sending, pls wait'});

if (this.state.newPassword !== '') {
let body = {password: this.state.newPassword};

let fetchData = APIFetch('/users/changePassword', JSON.stringify(body), 'POST');

fetchData.then(async (data) => {
if (await data.ok) {
this.setState({message: 'password changed successfully'});
} else {
this.setState({message: 'something went wrong'});
}
});
}

event.preventDefault();
}

render() {
return (
<div>
<p>this is page 2 and the server says: {this.state.data}</p>
<div className = 'login-form'>
<p>Change Password</p>
<form onSubmit = {this.handle} autoComplete = 'off' className = 'vertical-center'>
<input type = 'password' onChange = {this.inputPassword} placeholder = 'new password' />
<button onClick = {this.handle}>press me</button>
</form>
<p>{this.state.message}</p>
</div>
);
}
Expand Down
Empty file.