Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0c7b7bf
feat: add avatar component for user profile display in dashboard
shammy911 Mar 19, 2026
ae1ed87
feat: refactor authentication components and improve user profile han…
shammy911 Mar 20, 2026
7900d0a
Initial plan
Copilot Mar 20, 2026
bb116ae
Update components/dashboard/dashboard-client.tsx
shammy911 Mar 20, 2026
230590d
Update components/dashboard/dashboard-client.tsx
shammy911 Mar 20, 2026
466fb52
fix: prevent client-provided email spoofing in JWT callback
Copilot Mar 20, 2026
b887cd0
Update components/contact/FeedbackPopup.tsx
shammy911 Mar 20, 2026
9317173
Update components/dashboard/dashboard-client.tsx
shammy911 Mar 20, 2026
22329b8
Initial plan
Copilot Mar 20, 2026
bd679f4
Initial plan
Copilot Mar 20, 2026
e121ed0
security: remove console.log that exposed plaintext credentials in ad…
Copilot Mar 20, 2026
0e7bc76
Initial plan
Copilot Mar 20, 2026
84683bc
fix: enable NextAuth debug only in development environment
Copilot Mar 20, 2026
b32b6bf
fix: use session.user.id instead of email for profile DB lookups
Copilot Mar 20, 2026
96f1791
Initial plan
Copilot Mar 20, 2026
f6c3e5c
fix(accessibility): add aria-label to ChatBot chat input
Copilot Mar 20, 2026
af8c160
Update app/layout.tsx
shammy911 Mar 20, 2026
b03410c
Merge pull request #80 from G12CSC/copilot/sub-pr-79
shammy911 Mar 20, 2026
f8cb8bc
Merge pull request #82 from G12CSC/copilot/sub-pr-79-another-one
shammy911 Mar 20, 2026
503cb55
Merge pull request #84 from G12CSC/copilot/sub-pr-79-one-more-time
shammy911 Mar 20, 2026
fccc47c
Merge pull request #85 from G12CSC/copilot/sub-pr-79-please-work
shammy911 Mar 20, 2026
1ffa40b
Merge pull request #83 from G12CSC/copilot/sub-pr-79-yet-again
shammy911 Mar 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 0 additions & 98 deletions app/(auth)/login/old.txt

This file was deleted.

175 changes: 95 additions & 80 deletions app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const authOptions = {

return {
id: user.id,
name: user.name,
name: user.name,
email: user.email,
userType: "user",
};
Expand All @@ -55,7 +55,6 @@ export const authOptions = {
},

async authorize(credentials) {

if (!credentials?.email || !credentials?.password) return null;

const admin = await prisma.admin.findUnique({
Expand All @@ -71,14 +70,12 @@ export const authOptions = {

if (!ok) return null;

console.log("LOGIN ATTEMPT:", credentials);

return {
id: admin.id,
email: admin.email,
userType: admin.role === "advisor" ? "advisor" : "admin",
adminRole: admin.role,
mustChangePassword: admin.mustChangePassword
mustChangePassword: admin.mustChangePassword,
};
},
}),
Expand All @@ -103,84 +100,102 @@ export const authOptions = {
maxAge: 30 * 60, // 30 minutes
},

callbacks: {

// async jwt({ token, user }) {
// if (user) {
// token.id = user.id;
// token.userType = user.userType;
// token.name = user.name;
// token.adminRole = user.adminRole ?? null;
// token.mustChangePassword = user.mustChangePassword ?? false;
// }
//
// if (!token.userType) token.userType = "user";
//
// return token;
// },
//
// async session({ session, token }) {
// session.user.id = token.id;
// session.user.userType = token.userType;
// session.user.name = token.name;
// session.user.adminRole = token.adminRole;
// session.user.mustChangePassword = token.mustChangePassword;
// return session;
// },
//
// async redirect({ url, baseUrl }) {
// return process.env.BASEURL;
// },

async jwt({ token, user,account }) {

if (user) {
token.id = user.id;
token.name = user.name;
token.adminRole = user.adminRole ?? null;

// 🔥 KEY FIX
if (account?.provider === "google" || account?.provider === "github") {
token.userType = "user"; // ✅ assign role for OAuth users
} else {
token.userType = user.userType ?? "user";
}
}

// 🔥 ALWAYS GET LATEST VALUE FROM DB
if (token.userType === "advisor" || token.userType === "admin") {
const admin = await prisma.admin.findUnique({
where: { id: token.id },
});

if (admin) {
token.mustChangePassword = admin.mustChangePassword;
token.name = admin.name;
token.email = admin.email;
}
}

return token;
},

async session({ session, token }) {
session.user.id = token.id;
session.user.userType = token.userType ?? "user";
session.user.name = token.name;
session.user.adminRole = token.adminRole;
session.user.mustChangePassword = token.mustChangePassword;
session.user.email = token.email; // ✅ IMPORTANT
return session;
},

async redirect({ url, baseUrl }) {
if (url.startsWith("/")) return baseUrl + url;
if (url.startsWith(baseUrl)) return url;
return baseUrl;
callbacks: {
// async jwt({ token, user }) {
// if (user) {
// token.id = user.id;
// token.userType = user.userType;
// token.name = user.name;
// token.adminRole = user.adminRole ?? null;
// token.mustChangePassword = user.mustChangePassword ?? false;
// }
//
// if (!token.userType) token.userType = "user";
//
// return token;
// },
//
// async session({ session, token }) {
// session.user.id = token.id;
// session.user.userType = token.userType;
// session.user.name = token.name;
// session.user.adminRole = token.adminRole;
// session.user.mustChangePassword = token.mustChangePassword;
// return session;
// },
//
// async redirect({ url, baseUrl }) {
// return process.env.BASEURL;
// },

async jwt({ token, user, account, trigger, session }) {
if (user) {
token.id = user.id;
token.name = user.name;
token.email = user.email;
token.adminRole = user.adminRole ?? null;

// 🔥 KEY FIX
if (account?.provider === "google" || account?.provider === "github") {
token.userType = "user"; // ✅ assign role for OAuth users
} else {
token.userType = user.userType ?? "user";
}
}

// 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;
}
}

// 🔥 ALWAYS GET LATEST VALUE FROM DB
if (token.userType === "advisor" || token.userType === "admin") {
const admin = await prisma.admin.findUnique({
where: { id: token.id },
});

if (admin) {
token.mustChangePassword = admin.mustChangePassword;
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;
},

debug: true,
async session({ session, token }) {
session.user.id = token.id;
session.user.userType = token.userType ?? "user";
session.user.name = token.name;
session.user.adminRole = token.adminRole;
session.user.mustChangePassword = token.mustChangePassword;
session.user.email = token.email; // ✅ IMPORTANT
return session;
},

async redirect({ url, baseUrl }) {
if (url.startsWith("/")) return baseUrl + url;
if (url.startsWith(baseUrl)) return url;
return baseUrl;
},
},

debug: process.env.NODE_ENV === "development",

secret: process.env.NEXTAUTH_SECRET,
};
Expand Down
21 changes: 13 additions & 8 deletions app/api/user/profile/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ export async function GET(req: Request) {
try {
const session = await getServerSession(authOptions);

if (!session) {
if (!session || !session.user?.id) {
return new Response("Unauthorized", { status: 401 });
}

const user = await prisma.user.findUnique({
where: { email: session.user?.email || undefined },
where: { id: session.user.id },
select: {
name: true,
email: true,
Expand Down Expand Up @@ -39,35 +39,40 @@ export async function PATCH(request: Request) {
try {
const session = await getServerSession(authOptions);

if (!session) {
if (!session || !session.user?.id) {
return new Response("Unauthorized", { status: 401 });
}

const body = await request.json();

const user = await prisma.user.findUnique({
where: { email: session.user?.email || undefined },
where: { id: session.user.id },
});

if (!user) {
return new Response("User not found", { status: 404 });
}

await prisma.user.update({
where: { email: session.user?.email || undefined },
const updatedUser = await prisma.user.update({
where: { id: session.user.id },
data: {
Comment on lines +56 to 58

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint uses session.user.email as the identifier for findUnique/update. If session fields can be influenced (e.g., via session.update(...)), this increases the risk of cross-user access. Prefer using the immutable session.user.id (already set in the NextAuth session callback) for lookups/updates instead of email.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

name: body.name,
phone: body.phone,
},
select: {
name: true,
email: true,
phone: true,
createdAt: true,
},
});
return Response.json({
success: true,
data: user,
data: updatedUser,
});
} catch (error) {
return new Response("An error occurred while updating the profile", {
status: 500,
});
}
}

Loading