From 9c105b3e0c48c7c9273200fb853294862edcd44c Mon Sep 17 00:00:00 2001 From: harmeetsingh11 Date: Tue, 14 Jan 2025 01:23:15 +0530 Subject: [PATCH 1/7] fix: update incorrect route in auth router API endpoint --- backend/app.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/app.js b/backend/app.js index 2240a583..149c3d5b 100644 --- a/backend/app.js +++ b/backend/app.js @@ -15,10 +15,10 @@ app.use(cookieParser()); // Third-party middleware app.use(cors({ origin: [process.env.CLIENT_URL], credentials: true })); //Third-party middleware // Auth router -app.use('/auth', authRouter); +app.use('/api/auth', authRouter); -app.use('/', (req, res) => { - res.status(200).json({ data: 'JWTauth server ;)' }); -}); +// app.use('/', (req, res) => { +// res.status(200).json({ data: 'JWTauth server ;)' }); +// }); module.exports = app; From f00ade0e5f3b98f27ef8d03c2d5b6c45321499b1 Mon Sep 17 00:00:00 2001 From: harmeetsingh11 Date: Tue, 14 Jan 2025 01:23:46 +0530 Subject: [PATCH 2/7] fix: change logout API request method to POST in authRouter.js --- backend/router/authRoute.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/backend/router/authRoute.js b/backend/router/authRoute.js index 48f66e0e..d134f4e3 100644 --- a/backend/router/authRoute.js +++ b/backend/router/authRoute.js @@ -1,6 +1,6 @@ -const express = require("express"); +const express = require('express'); const authRouter = express.Router(); -const jwtAuth = require("../middleware/jwtAuth.js"); +const jwtAuth = require('../middleware/jwtAuth.js'); const { signUp, @@ -8,14 +8,12 @@ const { forgotPassword, resetPassword, getUser, - logout -} = require("../controller/authController.js"); + logout, +} = require('../controller/authController.js'); -authRouter.post("/signup", signUp); -authRouter.post("/signin", signIn); - - -authRouter.get("/user", jwtAuth, getUser); -authRouter.get("/logout", jwtAuth, logout); +authRouter.post('/signup', signUp); +authRouter.post('/signin', signIn); +authRouter.post('/logout', jwtAuth, logout); +authRouter.get('/user', jwtAuth, getUser); module.exports = authRouter; From 989d5039a4f325ad4f1ba5758f21c3e4c7af29db Mon Sep 17 00:00:00 2001 From: harmeetsingh11 Date: Tue, 14 Jan 2025 01:24:04 +0530 Subject: [PATCH 3/7] fix(jwtStrategy): validate tokens from Authorization header for getUser and logout APIs --- backend/middleware/jwtAuth.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/backend/middleware/jwtAuth.js b/backend/middleware/jwtAuth.js index aeec6509..d701d6c7 100644 --- a/backend/middleware/jwtAuth.js +++ b/backend/middleware/jwtAuth.js @@ -1,24 +1,31 @@ -const JWT = require("jsonwebtoken"); +const JWT = require('jsonwebtoken'); -// router level middleware function +// Middleware to check for a valid JWT token const jwtAuth = (req, res, next) => { - - // get cookie token(jwt token generated using json.sign()) form the request - const token = ( req.cookies?.token) || null; + // Check if the token is in the cookies or the Authorization header + const token = req.cookies?.token || req.headers?.authorization?.split(' ')[1]; // Bearer - // return response if there is no token(jwt token attached with cookie) if (!token) { - return res.status(400).json({ success: false, message: "NOT authorized" }); + return res.status(400).json({ + success: false, + message: 'Not authorized, no token provided', + }); } - // verify the token try { + // Verify the token using the secret key const payload = JWT.verify(token, process.env.SECRET); + + // Attach the payload data to the request object (i.e., user data) req.user = { id: payload.id, email: payload.email }; + + next(); } catch (error) { - return res.status(400).json({ success: false, message: error.message }); + return res.status(400).json({ + success: false, + message: 'Token verification failed: ' + error.message, + }); } - next(); }; module.exports = jwtAuth; From ec603c1b78de2c70f53546f6895b8fbbfc158dbf Mon Sep 17 00:00:00 2001 From: harmeetsingh11 Date: Tue, 14 Jan 2025 01:24:51 +0530 Subject: [PATCH 4/7] fix(authController): correct HTTP status codes, add token in login response, remove console logs and add relevant response messages --- backend/controller/authController.js | 82 ++++++++++++++++------------ 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/backend/controller/authController.js b/backend/controller/authController.js index 4410682b..7da5def8 100644 --- a/backend/controller/authController.js +++ b/backend/controller/authController.js @@ -1,7 +1,7 @@ -const userModel = require("../model/userSchema.js"); -const bcrypt = require("bcrypt"); +const userModel = require('../model/userSchema.js'); +const bcrypt = require('bcrypt'); -const emailValidator = require("email-validator"); +const emailValidator = require('email-validator'); /****************************************************** * @SIGNUP @@ -14,12 +14,12 @@ const emailValidator = require("email-validator"); const signUp = async (req, res, next) => { const { name, email, password, confirmPassword } = req.body; -console.log(name , email,password,confirmPassword) + // console.log(name, email, password, confirmPassword); /// every field is required if (!name || !email || !password || !confirmPassword) { return res.status(400).json({ success: false, - message: "Every field is required" + message: 'Every field is required', }); } @@ -28,7 +28,7 @@ console.log(name , email,password,confirmPassword) if (!validEmail) { return res.status(400).json({ success: false, - message: "Please provide a valid email address 📩" + message: 'Please provide a valid email address 📩', }); } @@ -37,7 +37,7 @@ console.log(name , email,password,confirmPassword) if (password !== confirmPassword) { return res.status(400).json({ success: false, - message: "password and confirm Password does not match ❌" + message: 'password and confirm Password does not match ❌', }); } @@ -48,19 +48,20 @@ console.log(name , email,password,confirmPassword) const result = await userInfo.save(); return res.status(200).json({ success: true, - data: result + message: 'Registered successfully', + data: result, }); } catch (error) { /// send the message of the email is not unique. if (error.code === 11000) { return res.status(400).json({ success: false, - message: `Account already exist with the provided email ${email} 😒` + message: `Account already exist with the provided email ${email} 😒`, }); } return res.status(400).json({ - message: error.message + message: error.message, }); } }; @@ -76,13 +77,13 @@ console.log(name , email,password,confirmPassword) const signIn = async (req, res, next) => { const { email, password } = req.body; - console.log(email,password) + // console.log(email,password) // send response with error message if email or password is missing if (!email || !password) { return res.status(400).json({ success: false, - message: "email and password are required" + message: 'email and password are required', }); } @@ -90,16 +91,16 @@ const signIn = async (req, res, next) => { // check user exist or not const user = await userModel .findOne({ - email + email, }) - .select("+password"); + .select('+password'); // If user is null or the password is incorrect return response with error message if (!user || !(await bcrypt.compare(password, user.password))) { // bcrypt.compare returns boolean value return res.status(400).json({ success: false, - message: "invalid credentials" + message: 'invalid credentials', }); } @@ -108,25 +109,26 @@ const signIn = async (req, res, next) => { user.password = undefined; const cookieOption = { - secure:true, + secure: true, maxAge: 24 * 60 * 60 * 1000, //24hr - httpOnly: true // not able to modify the cookie in client side + httpOnly: true, // not able to modify the cookie in client side }; - res.cookie("token", token, cookieOption); + res.cookie('token', token, cookieOption); res.status(200).json({ success: true, - data: user + message: 'Login successful', + data: user, + token, }); } catch (error) { return res.status(400).json({ success: false, - message: error.message + message: error.message, }); } }; - /****************************************************** * @LOGOUT * @route /api/auth/logout @@ -139,19 +141,22 @@ const logout = async (req, res, next) => { try { const cookieOption = { expires: new Date(Date.now()), // current expiry date - httpOnly: true // not able to modify the cookie in client side + httpOnly: true, // cookie cannot be modified in client-side JS + secure: process.env.NODE_ENV === 'production', // secure cookie if in production + sameSite: 'None', // allows cross-site cookie sharing if needed }; - // return response with cookie without token - res.cookie("token", null, cookieOption); - res.status(200).json({ + // Remove the token from the cookie by setting it to null + res.cookie('token', null, cookieOption); + + return res.status(200).json({ success: true, - message: "Logged Out" + message: 'Logged out successfully', }); } catch (error) { - res.stats(400).json({ + res.status(400).json({ success: false, - message: error.message + message: error.message, }); } }; @@ -165,17 +170,28 @@ const logout = async (req, res, next) => { ******************************************************/ const getUser = async (req, res, next) => { - const userId = req.user.id; try { + // Assuming that `req.user.id` has been set in the jwtAuth middleware + const userId = req.user.id; + + // Find the user by ID from the database (ensure you're using the correct model) const user = await userModel.findById(userId); + + if (!user) { + return res.status(404).json({ + success: false, + message: 'User not found', + }); + } + return res.status(200).json({ success: true, - data: user + data: user, }); } catch (error) { return res.status(400).json({ success: false, - message: error.message + message: error.message, }); } }; @@ -183,8 +199,6 @@ const getUser = async (req, res, next) => { module.exports = { signUp, signIn, - getUser, - - logout + logout, }; From f2a3209ae7b146e0e38fc9af9cd8a43d53faa70b Mon Sep 17 00:00:00 2001 From: harmeetsingh11 Date: Tue, 14 Jan 2025 01:25:22 +0530 Subject: [PATCH 5/7] fix: rename .ev file to .env for proper environment variable access --- backend/backendcode.ev | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 backend/backendcode.ev diff --git a/backend/backendcode.ev b/backend/backendcode.ev deleted file mode 100644 index 6ba66590..00000000 --- a/backend/backendcode.ev +++ /dev/null @@ -1,5 +0,0 @@ -PORT=3000 -MONGODB_URL=mongodb://username:password@cluster0.mongodb.net/retro_db -SECRET=retroApp_SecretKey_2024! -CLIENT_URL=https://vintagevibe.netlify.app/ -GITHUB_REPO=https://github.com/Anjaliavv51/Retro \ No newline at end of file From c92db5c4997da4f4de608464a331115b8f8860f7 Mon Sep 17 00:00:00 2001 From: harmeetsingh11 Date: Tue, 14 Jan 2025 03:05:47 +0530 Subject: [PATCH 6/7] feat(security): implement CSRF protection middleware using csurf and add rate limiting to authentication routes using express-rate-limit --- backend/app.js | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/backend/app.js b/backend/app.js index 149c3d5b..f13cf54f 100644 --- a/backend/app.js +++ b/backend/app.js @@ -5,20 +5,42 @@ const authRouter = require('./router/authRoute.js'); const databaseconnect = require('./config/databaseConfig.js'); const cookieParser = require('cookie-parser'); const cors = require('cors'); +const csrf = require('csurf'); +const rateLimit = require('express-rate-limit'); -// connect to db +// Initialize CSRF Protection +const csrfProtect = csrf({ cookie: true }); + +// Define a rate limiter +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // Limit each IP to 100 requests per windowMs + message: 'Too many requests from this IP, please try again later.', +}); + +// Connect to DB databaseconnect(); -app.use(express.json()); // Built-in middleware -app.use(cookieParser()); // Third-party middleware +// Middleware +app.use(express.json()); +app.use(cookieParser()); +app.use(cors({ origin: [process.env.CLIENT_URL], credentials: true })); -app.use(cors({ origin: [process.env.CLIENT_URL], credentials: true })); //Third-party middleware +// Expose CSRF token to client +app.get('/csrf-token', csrfProtect, (req, res) => { + res.json({ csrfToken: req.csrfToken() }); +}); -// Auth router -app.use('/api/auth', authRouter); +// Auth routes +app.use('/api/auth', csrfProtect, limiter, authRouter); -// app.use('/', (req, res) => { -// res.status(200).json({ data: 'JWTauth server ;)' }); -// }); +// Global error handler +app.use((err, req, res, next) => { + if (err.code === 'EBADCSRFTOKEN') { + res.status(403).json({ error: 'Invalid CSRF token' }); + } else { + res.status(500).json({ error: 'Something went wrong' }); + } +}); module.exports = app; From 6137f3efd705a78c2fe24f3721ce7157cfc86182 Mon Sep 17 00:00:00 2001 From: harmeetsingh11 Date: Tue, 14 Jan 2025 03:06:28 +0530 Subject: [PATCH 7/7] deps: install dependencies for csurf and express rate limit --- backend/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/package.json b/backend/package.json index 9155b03a..4b70ef4c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -15,9 +15,11 @@ "bcrypt": "^5.1.1", "cookie-parser": "^1.4.6", "cors": "^2.8.5", + "csurf": "^1.11.0", "dotenv": "^16.4.5", "email-validator": "^2.0.4", "express": "^4.21.2", + "express-rate-limit": "^7.5.0", "jsonwebtoken": "^9.0.2", "mongoose": "^8.9.3", "nodemon": "^3.1.5"