diff --git a/.env b/.env index 52324f0..6ead5e8 100644 --- a/.env +++ b/.env @@ -1,3 +1,5 @@ PORT=5005 NODE_ENV=development -VERSION=1.7.4 \ No newline at end of file +VERSION=1.7.4 +PASSWORD=flame +SECRET=e02eb43d69953658c6d07311d6313f2d4467672cb881f96b29368ba1f3f4da4b \ No newline at end of file diff --git a/controllers/auth/index.js b/controllers/auth/index.js new file mode 100644 index 0000000..8341f7c --- /dev/null +++ b/controllers/auth/index.js @@ -0,0 +1,3 @@ +module.exports = { + login: require('./login'), +}; diff --git a/controllers/auth/login.js b/controllers/auth/login.js new file mode 100644 index 0000000..07029c9 --- /dev/null +++ b/controllers/auth/login.js @@ -0,0 +1,25 @@ +const asyncWrapper = require('../../middleware/asyncWrapper'); +const ErrorResponse = require('../../utils/ErrorResponse'); +const signToken = require('../../utils/signToken'); + +// @desc Login user +// @route POST /api/auth/ +// @access Public +const login = asyncWrapper(async (req, res, next) => { + const { password, duration } = req.body; + + const isMatch = process.env.PASSWORD == password; + + if (!isMatch) { + return next(new ErrorResponse('Invalid credentials', 401)); + } + + const token = signToken(duration); + + res.status(200).json({ + success: true, + data: { token }, + }); +}); + +module.exports = login; diff --git a/middleware/requireBody.js b/middleware/requireBody.js new file mode 100644 index 0000000..ae1da6b --- /dev/null +++ b/middleware/requireBody.js @@ -0,0 +1,13 @@ +const ErrorResponse = require('../utils/ErrorResponse'); + +const requireBody = (keys) => (req, res, next) => { + const missing = keys.filter((key) => !Object.keys(req.body).includes(key)); + + if (missing.length) { + return next(new ErrorResponse(`'${missing[0]}' is required`, 400)); + } + + next(); +}; + +module.exports = requireBody; diff --git a/package-lock.json b/package-lock.json index 0b3a7a5..69d2e4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -498,6 +498,11 @@ "fill-range": "^7.0.1" } }, + "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", @@ -999,6 +1004,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", @@ -1680,6 +1693,30 @@ "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-0.19.0.tgz", "integrity": "sha512-GSVwsrzW9LsA5lzsqe4CkuZ9wp+kxBb2GwNniaWzI2YFn5Ig42rSW8ZxVpWXaAfakXNrx5pgY5AbQq7kzX29kg==" }, + "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": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -1691,6 +1728,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", @@ -1714,6 +1770,41 @@ "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.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "long-timeout": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", diff --git a/package.json b/package.json index d85ab5f..50983b1 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "concurrently": "^6.3.0", "dotenv": "^10.0.0", "express": "^4.17.1", + "jsonwebtoken": "^8.5.1", "multer": "^1.4.3", "node-schedule": "^2.0.0", "sequelize": "^6.9.0", diff --git a/routes/auth.js b/routes/auth.js new file mode 100644 index 0000000..cfd4771 --- /dev/null +++ b/routes/auth.js @@ -0,0 +1,9 @@ +const express = require('express'); +const router = express.Router(); + +const { login } = require('../controllers/auth'); +const requireBody = require('../middleware/requireBody'); + +router.route('/').post(requireBody(['password', 'duration']), login); + +module.exports = router; diff --git a/utils/signToken.js b/utils/signToken.js new file mode 100644 index 0000000..4df3d85 --- /dev/null +++ b/utils/signToken.js @@ -0,0 +1,8 @@ +const jwt = require('jsonwebtoken'); + +const signToken = (expiresIn) => { + const token = jwt.sign({ app: 'flame' }, process.env.SECRET, { expiresIn }); + return token; +}; + +module.exports = signToken;