diff --git a/backend/app.js b/backend/app.js index 5bb71635..6d1d71ca 100644 --- a/backend/app.js +++ b/backend/app.js @@ -3,14 +3,17 @@ require('dotenv').config({ path: `${process.env.NODE_ENV}.env` }); require('./utils/aws/awsSetup'); const path = require('path'); +const passport = require('passport'); const log = require('loglevel'); const express = require('express'); const fileUpload = require('express-fileupload'); const cors = require('cors'); const bodyParser = require('body-parser'); +const session = require('express-session'); +const MongoStore = require('connect-mongo'); +const cookieParser = require('cookie-parser'); const { errorHandler } = require('./utils'); -const { requireAuthentication } = require('./middleware/authentication'); const { initDB } = require('./utils/initDb'); const { setResponseHeaders, @@ -24,7 +27,7 @@ const app = express(); app.use(configureHelment()); app.use(setResponseHeaders); app.use(express.static(path.join(__dirname, '../frontend/build'))); -app.use(cors()); +app.use(cors({ credentials: true, origin: 'http://localhost:3000', methods: ['GET', 'POST'] })); app.use( fileUpload({ createParentPath: true, @@ -48,7 +51,40 @@ app.get('/*', (req, res, next) => { } }); -app.use(requireAuthentication); +/** + * This is the secret used to sign the session ID cookie. + * This can be either a string for a single secret, or an array of multiple secrets. + * If an array of secrets is provided, only the first element will be used to sign the session + * ID cookie, while all the elements will be considered when verifying the signature in requests. + * The secret itself should be not easily parsed by a human and would best be a random set of + * characters. + * + * Patients will be logged in a session for 5 minutes, unless they refresh to extend this period. + * maxAge can also be set to null, which keeps a user logged in until the BROWSER is closed. + */ +const sess = { + secret: '3DP4ME', + cookie: { + domain: 'localhost', path: '/', httpOnly: true, secure: false, maxAge: 150000, + }, + resave: false, + saveUninitialized: false, + store: MongoStore.create({ mongoUrl: process.env.DB_URI }), +}; + +if (app.get('env') === 'production') { + app.set('trust proxy', 1); // trust first proxy + sess.cookie.secure = true; // serve secure cookies +} + +app.use(cookieParser()); + +app.use(session(sess)); + +app.use(bodyParser.urlencoded({ extended: false })); +app.use(passport.initialize()); +app.use(passport.session()); + app.use(logRequest); app.use(require('./routes')); diff --git a/backend/middleware/conditionalAuthentication.js b/backend/middleware/conditionalAuthentication.js new file mode 100644 index 00000000..08090810 --- /dev/null +++ b/backend/middleware/conditionalAuthentication.js @@ -0,0 +1,23 @@ +const log = require('loglevel'); + +const { + ERR_AUTH_FAILED, +} = require('../utils/constants'); +const { sendResponse } = require('../utils/response'); + +const { requireAuthentication } = require('./authentication'); +const { requirePatientAuthentication } = require('./verifyPatient'); + +module.exports.requireConditionalAuthentication = async (req, res, next) => { + try { + const user = req?.session?.passport?.user; + if (!user) { + requireAuthentication(req, res, next); + } else { + requirePatientAuthentication(req, res); + } + } catch (error) { + log.error(error); + sendResponse(res, 401, ERR_AUTH_FAILED); + } +}; diff --git a/backend/middleware/patientAuthentication.js b/backend/middleware/patientAuthentication.js new file mode 100644 index 00000000..2ad5a1fa --- /dev/null +++ b/backend/middleware/patientAuthentication.js @@ -0,0 +1,41 @@ +const passport = require('passport'); +const LocalStrategy = require('passport-local').Strategy; +const twofactor = require('node-2fa'); + +const { models } = require('../models'); +const { TWO_FACTOR_WINDOW_MINS } = require('../utils/constants'); + +const verifyPatientToken = (patient, token) => { + const patientSecret = patient.secret; + + if (patient.secret) { + return twofactor.verifyToken(patientSecret, token, TWO_FACTOR_WINDOW_MINS); + } + + return false; +}; + +passport.use('passport-local', new LocalStrategy( + (_id, token, done) => { + models.Patient.findById(_id, (err, user) => { + if (err) { return done(err); } + if (!user) { + return done(null, false, { message: 'No such user exists.' }); + } + if (!(verifyPatientToken(user, token))) { + return done(null, false, { message: 'Incorrect token.' }); + } + return done(null, user); + }); + }, +)); + +passport.serializeUser((user, done) => { + done(null, user._id); +}); + +passport.deserializeUser((_id, done) => { + models.Patient.findById(_id, (err, user) => { + done(err, user); + }); +}); diff --git a/backend/middleware/verifyPatient.js b/backend/middleware/verifyPatient.js new file mode 100644 index 00000000..4ab46af9 --- /dev/null +++ b/backend/middleware/verifyPatient.js @@ -0,0 +1,26 @@ +const log = require('loglevel'); + +const { + ERR_AUTH_FAILED, + ERR_NOT_APPROVED, +} = require('../utils/constants'); +const { sendResponse } = require('../utils/response'); + +/** + * Middleware requires the incoming request to be authenticated. If not authenticated, a response + * is sent back to the client, and the middleware chain is stopped. Authentication is done by + * checking the request for a user, which is automatically attached when Passport logs a user in. + */ +module.exports.requirePatientAuthentication = async (req, res) => { + try { + const user = req?.session?.passport?.user; + if (!user) { + sendResponse(res, 401, ERR_NOT_APPROVED); + } else { + sendResponse(res, 200); + } + } catch (error) { + log.error(error); + sendResponse(res, 401, ERR_AUTH_FAILED); + } +}; diff --git a/backend/package.json b/backend/package.json index 2ce31bed..ff21ac0f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -11,6 +11,8 @@ "axios": "^0.21.2", "babel-eslint": "^10.1.0", "body-parser": "^1.19.0", + "connect-mongo": "^4.6.0", + "cookie-parser": "^1.4.6", "cors": "^2.8.5", "cross-env": "^7.0.3", "del": "^6.0.0", @@ -23,6 +25,7 @@ "express": "^4.17.1", "express-async-errors": "^3.1.1", "express-fileupload": "^1.2.0", + "express-session": "^1.17.2", "faker": "^5.5.3", "fs": "^0.0.1-security", "gulp": "^4.0.2", @@ -40,6 +43,9 @@ "node": "^16.9.1", "node-2fa": "^2.0.2", "omit-deep-lodash": "^1.1.5", + "passport": "^0.5.0", + "passport-local": "^1.0.0", + "passport-session": "^1.0.2", "prettier": "^2.4.1", "superagent-defaults": "^0.1.14", "supertest": "^6.1.3", diff --git a/backend/routes/api/authentication.js b/backend/routes/api/authentication.js index a14637c4..cb75a0b2 100644 --- a/backend/routes/api/authentication.js +++ b/backend/routes/api/authentication.js @@ -1,6 +1,16 @@ const express = require('express'); const twofactor = require('node-2fa'); +const passport = require('passport'); +const accountSid = process.env.ACCOUNT_SID; +const authToken = process.env.TWILIO_AUTH_TOKEN; + +const client = require('twilio')(accountSid, authToken); + +const { + TWILIO_SENDING_NUMBER, + TWILIO_WHATSAPP_PREFIX, +} = require('../../utils/constants'); const { errorWrap } = require('../../utils'); const { models } = require('../../models'); const { sendResponse } = require('../../utils/response'); @@ -8,15 +18,35 @@ const { TWO_FACTOR_WINDOW_MINS } = require('../../utils/constants'); const router = express.Router(); +require('../../middleware/patientAuthentication'); + +router.post('/authenticated/:patientId', passport.authenticate('passport-local'), async (req, res) => { + const { patientId } = req.params; + console.log('ya'); + if (!req.user) { return res.redirect(`/${patientId}`); } + console.log('nah'); + req.logIn(req.user, (err) => { + if (err) { return err; } + console.log(req.session); + // do i need this line? + req.session.save(); + return sendResponse( + res, + 200, + 'Successfully authenticated patient', + ); + }); +}); + /** * Get secret, generate the token, then return the token * If a patient's secret does not already exist, generate a new secret, then also return the token + * Send the patient token through Twilio to Whatsapp */ router.get( '/:patientId', errorWrap(async (req, res) => { const { patientId } = req.params; - const patient = await models.Patient.findById(patientId); if (!patient) { @@ -39,12 +69,14 @@ router.get( const newToken = twofactor.generateToken(patient.secret); - await sendResponse( - res, - 200, - 'New authentication key generated', - newToken, - ); + client.messages + .create({ + body: `Your one time token is ${newToken.token}`, + to: `${TWILIO_WHATSAPP_PREFIX}${patient.phoneNumber}`, + from: TWILIO_SENDING_NUMBER, + }) + .then((message) => console.log(message.sid)) + .catch((err) => console.log(err)); }), ); @@ -76,17 +108,58 @@ router.post( res, 200, 'Patient verified', - isAuthenticated, ); } else { await sendResponse( res, 404, 'Invalid token entered', - isAuthenticated, ); } }), ); +router.get( + '/patient-portal/:patientId', + errorWrap(async (req, res) => { + const { patientId } = req.params; + let patient; + + try { + patient = await models.Patient.findById(patientId); + } catch { + await sendResponse( + res, + 404, + 'An error occurred while checking for patient authentication', + ); + return; + } + + if (!patient) { + await sendResponse( + res, + 404, + 'Invalid patient ID', + ); + return; + } + + if (!req.user) { + await sendResponse( + res, + 404, + 'Session expired', + ); + return; + } + + await sendResponse( + res, + 200, + 'Patient verified', + ); + }), +); + module.exports = router; diff --git a/backend/routes/api/index.js b/backend/routes/api/index.js index 2197a81b..065ff700 100644 --- a/backend/routes/api/index.js +++ b/backend/routes/api/index.js @@ -9,6 +9,6 @@ router.use('/metadata', require('./metadata')); router.use('/users', require('./users')); router.use('/roles', require('./roles')); router.use('/messages', require('./messages')); -router.use('/authentication', require('./authentication')); +router.use('/patient-2fa', require('./authentication')); module.exports = router; diff --git a/backend/routes/api/messages.js b/backend/routes/api/messages.js index ed947e1c..318848c1 100644 --- a/backend/routes/api/messages.js +++ b/backend/routes/api/messages.js @@ -12,6 +12,9 @@ const { TWILIO_RECEIVING_NUMBER, TWILIO_SENDING_NUMBER, } = require('../../utils/constants'); +const { requireAuthentication } = require('../../middleware/authentication'); + +router.use(requireAuthentication); router.post('/sms', async (req, res) => { const phone = req?.body?.WaId; diff --git a/backend/routes/api/metadata.js b/backend/routes/api/metadata.js index f8d1bf03..55bb8901 100644 --- a/backend/routes/api/metadata.js +++ b/backend/routes/api/metadata.js @@ -12,6 +12,8 @@ const { updateStepsInTransaction, getReadableSteps, } = require('../../utils/stepUtils'); +const { requireAuthentication } = require('../../middleware/authentication'); +const { requireConditionalAuthentication } = require('../../middleware/conditionalAuthentication'); /** * Gets the metadata for a step. This describes the fields contained in the steps. @@ -19,6 +21,7 @@ const { */ router.get( '/steps', + requireConditionalAuthentication, errorWrap(async (req, res) => { const metaData = await getReadableSteps(req); @@ -36,7 +39,7 @@ router.get( */ router.post( '/steps', - requireAdmin, + requireAuthentication, requireAdmin, errorWrap(async (req, res) => { try { const stepToCreate = req.body; @@ -62,7 +65,7 @@ router.post( */ router.put( '/steps/', - requireAdmin, + requireAuthentication, requireAdmin, errorWrap(async (req, res) => { try { let stepData = []; @@ -101,7 +104,7 @@ router.put( */ router.delete( '/steps/:stepkey', - requireAdmin, + requireAuthentication, requireAdmin, errorWrap(async (req, res) => { const { stepkey } = req.params; const step = await models.Step.deleteOne({ key: stepkey }); diff --git a/backend/routes/api/patients.js b/backend/routes/api/patients.js index 8438591c..faad3613 100644 --- a/backend/routes/api/patients.js +++ b/backend/routes/api/patients.js @@ -22,12 +22,15 @@ const { isFieldWritable, getWritableFields, } = require('../../utils/fieldUtils'); +const { requireAuthentication } = require('../../middleware/authentication'); +const { requireConditionalAuthentication } = require('../../middleware/conditionalAuthentication'); /** * Returns everything in the patients collection (basic patient info) */ router.get( '/', + requireAuthentication, errorWrap(async (req, res) => { const patientData = await getDataFromModelWithPaginationAndSearch(req, models.Patient); await sendResponse(res, 200, '', patientData); @@ -40,6 +43,7 @@ router.get( router.get( '/count', + requireAuthentication, errorWrap(async (req, res) => { const patientCount = await models.Patient.count(); return sendResponse(res, 200, 'success', patientCount); @@ -52,6 +56,7 @@ router.get( * */ router.get( '/:id', + requireConditionalAuthentication, errorWrap(async (req, res) => { const { id } = req.params; @@ -99,6 +104,7 @@ router.get( */ router.post( '/', + requireAuthentication, removeRequestAttributes(PATIENT_IMMUTABLE_ATTRIBUTES), errorWrap(async (req, res) => { const patient = req.body; @@ -122,6 +128,7 @@ router.post( */ router.put( '/:id', + requireAuthentication, removeRequestAttributes(PATIENT_IMMUTABLE_ATTRIBUTES), errorWrap(async (req, res) => { const { id } = req.params; @@ -146,6 +153,7 @@ router.put( */ router.get( '/:id/files/:stepKey/:fieldKey/:fileName', + requireConditionalAuthentication, errorWrap(async (req, res) => { const { id, stepKey, fieldKey, fileName, @@ -183,6 +191,7 @@ router.get( */ router.delete( '/:id/files/:stepKey/:fieldKey/:fileName', + requireConditionalAuthentication, errorWrap(async (req, res) => { const { id, stepKey, fieldKey, fileName, @@ -245,6 +254,7 @@ router.delete( */ router.post( '/:id/files/:stepKey/:fieldKey/:fileName', + requireConditionalAuthentication, errorWrap(async (req, res) => { // TODO during refactoring: We upload file name in form data, is this even needed??? const { @@ -318,6 +328,7 @@ router.post( */ router.post( '/:id/:stepKey', + requireConditionalAuthentication, removeRequestAttributes(STEP_IMMUTABLE_ATTRIBUTES), errorWrap(async (req, res) => { const { id, stepKey } = req.params; diff --git a/backend/routes/api/roles.js b/backend/routes/api/roles.js index 350c6c7a..68b6db37 100644 --- a/backend/routes/api/roles.js +++ b/backend/routes/api/roles.js @@ -5,6 +5,9 @@ const { errorWrap } = require('../../utils'); const { models } = require('../../models/index'); const { requireAdmin } = require('../../middleware/authentication'); const { sendResponse } = require('../../utils/response'); +const { requireAuthentication } = require('../../middleware/authentication'); + +router.use(requireAuthentication); /** * Returns all roles in the DB. diff --git a/backend/routes/api/steps.js b/backend/routes/api/steps.js index a34812ae..fc972f78 100644 --- a/backend/routes/api/steps.js +++ b/backend/routes/api/steps.js @@ -10,6 +10,9 @@ const { sendResponse, getDataFromModelWithPaginationAndSearch, } = require('../../utils/response'); +const { requireAuthentication } = require('../../middleware/authentication'); + +router.use(requireAuthentication); /** * Returns basic information for all patients that are active in diff --git a/backend/routes/api/users.js b/backend/routes/api/users.js index a84f844b..721aab60 100644 --- a/backend/routes/api/users.js +++ b/backend/routes/api/users.js @@ -17,16 +17,16 @@ const { isRoleValid, } = require('../../utils/roleUtils'); const { requireAdmin } = require('../../middleware/authentication'); -const { - ADMIN_ID, - DEFAULT_USERS_ON_GET_REQUEST, -} = require('../../utils/constants'); +const { ADMIN_ID, DEFAULT_USERS_ON_GET_REQUEST } = require('../../utils/constants'); +const { requireAuthentication } = require('../../middleware/authentication'); +const { requireConditionalAuthentication } = require('../../middleware/conditionalAuthentication'); /** * Gets information about the user making this request. */ router.get( '/self', + requireConditionalAuthentication, errorWrap(async (req, res) => { const isAdmin = req?.user?.roles?.includes(ADMIN_ID) || false; @@ -43,7 +43,7 @@ router.get( */ router.get( '/', - requireAdmin, + requireAuthentication, requireAdmin, errorWrap(async (req, res) => { const { token } = req.query; let nPerPage = req.query.nPerPage ?? DEFAULT_USERS_ON_GET_REQUEST; @@ -78,7 +78,7 @@ router.get( */ router.put( '/:username/roles/:roleId', - requireAdmin, + requireAuthentication, requireAdmin, errorWrap(async (req, res) => { const { username, roleId } = req.params; @@ -109,7 +109,7 @@ router.put( */ router.delete( '/:username/roles/:roleId', - requireAdmin, + requireAuthentication, requireAdmin, errorWrap(async (req, res) => { const { username, roleId } = req.params; @@ -137,7 +137,7 @@ router.delete( */ router.put( '/:username/access/:accessLevel', - requireAdmin, + requireAuthentication, requireAdmin, errorWrap(async (req, res) => { const { username, accessLevel } = req.params; diff --git a/backend/utils/constants.js b/backend/utils/constants.js index eca80140..98b941b3 100644 --- a/backend/utils/constants.js +++ b/backend/utils/constants.js @@ -64,4 +64,6 @@ module.exports.DEFAULT_PATIENTS_ON_GET_REQUEST = 1; module.exports.TWILIO_SENDING_NUMBER = 'whatsapp:+14155238886'; module.exports.TWILIO_RECEIVING_NUMBER = 'whatsapp:+13098319210'; +module.exports.TWILIO_WHATSAPP_PREFIX = 'whatsapp:+'; + module.exports.TWO_FACTOR_WINDOW_MINS = 5; diff --git a/backend/yarn.lock b/backend/yarn.lock index 901df4ac..c0ea8132 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2606,6 +2606,16 @@ asap@^2.0.0: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= +asn1.js@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -3269,6 +3279,11 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" +bn.js@^4.0.0: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + body-parser@1.19.0, body-parser@^1.19.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" @@ -3778,6 +3793,14 @@ confusing-browser-globals@^1.0.10: resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz#30d1e7f3d1b882b25ec4933d1d1adac353d20a59" integrity sha512-gNld/3lySHwuhaVluJUKLePYirM3QNCKzVxqAdhJII9/WXKVX5PURzMVJspS1jTslSqjeuG4KMVTSouit5YPHA== +connect-mongo@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/connect-mongo/-/connect-mongo-4.6.0.tgz#1bf62868efc9f28ecf1459ae9a9d6caaf90ae8a6" + integrity sha512-8new4Z7NLP3CGP65Aw6ls3xDBeKVvHRSh39CXuDZTQsvpeeU9oNMzfFgvqmHqZ6gWpxIl663RyoVEmCAGf1yOg== + dependencies: + debug "^4.3.1" + kruptein "^3.0.0" + content-disposition@0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" @@ -3797,6 +3820,14 @@ convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, dependencies: safe-buffer "~5.1.1" +cookie-parser@^1.4.6: + version "1.4.6" + resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.6.tgz#3ac3a7d35a7a03bbc7e365073a26074824214594" + integrity sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA== + dependencies: + cookie "0.4.1" + cookie-signature "1.0.6" + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -3807,7 +3838,7 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== -cookie@^0.4.0: +cookie@0.4.1, cookie@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== @@ -4061,6 +4092,11 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= +depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" @@ -4668,6 +4704,20 @@ express-fileupload@^1.2.0: dependencies: busboy "^0.3.1" +express-session@^1.17.2: + version "1.17.2" + resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.17.2.tgz#397020374f9bf7997f891b85ea338767b30d0efd" + 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" + express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -6667,6 +6717,13 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +kruptein@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kruptein/-/kruptein-3.0.3.tgz#2073eba5cccbe5ad510b03416950f8a3e2e394c4" + integrity sha512-v5mqSHKS2M1xWUo5V7Q6TMcj1vjTgKWvfspizn6Z939Cmv8NNn5E+Z4LeGBEKDL3yT4pMXaRTjh98oksGTDntA== + dependencies: + asn1.js "^5.4.1" + language-subtag-registry@~0.3.2: version "0.3.21" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" @@ -7034,6 +7091,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -7497,6 +7559,11 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -7689,6 +7756,33 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +passport-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-local/-/passport-local-1.0.0.tgz#1fe63268c92e75606626437e3b906662c15ba6ee" + integrity sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4= + dependencies: + passport-strategy "1.x.x" + +passport-session@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/passport-session/-/passport-session-1.0.2.tgz#28abf357b0958e7c704164a3f539bd08cb9851db" + integrity sha1-KKvzV7CVjnxwQWSj9Tm9CMuYUds= + dependencies: + passport-strategy "1.x.x" + +passport-strategy@1.x.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" + integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= + +passport@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/passport/-/passport-0.5.0.tgz#7914aaa55844f9dce8c3aa28f7d6b73647ee0169" + integrity sha512-ln+ue5YaNDS+fes6O5PCzXKSseY5u8MYhX9H5Co4s+HfYI5oqvnHKoOORLYDUPh+8tHvrxugF2GFcUA1Q1Gqfg== + dependencies: + passport-strategy "1.x.x" + pause "0.0.1" + path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" @@ -7771,6 +7865,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pause@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" + integrity sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10= + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -8014,6 +8113,11 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +random-bytes@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" + integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs= + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -8399,7 +8503,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -8411,7 +8515,7 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -9350,6 +9454,13 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +uid-safe@~2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" + integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== + dependencies: + random-bytes "~1.0.0" + ulid@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/ulid/-/ulid-2.3.0.tgz#93063522771a9774121a84d126ecd3eb9804071f" diff --git a/frontend/package.json b/frontend/package.json index 6f5f3959..463f8997 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "@sweetalert/with-react": "^0.1.1", "@types/date-fns": "^2.6.0", "aws-amplify": "^4.2.9", - "axios": "^0.21.1", + "axios": "^0.24.0", "babel-eslint": "^10.1.0", "compose": "^0.1.2", "cra-template": "1.1.2", diff --git a/frontend/src/App.js b/frontend/src/App.js index b4aa713f..72b11905 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,20 +1,12 @@ -import React, { useEffect, useState } from 'react'; -import { Amplify, Auth } from 'aws-amplify'; +import React from 'react'; +import { Amplify } from 'aws-amplify'; import { registerLocale } from 'react-datepicker'; import { enUS, arSA } from 'date-fns/locale'; import Store from './store/Store'; import AppContent from './AppContent'; -import Login from './pages/Login/Login'; import { awsconfig } from './aws/aws-exports'; import { LANGUAGES } from './utils/constants'; -import { getCurrentUserInfo } from './aws/aws-helper'; -import { - UNDEFINED_AUTH, - AUTHENTICATED, - UNAUTHENTICATED, - setAuthListener, -} from './aws/aws-auth'; // Configure amplify Amplify.configure(awsconfig); @@ -24,56 +16,11 @@ registerLocale(LANGUAGES.EN, enUS); registerLocale(LANGUAGES.AR, arSA); function App() { - const [authLevel, setAuthLevel] = useState(UNDEFINED_AUTH); - const [username, setUsername] = useState(''); - const [userEmail, setUserEmail] = useState(''); - - /** - * Attempts to authenticate the user and get their name/email - */ - useEffect(() => { - const getUserInfo = async () => { - const userInfo = await getCurrentUserInfo(); - setUsername(userInfo?.attributes?.name); - setUserEmail(userInfo?.attributes?.email); - }; - - updateAuthLevel(); - getUserInfo(); - }, []); - - /** - * Checks if the current user is authenticated and updates the auth - * level accordingly - */ - const updateAuthLevel = async () => { - try { - await Auth.currentAuthenticatedUser(); - setAuthLevel(AUTHENTICATED); - } catch (error) { - setAuthLevel(UNAUTHENTICATED); - } - }; - - // We get the auth level at startup, then set a listener to get notified when it changes. - setAuthListener((newAuthLevel) => setAuthLevel(newAuthLevel)); - - // If we're not sure of the user's status, say we're authenticating - if (authLevel === UNDEFINED_AUTH) return
Authenticating User
; - - // If the user is unauthenticated, show login screen - if (authLevel === UNAUTHENTICATED) returnSomething went wrong
; + return ( +Authenticating User
; + + // If the user is unauthenticated, show login screen + if (authLevel === UNAUTHENTICATED) + return ( +Authenticating User
; + + // If the user is unauthenticated, show login screen + if (authLevel === UNAUTHENTICATED) + return ( +