From bfe21443d5c95df7e328dc392871473cc3933d1d Mon Sep 17 00:00:00 2001 From: LONECODER1 Date: Tue, 21 Oct 2025 22:10:10 +0530 Subject: [PATCH 1/3] feat:Add JWT-based authentication middleware to protect APIs --- backend/.env.example | 17 ++ backend/package-lock.json | 199 ++++++++++++++++++++++- backend/package.json | 17 +- backend/src/controllers/auth.ts | 50 ++++++ backend/src/middleware/authMiddleware.ts | 41 +++++ backend/src/middleware/roleMiddleWare.ts | 16 ++ backend/src/server.ts | 117 +++++++++++++ backend/src/types.d.ts | 5 + package-lock.json | 129 +++++++++++++++ package.json | 1 + 10 files changed, 583 insertions(+), 9 deletions(-) create mode 100644 backend/src/controllers/auth.ts create mode 100644 backend/src/middleware/authMiddleware.ts create mode 100644 backend/src/middleware/roleMiddleWare.ts create mode 100644 backend/src/server.ts create mode 100644 backend/src/types.d.ts diff --git a/backend/.env.example b/backend/.env.example index e69de29..acbb71c 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -0,0 +1,17 @@ + +# --- JWT Configuration --- +# Secret key for signing access tokens. Should be a long, random string. +JWT_SECRET=your_jwt_secret_key_here + +# Secret key for signing refresh tokens. Should be a long, random string, different from JWT_SECRET. +JWT_REFRESH_SECRET=your_jwt_refresh_secret_key_here + +# Access token expiration time (e.g., 10m, 1h, 1d) +ACCESS_TOKEN_EXPIRY=10m + +# Refresh token expiration time (e.g., 7d, 30d) +REFRESH_TOKEN_EXPIRY=7d + +# --- Server Configuration --- +# Port for the server to run on +PORT=4000 \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json index bb92734..f5c3687 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -9,19 +9,26 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "bcryptjs": "^3.0.2", "body-parser": "^1.20.2", + "cookie-parser": "^1.4.7", "cors": "^2.8.5", - "dotenv": "^16.3.1", - "express": "^4.18.2", + "dotenv": "^16.6.1", + "express": "^4.21.2", + "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0" }, "devDependencies": { + "@types/bcryptjs": "^2.4.6", "@types/body-parser": "^1.19.2", + "@types/cookie-parser": "^1.4.9", "@types/cors": "^2.8.13", - "@types/express": "^4.17.21", + "@types/express": "^4.17.23", + "@types/jsonwebtoken": "^9.0.10", "@types/morgan": "^1.9.7", + "ts-node": "^10.9.2", "ts-node-dev": "^2.0.0", - "typescript": "^5.2.2" + "typescript": "^5.9.3" } }, "node_modules/@cspotcode/source-map-support": { @@ -93,6 +100,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -114,6 +128,16 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.9.tgz", + "integrity": "sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, "node_modules/@types/cors": { "version": "2.8.19", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", @@ -157,6 +181,17 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -174,6 +209,13 @@ "@types/node": "*" } }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "24.7.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.7.2.tgz", @@ -336,6 +378,15 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, + "node_modules/bcryptjs": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", + "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -397,6 +448,12 @@ "node": ">=8" } }, + "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": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -504,6 +561,28 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -604,6 +683,15 @@ "xtend": "^4.0.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==", + "license": "Apache-2.0", + "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", @@ -1015,6 +1103,97 @@ "node": ">=0.12.0" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "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": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -1403,6 +1582,18 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", diff --git a/backend/package.json b/backend/package.json index acdf5a3..cea2130 100644 --- a/backend/package.json +++ b/backend/package.json @@ -11,18 +11,25 @@ "author": "", "license": "MIT", "dependencies": { + "bcryptjs": "^3.0.2", "body-parser": "^1.20.2", + "cookie-parser": "^1.4.7", "cors": "^2.8.5", - "dotenv": "^16.3.1", - "express": "^4.18.2", + "dotenv": "^16.6.1", + "express": "^4.21.2", + "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0" }, "devDependencies": { + "@types/bcryptjs": "^2.4.6", "@types/body-parser": "^1.19.2", + "@types/cookie-parser": "^1.4.9", "@types/cors": "^2.8.13", - "@types/express": "^4.17.21", + "@types/express": "^4.17.23", + "@types/jsonwebtoken": "^9.0.10", "@types/morgan": "^1.9.7", + "ts-node": "^10.9.2", "ts-node-dev": "^2.0.0", - "typescript": "^5.2.2" + "typescript": "^5.9.3" } -} \ No newline at end of file +} diff --git a/backend/src/controllers/auth.ts b/backend/src/controllers/auth.ts new file mode 100644 index 0000000..bd6f4de --- /dev/null +++ b/backend/src/controllers/auth.ts @@ -0,0 +1,50 @@ +import jwt, { SignOptions, JwtPayload } from "jsonwebtoken"; +import dotenv from "dotenv"; +dotenv.config(); + +if (!process.env.JWT_SECRET || !process.env.JWT_REFRESH_SECRET) { + throw new Error("JWT secrets missing in environment variables"); +} + +const accessSecret = process.env.JWT_SECRET as string; +const refreshSecret = process.env.JWT_REFRESH_SECRET as string; + +// Define your payload type +export interface TokenPayload extends JwtPayload { + sub: string; + username?: string; + role?: string; + //role?:"user"|"admin"; // Example roles +} + +export function signAccessToken(payload: TokenPayload) { + const options: SignOptions = { expiresIn: process.env.ACCESS_TOKEN_EXPIRY as any }; + + // --- CRITICAL FIX: Create a clean payload copy --- + const cleanPayload = { ...payload }; + delete cleanPayload.iat; // Remove "Issued At" claim + delete cleanPayload.exp; // Remove "Expiration" claim to allow options.expiresIn to work + // --- END FIX --- + + return jwt.sign(cleanPayload, accessSecret, options); +} + +export function signRefreshToken(payload: TokenPayload) { + const options: SignOptions = { expiresIn: process.env.REFRESH_TOKEN_EXPIRY as any }; + + // --- CRITICAL FIX: Create a clean payload copy --- + const cleanPayload = { ...payload }; + delete cleanPayload.iat; // Remove "Issued At" claim + delete cleanPayload.exp; // Remove "Expiration" claim to allow options.expiresIn to work + // --- END FIX --- + + return jwt.sign(cleanPayload, refreshSecret, options); +} + +export function verifyAccessToken(token: string): TokenPayload { + return jwt.verify(token, accessSecret) as TokenPayload; +} + +export function verifyRefreshToken(token: string): TokenPayload { + return jwt.verify(token, refreshSecret) as TokenPayload; +} \ No newline at end of file diff --git a/backend/src/middleware/authMiddleware.ts b/backend/src/middleware/authMiddleware.ts new file mode 100644 index 0000000..9368ec5 --- /dev/null +++ b/backend/src/middleware/authMiddleware.ts @@ -0,0 +1,41 @@ +import { Request, Response, NextFunction } from 'express'; +import { verifyAccessToken } from '../controllers/auth'; + +export interface AuthenticatedRequest extends Request { + user?: any; +} + +export type AuthRequest = AuthenticatedRequest; + +// (Middleware file - authMiddleware.ts) + +export function authenticate(req: AuthenticatedRequest, res: Response, next: NextFunction) { + const header = req.headers.authorization; + const token = header?.startsWith('Bearer ') ? header.slice(7) : undefined; + + if (!token) { + // Use 401 for NO credentials (token missing entirely) + return res.status(401).json({ error: 'No token provided' }); + } + + try { + const payload = verifyAccessToken(token); + req.user = payload; + next(); + } catch { + // Use 403 for INVALID credentials (token present but invalid/expired/rejected) + // This is often seen as a better status for expired tokens. + return res.status(403).json({ error: 'Invalid or expired token' }); + } +} +export function authorizeRole(role: string) { + return (req: AuthenticatedRequest, res: Response, next: NextFunction) => { + if (!req.user) { + return res.status(401).json({ error: 'Not authenticated' }); + } + if (req.user.role !== role) { + return res.status(403).json({ error: 'Forbidden' }); + } + next(); + }; +} diff --git a/backend/src/middleware/roleMiddleWare.ts b/backend/src/middleware/roleMiddleWare.ts new file mode 100644 index 0000000..2580d7e --- /dev/null +++ b/backend/src/middleware/roleMiddleWare.ts @@ -0,0 +1,16 @@ +import { Response, NextFunction } from "express"; +import { AuthRequest } from "./authMiddleware"; + +export const authorizeRoles = (...allowedRoles: string[]) => { + return (req: AuthRequest, res: Response, next: NextFunction) => { + if (!req.user) { + return res.status(401).json({ error: "Unauthorized" }); + } + + const { role } = req.user; + if (!allowedRoles.includes(role)) { + return res.status(403).json({ error: "Forbidden: insufficient privileges" }); + } + next(); + }; +}; diff --git a/backend/src/server.ts b/backend/src/server.ts new file mode 100644 index 0000000..307837f --- /dev/null +++ b/backend/src/server.ts @@ -0,0 +1,117 @@ +import express from "express"; +import dotenv from "dotenv"; +import bcrypt from "bcryptjs"; +import cookieParser from "cookie-parser"; +import { signAccessToken, signRefreshToken, verifyRefreshToken } from "./controllers/auth"; +import { authenticate, AuthRequest } from "./middleware/authMiddleware"; + +dotenv.config(); + +const app = express(); +app.use(express.json()); +app.use(cookieParser()); + + +interface User { + id: string; + username: string; + passwordHash: string; + role: string; +} + +const users: User[] = [ + { id: "1", username: "alice", passwordHash: bcrypt.hashSync("password", 8), role: "admin" }, +]; + +const refreshTokens = new Map() + +app.post("/login", async (req, res) => { + const { username, password } = req.body; + const user = users.find((u) => u.username === username); + if (!user) return res.status(401).json({ error: "Invalid credentials" }); + + const match = await bcrypt.compare(password, user.passwordHash); + if (!match) return res.status(401).json({ error: "Invalid credentials" }); + + const payload = { sub: user.id, username: user.username, role: user.role }; + const accessToken = signAccessToken(payload); + const refreshToken = signRefreshToken(payload); + + refreshTokens.set(user.id, refreshToken); + + res.cookie("refreshToken", refreshToken, { + httpOnly: true, + sameSite: "strict", + secure: process.env.NODE_ENV === "production", + maxAge: 7 * 24 * 60 * 60 * 1000, + }); + + res.json({ accessToken }); +}); + +app.post("/refresh", (req, res) => { + const token = req.cookies?.refreshToken || req.body.refreshToken; + + if (!token) { + return res.status(401).json({ error: "Refresh token is missing" }); + } + + try { + const payload = verifyRefreshToken(token); + const storedToken = refreshTokens.get(payload.sub); + + console.log("--- REFRESH TOKEN COMPARISON DEBUG ---"); + console.log(`User ID (sub): ${payload.sub}`); + console.log(`Token from request (RT): ${token}`); + console.log(`Stored token from Map: ${storedToken}`); + console.log(`Tokens are strictly equal (storedToken === token): ${storedToken === token}`); + console.log(`Type of request token: ${typeof token}`); + console.log(`Type of stored token: ${typeof storedToken}`); + console.log("--------------------------------------"); + + if (!storedToken) { + return res.status(401).json({ error: "Session not found or already logged out" }); + } + + if (storedToken !== token) { + return res.status(401).json({ error: "Token used is not the latest valid token" }); + } + + const cleanPayload = { + sub: payload.sub, + username: payload.username, + role: payload.role + }; + + const newAccess = signAccessToken(cleanPayload); + const newRefresh = signRefreshToken(cleanPayload); + + refreshTokens.set(cleanPayload.sub, newRefresh); + + res.cookie("refreshToken", newRefresh, { + httpOnly: true, + sameSite: "strict", + secure: process.env.NODE_ENV === "production", + maxAge: 7 * 24 * 60 * 60 * 1000, + }); + + res.json({ accessToken: newAccess }); + + } catch (error) { + console.error("Refresh token verification failed:", error); + res.status(401).json({ error: "Refresh token is expired or invalid" }); + } +}); + +app.post("/logout", authenticate, (req: AuthRequest, res) => { + refreshTokens.delete(req.user!.sub); + res.clearCookie("refreshToken"); + res.status(204).send(); +}); + +app.get("/protected", authenticate, (req: AuthRequest, res) => { + res.json({ message: "Protected route accessed", user: req.user }); +}); + +const PORT = process.env.PORT || 4000; +app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); \ No newline at end of file diff --git a/backend/src/types.d.ts b/backend/src/types.d.ts new file mode 100644 index 0000000..ee0fbd1 --- /dev/null +++ b/backend/src/types.d.ts @@ -0,0 +1,5 @@ +declare namespace Express { + export interface Request { + user?: { sub: string; username: string; role: string }; + } +} diff --git a/package-lock.json b/package-lock.json index 01f29c5..b29966b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "devDependencies": { "@commitlint/cli": "^19.1.0", "@commitlint/config-conventional": "^19.1.0", + "@types/cookie-parser": "^1.4.9", "autoprefixer": "^10.4.21", "husky": "^9.0.11", "postcss": "^8.5.6", @@ -371,6 +372,29 @@ "node": ">=v18" } }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/conventional-commits-parser": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", @@ -380,6 +404,59 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.9.tgz", + "integrity": "sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/node": { "version": "20.11.26", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.26.tgz", @@ -389,6 +466,58 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.0.tgz", + "integrity": "sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.9.tgz", + "integrity": "sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "node_modules/ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", diff --git a/package.json b/package.json index 53ab1e1..a14a1cf 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "devDependencies": { "@commitlint/cli": "^19.1.0", "@commitlint/config-conventional": "^19.1.0", + "@types/cookie-parser": "^1.4.9", "autoprefixer": "^10.4.21", "husky": "^9.0.11", "postcss": "^8.5.6", From a4bdfa89f5f6720cb799030b0106f1a6c35e5cc8 Mon Sep 17 00:00:00 2001 From: LONECODER1 Date: Wed, 22 Oct 2025 11:25:36 +0530 Subject: [PATCH 2/3] merge conflicts resolution --- backend/package-lock.json | 368 +++++++++++++++++++++++++++++++++++--- backend/package.json | 4 +- backend/src/server.ts | 9 - 3 files changed, 351 insertions(+), 30 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index f5c3687..c4d65e6 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -16,7 +16,9 @@ "dotenv": "^16.6.1", "express": "^4.21.2", "jsonwebtoken": "^9.0.2", - "morgan": "^1.10.0" + "mongoose": "^8.19.2", + "morgan": "^1.10.0", + "nodemon": "^3.1.10" }, "devDependencies": { "@types/bcryptjs": "^2.4.6", @@ -72,6 +74,15 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.2.tgz", + "integrity": "sha512-QgA5AySqB27cGTXBFmnpifAi7HxoGUeezwo6p9dI03MuDB6Pp33zgclqVb6oVK3j6I9Vesg0+oojW2XxB59SGg==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -287,6 +298,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -330,7 +356,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -357,7 +382,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/basic-auth": { @@ -391,7 +415,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -428,7 +451,6 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -439,7 +461,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -448,6 +469,15 @@ "node": ">=8" } }, + "node_modules/bson": { + "version": "6.10.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", + "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "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", @@ -503,7 +533,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -528,7 +557,6 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, "node_modules/content-disposition": { @@ -802,7 +830,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -858,7 +885,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -941,7 +967,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -962,6 +987,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -1014,6 +1048,12 @@ "node": ">=0.10.0" } }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "license": "ISC" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1045,7 +1085,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -1074,7 +1113,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1084,7 +1122,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -1097,7 +1134,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -1152,6 +1188,15 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -1219,6 +1264,12 @@ "node": ">= 0.6" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -1274,7 +1325,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -1306,6 +1356,90 @@ "node": ">=10" } }, + "node_modules/mongodb": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", + "integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.3.0", + "bson": "^6.10.4", + "mongodb-connection-string-url": "^3.0.2" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.3.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.19.2", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.19.2.tgz", + "integrity": "sha512-ww2T4dBV+suCbOfG5YPwj9pLCfUVyj8FEA1D3Ux1HHqutpLxGyOYEPU06iPRBW4cKr3PJfOSYsIpHWPTkz5zig==", + "license": "MIT", + "dependencies": { + "bson": "^6.10.4", + "kareem": "2.6.3", + "mongodb": "~6.20.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/morgan": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", @@ -1334,6 +1468,50 @@ "node": ">= 0.8" } }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1349,11 +1527,61 @@ "node": ">= 0.6" } }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -1447,7 +1675,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -1469,6 +1696,21 @@ "node": ">= 0.10" } }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -1512,7 +1754,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -1726,6 +1967,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -1747,6 +2006,15 @@ "source-map": "^0.6.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -1776,6 +2044,18 @@ "node": ">=0.10.0" } }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -1793,7 +2073,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -1811,6 +2090,27 @@ "node": ">=0.6" } }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -1940,6 +2240,12 @@ "node": ">=14.17" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "license": "MIT" + }, "node_modules/undici-types": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", @@ -1981,6 +2287,28 @@ "node": ">= 0.8" } }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/backend/package.json b/backend/package.json index cea2130..0fdd695 100644 --- a/backend/package.json +++ b/backend/package.json @@ -18,7 +18,9 @@ "dotenv": "^16.6.1", "express": "^4.21.2", "jsonwebtoken": "^9.0.2", - "morgan": "^1.10.0" + "mongoose": "^8.19.2", + "morgan": "^1.10.0", + "nodemon": "^3.1.10" }, "devDependencies": { "@types/bcryptjs": "^2.4.6", diff --git a/backend/src/server.ts b/backend/src/server.ts index 307837f..7e4c155 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -59,15 +59,6 @@ app.post("/refresh", (req, res) => { try { const payload = verifyRefreshToken(token); const storedToken = refreshTokens.get(payload.sub); - - console.log("--- REFRESH TOKEN COMPARISON DEBUG ---"); - console.log(`User ID (sub): ${payload.sub}`); - console.log(`Token from request (RT): ${token}`); - console.log(`Stored token from Map: ${storedToken}`); - console.log(`Tokens are strictly equal (storedToken === token): ${storedToken === token}`); - console.log(`Type of request token: ${typeof token}`); - console.log(`Type of stored token: ${typeof storedToken}`); - console.log("--------------------------------------"); if (!storedToken) { return res.status(401).json({ error: "Session not found or already logged out" }); From 5123eee0e3a9d55e24a0cf60dbb4e8e950aedcb6 Mon Sep 17 00:00:00 2001 From: LONECODER1 Date: Wed, 22 Oct 2025 11:56:51 +0530 Subject: [PATCH 3/3] merge conflict resolution in server --- backend/src/app.ts | 129 +++++++++++++++++++++++++++++++++++++++++- backend/src/server.ts | 107 ++--------------------------------- 2 files changed, 132 insertions(+), 104 deletions(-) diff --git a/backend/src/app.ts b/backend/src/app.ts index 7d3e6f8..91ca675 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -1,9 +1,36 @@ -import express, { Application } from 'express'; +// import express, { Application } from 'express'; +// import cors from 'cors'; +// import morgan from 'morgan'; +// import bodyParser from 'body-parser'; +// import healthRoutes from './routes/healthRoutes'; + +// const app: Application = express(); + + +// // Middleware setup +// app.use(cors()); +// app.use(morgan('dev')); +// app.use(bodyParser.json()); +// app.use(bodyParser.urlencoded({ extended: true })); + + +// // API Routes +// app.use('/api/health', healthRoutes); + +// export default app; + +import express, { Application, Request, Response } from 'express'; import cors from 'cors'; import morgan from 'morgan'; import bodyParser from 'body-parser'; import healthRoutes from './routes/healthRoutes'; +// 1. Imports needed for JWT/Auth +import bcrypt from "bcryptjs"; +import cookieParser from "cookie-parser"; +import { signAccessToken, signRefreshToken, verifyRefreshToken } from "./controllers/auth"; +import { authenticate, AuthRequest } from "./middleware/authMiddleware"; + const app: Application = express(); @@ -12,9 +39,107 @@ app.use(cors()); app.use(morgan('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); +// 2. Middleware needed for Auth +app.use(cookieParser()); + + +// 3. User Interface and Mock Data +interface User { + id: string; + username: string; + passwordHash: string; + role: string; +} + +const users: User[] = [ + { id: "1", username: "alice", passwordHash: bcrypt.hashSync("password", 8), role: "admin" }, +]; + +const refreshTokens = new Map() + + +// 4. Authentication API Routes +app.post("/login", async (req: Request, res: Response) => { + const { username, password } = req.body; + const user = users.find((u) => u.username === username); + if (!user) return res.status(401).json({ error: "Invalid credentials" }); + + const match = await bcrypt.compare(password, user.passwordHash); + if (!match) return res.status(401).json({ error: "Invalid credentials" }); + + const payload = { sub: user.id, username: user.username, role: user.role }; + const accessToken = signAccessToken(payload); + const refreshToken = signRefreshToken(payload); + + refreshTokens.set(user.id, refreshToken); + + res.cookie("refreshToken", refreshToken, { + httpOnly: true, + sameSite: "strict", + secure: process.env.NODE_ENV === "production", + maxAge: 7 * 24 * 60 * 60 * 1000, + }); + + res.json({ accessToken }); +}); + +app.post("/refresh", (req: Request, res: Response) => { + const token = req.cookies?.refreshToken || req.body.refreshToken; + + if (!token) { + return res.status(401).json({ error: "Refresh token is missing" }); + } + + try { + const payload = verifyRefreshToken(token); + const storedToken = refreshTokens.get(payload.sub); + + if (!storedToken) { + return res.status(401).json({ error: "Session not found or already logged out" }); + } + + if (storedToken !== token) { + return res.status(401).json({ error: "Token used is not the latest valid token" }); + } + + const cleanPayload = { + sub: payload.sub, + username: payload.username, + role: payload.role + }; + + const newAccess = signAccessToken(cleanPayload); + const newRefresh = signRefreshToken(cleanPayload); + + refreshTokens.set(cleanPayload.sub, newRefresh); + + res.cookie("refreshToken", newRefresh, { + httpOnly: true, + sameSite: "strict", + secure: process.env.NODE_ENV === "production", + maxAge: 7 * 24 * 60 * 60 * 1000, + }); + + res.json({ accessToken: newAccess }); + + } catch (error) { + console.error("Refresh token verification failed:", error); + res.status(401).json({ error: "Refresh token is expired or invalid" }); + } +}); + +app.post("/logout", authenticate, (req: AuthRequest, res: Response) => { + // req.user is guaranteed to exist by the 'authenticate' middleware + refreshTokens.delete(req.user!.sub); + res.clearCookie("refreshToken"); + res.status(204).send(); +}); +app.get("/protected", authenticate, (req: AuthRequest, res: Response) => { + res.json({ message: "Protected route accessed", user: req.user }); +}); -// API Routes +// Existing API Routes app.use('/api/health', healthRoutes); export default app; \ No newline at end of file diff --git a/backend/src/server.ts b/backend/src/server.ts index 7e4c155..407e1f8 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,108 +1,11 @@ -import express from "express"; import dotenv from "dotenv"; -import bcrypt from "bcryptjs"; -import cookieParser from "cookie-parser"; -import { signAccessToken, signRefreshToken, verifyRefreshToken } from "./controllers/auth"; -import { authenticate, AuthRequest } from "./middleware/authMiddleware"; dotenv.config(); -const app = express(); -app.use(express.json()); -app.use(cookieParser()); +import app from './app'; +const PORT = process.env.PORT ? Number(process.env.PORT) : 4000; -interface User { - id: string; - username: string; - passwordHash: string; - role: string; -} - -const users: User[] = [ - { id: "1", username: "alice", passwordHash: bcrypt.hashSync("password", 8), role: "admin" }, -]; - -const refreshTokens = new Map() - -app.post("/login", async (req, res) => { - const { username, password } = req.body; - const user = users.find((u) => u.username === username); - if (!user) return res.status(401).json({ error: "Invalid credentials" }); - - const match = await bcrypt.compare(password, user.passwordHash); - if (!match) return res.status(401).json({ error: "Invalid credentials" }); - - const payload = { sub: user.id, username: user.username, role: user.role }; - const accessToken = signAccessToken(payload); - const refreshToken = signRefreshToken(payload); - - refreshTokens.set(user.id, refreshToken); - - res.cookie("refreshToken", refreshToken, { - httpOnly: true, - sameSite: "strict", - secure: process.env.NODE_ENV === "production", - maxAge: 7 * 24 * 60 * 60 * 1000, - }); - - res.json({ accessToken }); -}); - -app.post("/refresh", (req, res) => { - const token = req.cookies?.refreshToken || req.body.refreshToken; - - if (!token) { - return res.status(401).json({ error: "Refresh token is missing" }); - } - - try { - const payload = verifyRefreshToken(token); - const storedToken = refreshTokens.get(payload.sub); - - if (!storedToken) { - return res.status(401).json({ error: "Session not found or already logged out" }); - } - - if (storedToken !== token) { - return res.status(401).json({ error: "Token used is not the latest valid token" }); - } - - const cleanPayload = { - sub: payload.sub, - username: payload.username, - role: payload.role - }; - - const newAccess = signAccessToken(cleanPayload); - const newRefresh = signRefreshToken(cleanPayload); - - refreshTokens.set(cleanPayload.sub, newRefresh); - - res.cookie("refreshToken", newRefresh, { - httpOnly: true, - sameSite: "strict", - secure: process.env.NODE_ENV === "production", - maxAge: 7 * 24 * 60 * 60 * 1000, - }); - - res.json({ accessToken: newAccess }); - - } catch (error) { - console.error("Refresh token verification failed:", error); - res.status(401).json({ error: "Refresh token is expired or invalid" }); - } -}); - -app.post("/logout", authenticate, (req: AuthRequest, res) => { - refreshTokens.delete(req.user!.sub); - res.clearCookie("refreshToken"); - res.status(204).send(); -}); - -app.get("/protected", authenticate, (req: AuthRequest, res) => { - res.json({ message: "Protected route accessed", user: req.user }); -}); - -const PORT = process.env.PORT || 4000; -app.listen(PORT, () => console.log(`Server running on port ${PORT}`)); \ No newline at end of file +app.listen(PORT, () => { + console.log(`Server running on port ${PORT}`); +}); \ No newline at end of file