From 7900d0a5d3e8ff52a44274e54a6551237326930c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 06:10:40 +0000 Subject: [PATCH 1/2] Initial plan From 466fb528a94108fa956158f013bf3f9738c598fb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 20 Mar 2026 06:14:17 +0000 Subject: [PATCH 2/2] fix: prevent client-provided email spoofing in JWT callback - Remove token.email = session.email from session.update() handler; client-controlled email was being written directly to the JWT token, enabling session.user.email spoofing. - Add DB re-hydration of token.email for regular users (userType=user) using token.id, consistent with the existing pattern for admin/advisor. - Add a warning log when the user record is not found during re-hydration. Co-authored-by: shammy911 <180457932+shammy911@users.noreply.github.com> --- app/api/auth/[...nextauth]/route.ts | 15 +++- package-lock.json | 108 +++------------------------- 2 files changed, 20 insertions(+), 103 deletions(-) diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts index 28c8439..1a9e2a7 100644 --- a/app/api/auth/[...nextauth]/route.ts +++ b/app/api/auth/[...nextauth]/route.ts @@ -146,13 +146,11 @@ export const authOptions = { } // Apply client-side session.update(...) payload immediately. + // Only allow updating display name — never trust client-provided email. if (trigger === "update" && session) { if (typeof session.name === "string") { token.name = session.name; } - if (typeof session.email === "string") { - token.email = session.email; - } } // 🔥 ALWAYS GET LATEST VALUE FROM DB @@ -166,6 +164,17 @@ export const authOptions = { token.name = admin.name; token.email = admin.email; } + } else if (token.userType === "user" && token.id) { + const user = await prisma.user.findUnique({ + where: { id: token.id }, + select: { email: true }, + }); + + if (user) { + token.email = user.email; + } else { + console.warn(`JWT: no user found for id=${token.id}; token may be stale`); + } } return token; diff --git a/package-lock.json b/package-lock.json index a25f105..38f031f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -157,7 +157,6 @@ "version": "7.29.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -666,7 +665,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -688,7 +686,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -2491,7 +2488,6 @@ "version": "6.19.2", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.18" }, @@ -4081,25 +4077,6 @@ "tailwindcss": "4.2.1" } }, - "node_modules/@testing-library/dom": { - "version": "10.4.1", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "picocolors": "1.1.1", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", "dev": true, @@ -4190,11 +4167,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@types/aria-query": { - "version": "5.0.4", - "dev": true, - "license": "MIT" - }, "node_modules/@types/babel__core": { "version": "7.20.5", "dev": true, @@ -4311,7 +4283,6 @@ "node_modules/@types/node": { "version": "22.19.15", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -4338,18 +4309,16 @@ }, "node_modules/@types/react": { "version": "19.2.14", - "devOptional": true, + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { "version": "19.2.3", - "devOptional": true, + "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -5110,7 +5079,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5740,11 +5708,6 @@ "node": ">=0.3.1" } }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "dev": true, - "license": "MIT" - }, "node_modules/dom-helpers": { "version": "5.2.1", "license": "MIT", @@ -5813,11 +5776,6 @@ "version": "1.5.313", "license": "ISC" }, - "node_modules/embla-carousel": { - "version": "8.6.0", - "license": "MIT", - "peer": true - }, "node_modules/embla-carousel-autoplay": { "version": "8.6.0", "license": "MIT", @@ -5838,8 +5796,7 @@ }, "node_modules/embla-carousel-react/node_modules/embla-carousel": { "version": "8.5.1", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/embla-carousel-react/node_modules/embla-carousel-reactive-utils": { "version": "8.5.1", @@ -6598,7 +6555,6 @@ "version": "30.3.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.3.0", "@jest/types": "30.3.0", @@ -7529,7 +7485,6 @@ "version": "26.1.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", @@ -8047,14 +8002,6 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, - "node_modules/lz-string": { - "version": "1.5.0", - "dev": true, - "license": "MIT", - "bin": { - "lz-string": "bin/bin.js" - } - }, "node_modules/magic-string": { "version": "0.30.21", "dev": true, @@ -8214,7 +8161,6 @@ "resolved": "https://registry.npmjs.org/next/-/next-16.2.0.tgz", "integrity": "sha512-NLBVrJy1pbV1Yn00L5sU4vFyAHt5XuSjzrNyFnxo6Com0M0KrL6hHM5B99dbqXb2bE9pm4Ow3Zl1xp6HVY9edQ==", "license": "MIT", - "peer": true, "dependencies": { "@next/env": "16.2.0", "@swc/helpers": "0.5.15", @@ -8266,7 +8212,6 @@ "node_modules/next-auth": { "version": "4.24.13", "license": "ISC", - "peer": true, "dependencies": { "@babel/runtime": "^7.20.13", "@panva/hkdf": "^1.0.2", @@ -8387,7 +8332,6 @@ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.13.tgz", "integrity": "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==", "license": "MIT-0", - "peer": true, "engines": { "node": ">=6.0.0" } @@ -8660,7 +8604,6 @@ "node_modules/pg": { "version": "8.20.0", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.12.0", "pg-pool": "^3.13.0", @@ -8794,6 +8737,7 @@ }, "node_modules/postcss": { "version": "8.5.8", + "dev": true, "funding": [ { "type": "opencollective", @@ -8809,7 +8753,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -8825,6 +8768,7 @@ }, "node_modules/postcss/node_modules/nanoid": { "version": "3.3.11", + "dev": true, "funding": [ { "type": "github", @@ -8877,7 +8821,6 @@ "node_modules/preact": { "version": "10.29.0", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -8897,35 +8840,10 @@ "version": "3.8.0", "license": "MIT" }, - "node_modules/pretty-format": { - "version": "27.5.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/prisma": { "version": "6.19.2", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@prisma/config": "6.19.2", "@prisma/engines": "6.19.2" @@ -8996,7 +8914,6 @@ "node_modules/react": { "version": "19.2.0", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -9023,7 +8940,6 @@ "node_modules/react-dom": { "version": "19.2.0", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -9034,7 +8950,6 @@ "node_modules/react-hook-form": { "version": "7.71.2", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -9053,11 +8968,6 @@ "react": "*" } }, - "node_modules/react-is": { - "version": "17.0.2", - "dev": true, - "license": "MIT" - }, "node_modules/react-remove-scroll": { "version": "2.7.2", "license": "MIT", @@ -9701,8 +9611,8 @@ }, "node_modules/tailwindcss": { "version": "4.2.1", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/tailwindcss-animate": { "version": "1.0.7", @@ -9892,7 +9802,6 @@ "version": "10.9.2", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -9982,9 +9891,8 @@ }, "node_modules/typescript": { "version": "5.9.3", - "devOptional": true, + "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver"