diff --git a/api/auth/auth-middleware.js b/api/auth/auth-middleware.js index ddd8bc4dd7..a465e589fa 100644 --- a/api/auth/auth-middleware.js +++ b/api/auth/auth-middleware.js @@ -1,3 +1,4 @@ +const User = require("../users/users-model") /* If the user does not have a session saved in the server @@ -6,8 +7,12 @@ "message": "You shall not pass!" } */ -function restricted() { - +function restricted(req, res, next) { + if (req.session.user){ + next() + } else{ + next({status: 401, message:"You shall not pass!"}) + } } /* @@ -18,8 +23,21 @@ function restricted() { "message": "Username taken" } */ -function checkUsernameFree() { - +async function checkUsernameFree(req, res, next) { + try{ + const users = await User.findBy({ username: req.body.username}) + if(!users.length) { + req.user = users[0] + next() + } + else{ + next({status: 422, message: "Username taken"}) + } + } + catch(err){ + next(err) + } + } /* @@ -30,8 +48,18 @@ function checkUsernameFree() { "message": "Invalid credentials" } */ -function checkUsernameExists() { - +async function checkUsernameExists(req, res, next) { + try{ + const users = await User.findBy({ username: req.body.username}) + if(users.length) { + next() + } + else{ + next({status: 401, message: "Invalid credentials"}) + } + } catch(err){ + next(err) + } } /* @@ -42,8 +70,18 @@ function checkUsernameExists() { "message": "Password must be longer than 3 chars" } */ -function checkPasswordLength() { - +function checkPasswordLength(req, res, next) { + if(!req.body.password || req.body.password.length < 3 ){ + next({ message: "Password must be longer than 3 chars", status: 422}) + } else{ + next() + } } // Don't forget to add these to the `exports` object so they can be required in other modules +module.exports = { + restricted, + checkUsernameFree, +checkUsernameExists, +checkPasswordLength, +} \ No newline at end of file diff --git a/api/auth/auth-router.js b/api/auth/auth-router.js index ffd7a2e003..3a52c1e367 100644 --- a/api/auth/auth-router.js +++ b/api/auth/auth-router.js @@ -1,5 +1,14 @@ // Require `checkUsernameFree`, `checkUsernameExists` and `checkPasswordLength` // middleware functions from `auth-middleware.js`. You will need them here! +const router = require('express').Router() +const bcrypt = require('bcryptjs') +const User = require('../users/users-model') +const { + checkPasswordLength, + checkUsernameExists, + checkUsernameFree +} = require('./auth-middleware') + /** @@ -24,7 +33,16 @@ "message": "Password must be longer than 3 chars" } */ +router.post('/register', checkPasswordLength, checkUsernameFree,(req, res , next)=> { + const { username, password } = req.body + const hash = bcrypt.hashSync(password, 8) + User.add({ username, password: hash}) + .then(saved => { + res.status(201).json(saved) + }) + .catch(next) +}) /** 2 [POST] /api/auth/login { "username": "sue", "password": "1234" } @@ -42,7 +60,17 @@ } */ +router.post('/login',checkUsernameExists, (req, res, next)=>{ + const { password } = req.body + if(bcrypt.compareSync(password, req.user.password)){ + + req.session.user = req.user + res.json({ message: `Welcome ${req.user.username}`}) + }else{ + next({ status: 401, message: 'Invalid credentials' }) + } +}) /** 3 [GET] /api/auth/logout @@ -58,6 +86,19 @@ "message": "no session" } */ - +router.get('/logout', (req, res, next )=> { + if(req.session.user){ + req.session.destroy(err => { + if(err) { + next(err) + }else{ + res.json({ message: "logged out"}) + } + }) + } else{ + res.json({ message: 'no session'}) + } +}) // Don't forget to add the router to the `exports` object so it can be required in other modules +module.exports = router \ No newline at end of file diff --git a/api/server.js b/api/server.js index bdc628cef2..263dd0bab2 100644 --- a/api/server.js +++ b/api/server.js @@ -1,7 +1,11 @@ const express = require("express"); const helmet = require("helmet"); const cors = require("cors"); - +const usersRouter = require('./users/users-router'); +const authRouter = require('./auth/auth-router') +const session = require('express-session'); +const Store = require('connect-session-knex')(session) +const knex = require('../data/db-config') /** Do what needs to be done to support sessions with the `express-session` package! To respect users' privacy, do NOT send them a cookie unless they log in. @@ -17,12 +21,35 @@ const cors = require("cors"); const server = express(); +server.use(session({ + name:'chocolatechip', + secret: 'shh', + saveUninitialized: false, + resave: false, + store: new Store({ + knex, + createTable: true, + clearInterval: 1000 * 60 * 10, + tableName: 'sessions', + sidfieldname: 'sid', + + }), + cookie: { + maxAge: 1000 * 60 * 10, + secure: false, + httpOnly: true, + } + +})) + server.use(helmet()); server.use(express.json()); server.use(cors()); +server.use('/api/users',usersRouter) +server.use('/api/auth', authRouter) server.get("/", (req, res) => { - res.json({ api: "up" }); + res.json({ api: "api up" }); }); server.use((err, req, res, next) => { // eslint-disable-line diff --git a/api/users/users-model.js b/api/users/users-model.js index 0eb347ce52..bf27cd78e9 100644 --- a/api/users/users-model.js +++ b/api/users/users-model.js @@ -1,29 +1,40 @@ +const db = require('../../data/db-config') + /** resolves to an ARRAY with all users, each user having { user_id, username } */ function find() { - + return db('users').select('user_id', 'username') } /** resolves to an ARRAY with all users that match the filter condition */ function findBy(filter) { - + return db('users').where(filter) } /** resolves to the user { user_id, username } with the given user_id */ function findById(user_id) { - + return db('users') +.select('user_id', 'username') +.where('user_id ', user_id).first() } /** resolves to the newly inserted user { user_id, username } */ -function add(user) { - +async function add(user) { + const [id] = await db('users').insert(user) + return findById(id) } // Don't forget to add these to the `exports` object so they can be required in other modules +module.exports = { + find, + findBy, + findById, + add +} \ No newline at end of file diff --git a/api/users/users-router.js b/api/users/users-router.js index 84aaf5b4be..ac699b2f1c 100644 --- a/api/users/users-router.js +++ b/api/users/users-router.js @@ -1,5 +1,7 @@ // Require the `restricted` middleware from `auth-middleware.js`. You will need it here! - +const router = require("express").Router() +const { restricted } = require("../auth/auth-middleware") +const User = require('./users-model') /** [GET] /api/users @@ -23,6 +25,14 @@ "message": "You shall not pass!" } */ - +router.get('/', restricted, async (req, res , next)=>{ + try{ + const users = await User.find() + res.json(users) + } catch (error){ + next(error) + } + }) // Don't forget to add the router to the `exports` object so it can be required in other modules +module.exports = router \ No newline at end of file diff --git a/data/auth.db3 b/data/auth.db3 index c675b8976d..b843d00bc9 100644 Binary files a/data/auth.db3 and b/data/auth.db3 differ diff --git a/package-lock.json b/package-lock.json index 88b90ba28e..cab21120d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,10 @@ "version": "1.0.0", "dependencies": { "bcryptjs": "^2.4.3", + "connect-session-knex": "^2.1.1", "cors": "^2.8.5", "express": "^4.17.1", + "express-session": "^1.17.2", "helmet": "^4.6.0", "knex": "^0.95.14", "sqlite3": "^5.0.2" @@ -2052,6 +2054,23 @@ "node": ">=8" } }, + "node_modules/connect-session-knex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/connect-session-knex/-/connect-session-knex-2.1.1.tgz", + "integrity": "sha512-gIOqwoU4mWe9uwkWsnBI9KsBr2sYp0IyXX6NJG7oGW6wJjy5CpWufB3FoJPEYb2OqNPMmshr07vS12pcMfok2g==", + "dependencies": { + "bluebird": "^3.7.2", + "knex": "^0.95.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/connect-session-knex/node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -2854,6 +2873,72 @@ "node": ">= 0.10.0" } }, + "node_modules/express-session": { + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", + "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express-session/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express-session/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/express-session/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -5384,6 +5469,14 @@ "node": ">= 0.8" } }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5765,6 +5858,14 @@ "node": ">=0.6" } }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -6770,6 +6871,17 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -8849,6 +8961,22 @@ "xdg-basedir": "^4.0.0" } }, + "connect-session-knex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/connect-session-knex/-/connect-session-knex-2.1.1.tgz", + "integrity": "sha512-gIOqwoU4mWe9uwkWsnBI9KsBr2sYp0IyXX6NJG7oGW6wJjy5CpWufB3FoJPEYb2OqNPMmshr07vS12pcMfok2g==", + "requires": { + "bluebird": "^3.7.2", + "knex": "^0.95.6" + }, + "dependencies": { + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + } + } + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -9472,6 +9600,51 @@ } } }, + "express-session": { + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", + "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", + "requires": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -11412,6 +11585,11 @@ "ee-first": "1.1.1" } }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -11701,6 +11879,11 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -12480,6 +12663,14 @@ "is-typedarray": "^1.0.0" } }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, "undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", diff --git a/package.json b/package.json index 4b1b5792f5..5e53f4d7bc 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,10 @@ }, "dependencies": { "bcryptjs": "^2.4.3", + "connect-session-knex": "^2.1.1", "cors": "^2.8.5", "express": "^4.17.1", + "express-session": "^1.17.2", "helmet": "^4.6.0", "knex": "^0.95.14", "sqlite3": "^5.0.2"