From 0a098d34b6c13aa13708dceb88b41def13b1e5c6 Mon Sep 17 00:00:00 2001 From: jkarenzi Date: Mon, 10 Jun 2024 11:51:52 +0200 Subject: [PATCH] feat(auth): set up authentication and authorization - implement endpoint for user signup and login - implement middleware for authenticating jwt tokens - implement role checking middleware [Delivers #2] --- __tests__/authController.test.ts | 28 ++++++++++++++-------------- __tests__/test.test.ts | 19 +++++-------------- package-lock.json | 22 ++++++++++++++++++++++ package.json | 5 ++++- src/app.ts | 15 +++++++++++++++ src/middleware/authenticateToken.ts | 5 ++--- src/middleware/authorizeAdmin.ts | 3 ++- src/server.ts | 18 +++--------------- 8 files changed, 67 insertions(+), 48 deletions(-) create mode 100644 src/app.ts diff --git a/__tests__/authController.test.ts b/__tests__/authController.test.ts index 6d3ad51..becb2ef 100644 --- a/__tests__/authController.test.ts +++ b/__tests__/authController.test.ts @@ -1,6 +1,6 @@ export {}; const request = require('supertest'); -const app = require('../src/server'); +const app = require('../src/app'); const bcrypt = require('bcrypt'); const { signUpSchema, @@ -27,12 +27,12 @@ describe('Auth Controller Tests', () => { }; const returnedUser = { - _id:"some id", - fullName:"mock user", - email:"mock@gmail.com", - password:"password1234", - createdAt: "some date", - updatedAt: "some date" + _id:'some id', + fullName:'mock user', + email:'mock@gmail.com', + password:'password1234', + createdAt: 'some date', + updatedAt: 'some date' } it('should return a 201 if signup is successful', async () => { @@ -61,12 +61,12 @@ describe('Auth Controller Tests', () => { signUpSchema.validate.mockReturnValueOnce({ error: null }); User.findOne.mockImplementationOnce(() => Promise.resolve({ - _id:"some id", - fullName:"mock user", - email:"mock@gmail.com", - password:"password1234", - createdAt: "some date", - updatedAt: "some date" + _id:'some id', + fullName:'mock user', + email:'mock@gmail.com', + password:'password1234', + createdAt: 'some date', + updatedAt: 'some date' })); const response = await request(app).post('/api/auth/signup').send(signUpFormData); @@ -89,7 +89,7 @@ describe('Auth Controller Tests', () => { bcrypt.compare.mockResolvedValueOnce(true) - jwt.sign.mockResolvedValueOnce("fake token") + jwt.sign.mockResolvedValueOnce('fake token') const response = await request(app).post('/api/auth/login').send(loginFormData); expect(response.status).toBe(200); diff --git a/__tests__/test.test.ts b/__tests__/test.test.ts index 2d6e152..6c5db7f 100644 --- a/__tests__/test.test.ts +++ b/__tests__/test.test.ts @@ -1,20 +1,11 @@ -const { tester } = require('../src/controllers/testController'); +export {} +const request = require('supertest'); +const app = require('../src/app'); -const res: any = {}; - -(res.json = jest.fn((x: Object) => x)), - (res.status = jest.fn((x: number) => res)); - -const req: any = { - body: { - name: 'test', - }, -}; describe('Test', () => { it('should return 200 successful upon testing route', async () => { - await tester(req, res); - - expect(res.status).toHaveBeenCalledWith(200); + const response = await request(app).get('/api/test') + expect(response.status).toBe(200); }); }); diff --git a/package-lock.json b/package-lock.json index 78ffbad..2c1a0b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,11 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^8.2.1", "multer": "^1.4.4", + "node-mailer": "^0.1.1", "supertest": "^7.0.0", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^5.0.0", + "ts-node": "^10.9.2", "ts-node-dev": "^2.0.0", "typescript": "^5.4.2" }, @@ -5580,12 +5582,32 @@ "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, + "node_modules/node-mailer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/node-mailer/-/node-mailer-0.1.1.tgz", + "integrity": "sha512-L3YwTtPodsYr1sNPW/PxXw0rSOr/ldygaIph2YtXDwLGt9l8km/OjM0Wrr57Yf07JEEnDb3wApjhVdR0k5v0kw==", + "deprecated": "node-mailer is not maintained", + "dependencies": { + "nodemailer": ">= 0.1.15" + }, + "engines": { + "node": "*" + } + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, + "node_modules/nodemailer": { + "version": "6.9.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz", + "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", diff --git a/package.json b/package.json index bb74568..8d09872 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "start": "node dist/server.js", "dev": "ts-node-dev src/server.ts", "lint": "eslint . --ext .ts --fix", - "format": "prettier --write ." + "format": "prettier --write .", + "email":"ts-node src/utils/sendEmail.ts" }, "repository": { "type": "git", @@ -33,9 +34,11 @@ "jsonwebtoken": "^9.0.2", "mongoose": "^8.2.1", "multer": "^1.4.4", + "node-mailer": "^0.1.1", "supertest": "^7.0.0", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^5.0.0", + "ts-node": "^10.9.2", "ts-node-dev": "^2.0.0", "typescript": "^5.4.2" }, diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..5da1433 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,15 @@ +export {} +const express = require('express'); +const cors = require('cors'); +const routes = require('./routes/index'); +const swaggerUi = require('swagger-ui-express'); +const swaggerSpec = require('./docs/swaggerconfig'); + +const app = express(); + +app.use(express.json()); +app.use(cors()); +app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); +app.use('/api', routes); + +module.exports = app; \ No newline at end of file diff --git a/src/middleware/authenticateToken.ts b/src/middleware/authenticateToken.ts index e50f9a2..1b6dac0 100644 --- a/src/middleware/authenticateToken.ts +++ b/src/middleware/authenticateToken.ts @@ -17,12 +17,11 @@ const authenticateToken = async ( try { const decoded = await jwt.verify(token, process.env.JWT_SECRET); - //@ts-expect-errors + //@ts-expect-errors still figuring out how to extend request req.user = decoded.user; next(); - } catch (err: any) { - console.log(err.message); + } catch (err) { return res.status(403).json({ status: 'error', message: 'Invalid token' }); } }; diff --git a/src/middleware/authorizeAdmin.ts b/src/middleware/authorizeAdmin.ts index f37a59c..498bef4 100644 --- a/src/middleware/authorizeAdmin.ts +++ b/src/middleware/authorizeAdmin.ts @@ -1,6 +1,7 @@ import { Request, Response, NextFunction } from 'express'; -const authorizeAdmin = (req: any, res: Response, next: NextFunction) => { +const authorizeAdmin = (req: Request, res: Response, next: NextFunction) => { + //@ts-expect-errors still figuring out how to extend request const user = req.user; if (user.role !== 'admin') { return res.status(403).json({ status: 'error', message: 'Forbidden' }); diff --git a/src/server.ts b/src/server.ts index ca85b26..aaf8b21 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,15 +1,7 @@ -const express = require('express'); const mongoose = require('mongoose'); require('dotenv').config(); -const cors = require('cors'); -const routes = require('./routes/index'); -const swaggerUi = require('swagger-ui-express'); -const swaggerSpec = require('./docs/swaggerconfig'); +const app = require('./app') -const app = express(); -app.use(express.json()); -app.use(cors()); -app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); const url = process.env.MONGO_URL as string; const dbName = process.env.DB_NAME; @@ -22,10 +14,6 @@ mongoose console.log(`Server listening at http://localhost:${port}`); }); }) - .catch((err: any) => { + .catch((err:Error) => { console.log(err); - }); - -app.use('/api', routes); - -module.exports = app; + }); \ No newline at end of file