From 3875261b140a1cd7f7551b0f4bb5540e699ba45a Mon Sep 17 00:00:00 2001 From: Maria Negri Date: Sun, 12 Dec 2021 01:59:10 -0300 Subject: [PATCH] WIP --- README.md | 52 +++++++++ api/auth/auth-middleware.js | 47 ++++++++ api/auth/auth-model.js | 25 ++++ api/auth/auth-router.js | 72 +++++++++++- api/middleware/restricted.js | 24 +++- api/server.js | 15 ++- api/server.test.js | 55 ++++++++- config/.Rhistory | 0 config/index.js | 3 + config/secrets.js | 3 + package-lock.json | 213 +++++++++++++++++++++++++++++++++++ package.json | 3 + 12 files changed, 502 insertions(+), 10 deletions(-) create mode 100644 api/auth/auth-middleware.js create mode 100644 api/auth/auth-model.js create mode 100644 config/.Rhistory create mode 100644 config/index.js create mode 100644 config/secrets.js diff --git a/README.md b/README.md index 2457dedc0..1417bc459 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,58 @@ Your finished project must include all of the following requirements (further in Be prepared to demonstrate your understanding of this week's concepts by answering questions on the following topics. 1. Differences between using _sessions_ or _JSON Web Tokens_ for authentication. + +Sessions provide a way to persist data across requests. For example, we'll use them to save authentication information, so there is no need to re-enter credentials on every new request the client makes to the server. When using sessions, each client will have a unique session stored on the server. In ordee to transmit authentication information between the client and server. For that, we'll use cookies. + +Every HTTP message has two main parts: the headers and the body. To send cookies, the server adds the Set-Cookie header to the response like so: "Set-Cookie": "session=12345". The browser will read the header and save a cookie called session with the value 12345 in this example +The browser will add the "Cookie": "session=12345" header on every subsequent request and the server. Cookies are not accessible from JavaScript or anywhere because they are cryptographically signed and very secure. + + +JSON Web Tokens (JWT) are a way to transmit information between parties in the form of a JSON object. The JSON information is most commonly used for authentication and information exchange. + +When you design a backend application, if it is well designed, the application will be stateless. Especially when you have millions of users, you want to be able to horizontally scale your application. Horizontally scaling means being able to scale by adding servers. Vertically scaling means taking an existing server and adding capacity/computing power/space to it + +Horizontal scaling generates some challenges related to your application design. If you are keeping session data in memory, then every request from a user would have to go back to the same server in order to validate the session id. You can generate sticky sessions, which allows that your requests always go to the same server until your session has expired. You can do that, but you have to plan for that. + +When you have an application, there is always state (variables a and their values). The question is where you store that state. A stateless application means that the state is not going to be stored in the application (disk, database, memory). The application doesn’t keep track of it. + +By keeping the state in the token, every time the client makes a request, the state that is in the token is sent to the application. The application needs a way to validate. + + 2. What does `bcryptjs` do to help us store passwords in a secure manner? + +Authentication is the process by which our Web API verifies a client's identity that is trying to access a resource. +Some of the things we need to take into account when implementing authentication are: +• Password storage. +• Password strength. +• Brute-force safeguards. + +Regarding password storage, there are the two main options: encryption and hashing. +• Encryption goes two ways. First, it utilizes plain text and private keys to generate encrypted passwords and then reverses the process to match an original password. +• Cryptographic hashes only go one way: parameters + input = hash. It is pure; given the same parameters and input, it generates the same hash. +Suppose the database of users and keys is compromised. In that case, it is possible to decrypt the passwords to their original values, which is bad because users often share passwords across different sites. This is one reason why cryptographic hashing is the preferred method for storing user passwords. + +A common way that attackers circumvent hashing algorithms is by pre-calculating hashes for all possible character combinations up to a particular length using common hashing techniques. + +In order to slow down hackers' ability to get at a user's password. To do so, we will add time to our security algorithm to produce what is known as a key derivation function. +[Hash] + [Time] = [Key Derivation Function]. + +Instead of writing our key derivation function, we use a well-known and popular module called bcryptjs. Bcryptjs features include: +• password hashing function +• implements salting both manually and automatically. +• accumulative hashing rounds + +Having an algorithm that hashes the information multiple times (rounds) means an attacker needs to have the hash, know the algorithm used, and how many rounds were used to generate the hash in the first place. + + 3. How are unit tests different from integration and end-to-end testing? + +Unit tests are where we isolate smaller units of software (often functions or methods). There are usually many unit tests in a codebase, and because these tests are meant to be run often, they need to run fast. + +End to end testing (E2E testing) refers to a software testing method that involves testing an application's workflow from beginning to end. This method basically aims to replicate real user scenarios so that the system can be validated for integration and data integrity. + 4. How does _Test Driven Development_ change the way we write applications and tests? + +TDD allows writing smaller code having single responsibility rather than monolithic procedures with multiple responsibilities. This makes the code simpler to understand. TDD also forces to write only production code to pass tests based on user requirements. + +As a result, unit tests are the preferred tool for test driven development (TDD). Developers regularly use them to test correctness in units of code (usually functions). \ No newline at end of file diff --git a/api/auth/auth-middleware.js b/api/auth/auth-middleware.js new file mode 100644 index 000000000..739a28ea8 --- /dev/null +++ b/api/auth/auth-middleware.js @@ -0,0 +1,47 @@ +const User = require('./auth-model.js') + +const checkUserExists = async(req, res, next ) => { + try{ + const { username } = req.body + const newUser = await User.findBy({username}) + if(newUser[0]){ + next({ + status: 422, + message:'The username is already taken'}) + } else { + next() + } + } catch(err){ + next(err) + } +} + + + +const validateBody = async(req, res, next ) => { + try{ + + const { username , password } = req.body + if(!username || !password || + typeof password !== 'string' || + !password.trim() || + !username.trim() ) { + next({ + status: 400, + message: 'username and password required' + }) + + } else { + next() + } + + } catch(err){ + next(err) + } +} + + +module.exports = { +checkUserExists, +validateBody +} \ No newline at end of file diff --git a/api/auth/auth-model.js b/api/auth/auth-model.js new file mode 100644 index 000000000..6aca3d216 --- /dev/null +++ b/api/auth/auth-model.js @@ -0,0 +1,25 @@ +const db = require('../../data/dbConfig'); + +function findBy(filter) { + return db('users') + .select('id', 'username', 'password') + .where(filter) +} + +function findById(id) { + return db('users') + .select('id','username','password') + .where("id", id) + .first() +} + +async function add({ username, password }) { + const [id] = await db('users').insert({ username, password }) + return findById(id) +} + +module.exports = { + findBy, + findById, + add, +}; \ No newline at end of file diff --git a/api/auth/auth-router.js b/api/auth/auth-router.js index 47d8e51ae..1e78f466e 100644 --- a/api/auth/auth-router.js +++ b/api/auth/auth-router.js @@ -1,7 +1,35 @@ const router = require('express').Router(); +const jwt = require("jsonwebtoken") // creates token +const secrets = require('../../config/secrets') +const User = require('./auth-model.js') -router.post('/register', (req, res) => { - res.end('implement register, please!'); +const bcrypt = require('bcryptjs') // creates hash +const { BCRYPT_ROUNDS } = require('../../config') + +const { checkUserExists, validateBody } = require('./auth-middleware') + + + +router.post('/register',checkUserExists,validateBody, async (req, res, next) => { + + try { + const { username , password} = req.body + + // bcrypting the password before saving + const hash = bcrypt.hashSync(password, BCRYPT_ROUNDS) + // never save the plain text password in the db + const user = { 'username': username , 'password':hash} + + const NewUser = await User.add(user) + res.status(201).json({'id': NewUser.id, + 'username': NewUser.username, + 'password': NewUser.password, + }) + } catch(err) { + next(err) + } +}); + //res.end('implement register, please!'); /* IMPLEMENT You are welcome to build additional middlewares to help with the endpoint's functionality. @@ -27,10 +55,28 @@ router.post('/register', (req, res) => { 4- On FAILED registration due to the `username` being taken, the response body should include a string exactly as follows: "username taken". */ + + +router.post('/login', validateBody,async (req, res, next) => { + const { username , password } = req.body + const newUser = await User.findBy({username}) + const user = newUser[0] + if(user && bcrypt.compareSync(password, user.password) ){ + // generate a token and include it in the response + const token = generateToken(user) + res.status(200).json({ + message :`welcome, ${user.username}`, + token: token + }) + } else { + next({ status: 401, message: 'invalid credentials' }) + } + }); -router.post('/login', (req, res) => { - res.end('implement login, please!'); + + + //res.end('implement login, please!'); /* IMPLEMENT You are welcome to build additional middlewares to help with the endpoint's functionality. @@ -54,6 +100,22 @@ router.post('/login', (req, res) => { 4- On FAILED login due to `username` not existing in the db, or `password` being incorrect, the response body should include a string exactly as follows: "invalid credentials". */ -}); + + +function generateToken (user) { + const payload = { + subject: user.id, + username: user.username + } + + const options = { + expiresIn: '1d', + } + const secret = secrets.jwtSecret + + return jwt.sign(payload,secret,options) + +} + module.exports = router; diff --git a/api/middleware/restricted.js b/api/middleware/restricted.js index a690e961e..478dbed30 100644 --- a/api/middleware/restricted.js +++ b/api/middleware/restricted.js @@ -1,5 +1,24 @@ -module.exports = (req, res, next) => { - next(); +const jwt = require("jsonwebtoken") +const secrets = require('../../config/secrets') + +module.exports = async (req, res, next) => { + const token = req.headers.authorization + console.log('token',token ) + + if(token){ // Validate token + jwt.verify(token,secrets.jwtSecret ,(err,decodedToken )=>{ + if(err) { + next({ status: 401, message:"token invalid"}) + } else { + req.decodedJwt = decodedToken // return decoded token + next() + } + }) + } else { + res.status(401).json({message: "token required"}) + } + + /* IMPLEMENT @@ -12,3 +31,4 @@ module.exports = (req, res, next) => { the response body should include a string exactly as follows: "token invalid". */ }; + diff --git a/api/server.js b/api/server.js index 33320b871..b17f165af 100644 --- a/api/server.js +++ b/api/server.js @@ -1,7 +1,7 @@ const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); - +const path = require('path') const restrict = require('./middleware/restricted.js'); const authRouter = require('./auth/auth-router.js'); @@ -13,7 +13,20 @@ server.use(helmet()); server.use(cors()); server.use(express.json()); +server.use(express.static(path.join(__dirname, '../client'))) server.use('/api/auth', authRouter); server.use('/api/jokes', restrict, jokesRouter); // only logged-in users should have access! +server.get('/', (req, res) => { + res.sendFile(path.join(__dirname, '../client', 'index.html')) +}) + +server.use((err, req, res, next) => { // eslint-disable-line + res.status(err.status || 500).json({ + message: err.message, + stack: err.stack, + }) +}) + + module.exports = server; diff --git a/api/server.test.js b/api/server.test.js index 96965c559..57acc047e 100644 --- a/api/server.test.js +++ b/api/server.test.js @@ -1,4 +1,55 @@ -// Write your tests here +const server = require('./server'); +const request = require('supertest'); +const db = require('../data/dbConfig'); + test('sanity', () => { - expect(true).toBe(false) + expect(true).toBe(true) +}); + +beforeAll(async () => { + await db.migrate.rollback() + await db.migrate.latest() +}); + +afterAll(async () => { + await db.destroy() }) + + + beforeEach(async () => { + await request(server).post('/api/auth/register') + .send({ + username: "Brody", + password: "youngCrazedPeeling" + }) + }) + +describe('[POST] /register', () => { + test('causes a user to be added to the database', async () => { + const users = await db('users') + expect(users).toHaveLength(1) + }) + test('responds with a newly created user', async () => { + const users = await db('users') + expect(users[0].username).toEqual("Brody") + }) +}) + +describe('[POST] /login', () => { + let login + beforeEach(async () => { + login = await request(server).post('/api/auth/login') + .send({ + username: "Brody", + password: "youngCrazedPeeling" + }) + }) + + test('allows a user to login', async () => { + expect(login.text).toMatch('token') + }) + + test('responds with a greeting to logged in user', async () => { + expect(login.text).toMatch('welcome, Brody') + }) +}) \ No newline at end of file diff --git a/config/.Rhistory b/config/.Rhistory new file mode 100644 index 000000000..e69de29bb diff --git a/config/index.js b/config/index.js new file mode 100644 index 000000000..2cc988e8f --- /dev/null +++ b/config/index.js @@ -0,0 +1,3 @@ +module.exports = { + BCRYPT_ROUNDS: process.env.BCRYPT_ROUNDS || 8 +} diff --git a/config/secrets.js b/config/secrets.js new file mode 100644 index 000000000..250ec059a --- /dev/null +++ b/config/secrets.js @@ -0,0 +1,3 @@ +module.exports = { + jwtSecret: process.env.JWT_SECRET || "shh" +} diff --git a/package-lock.json b/package-lock.json index 65bb32219..017ec6397 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,12 @@ "version": "0.0.1", "license": "ISC", "dependencies": { + "bcryptjs": "^2.4.3", "cors": "2.8.5", + "dotenv": "^10.0.0", "express": "4.17.1", "helmet": "4.6.0", + "jsonwebtoken": "^8.5.1", "knex": "0.95.14", "sqlite3": "5.0.2" }, @@ -1541,6 +1544,11 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1711,6 +1719,11 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -2370,6 +2383,14 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", + "engines": { + "node": ">=10" + } + }, "node_modules/duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -2386,6 +2407,14 @@ "safer-buffer": "^2.1.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4603,6 +4632,35 @@ "node": ">=6" } }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/jsprim": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", @@ -4618,6 +4676,25 @@ "node": ">=0.6.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -4757,12 +4834,47 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "node_modules/lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -8417,6 +8529,11 @@ "tweetnacl": "^0.14.3" } }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -8551,6 +8668,11 @@ "node-int64": "^0.4.0" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -9061,6 +9183,11 @@ "is-obj": "^2.0.0" } }, + "dotenv": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", + "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -9077,6 +9204,14 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -10794,6 +10929,30 @@ "minimist": "^1.2.5" } }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, "jsprim": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", @@ -10806,6 +10965,25 @@ "verror": "1.10.0" } }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -10895,12 +11073,47 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", diff --git a/package.json b/package.json index 949da24d5..dd57d4af7 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,12 @@ }, "license": "ISC", "dependencies": { + "bcryptjs": "^2.4.3", "cors": "2.8.5", + "dotenv": "^10.0.0", "express": "4.17.1", "helmet": "4.6.0", + "jsonwebtoken": "^8.5.1", "knex": "0.95.14", "sqlite3": "5.0.2" },