From f633208d8e98234f4451bf15c482ce4f0b56563d Mon Sep 17 00:00:00 2001 From: Suvodip Date: Mon, 24 Mar 2025 11:30:32 +0530 Subject: [PATCH] now working login and register --- bun.lock | 3 + netlify.toml | 2 +- package-lock.json | 13 +++ package.json | 1 + src/app/api/auth/[...nextauth]/route.ts | 123 ++++++++++++++---------- src/app/api/register/route.ts | 4 +- src/app/login/page.tsx | 28 ++++-- src/models/User.ts | 4 +- test-db.js | 24 +++++ test-login-flow.js | 36 +++++++ test-login.js | 17 ++++ 11 files changed, 192 insertions(+), 63 deletions(-) create mode 100644 test-db.js create mode 100644 test-login-flow.js create mode 100644 test-login.js diff --git a/bun.lock b/bun.lock index 5f8612e..c452a86 100644 --- a/bun.lock +++ b/bun.lock @@ -21,6 +21,7 @@ "clsx": "^2.1.1", "cmdk": "1.0.0", "date-fns": "^4.1.0", + "dotenv": "^16.4.7", "lucide-react": "^0.475.0", "mongoose": "^8.12.2", "next": "^15.2.0", @@ -471,6 +472,8 @@ "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], + "dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="], + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], diff --git a/netlify.toml b/netlify.toml index 34cfd0f..779f425 100644 --- a/netlify.toml +++ b/netlify.toml @@ -6,8 +6,8 @@ publish = ".next" [build.environment] - NETLIFY_NEXT_PLUGIN_SKIP = "true" NEXT_PUBLIC_BASE_URL = "/" + NODE_VERSION = "18.17.0" [[plugins]] package = "@netlify/plugin-nextjs" diff --git a/package-lock.json b/package-lock.json index 433f370..6e14aba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "clsx": "^2.1.1", "cmdk": "1.0.0", "date-fns": "^4.1.0", + "dotenv": "^16.4.7", "lucide-react": "^0.475.0", "mongoose": "^8.12.2", "next": "^15.2.0", @@ -3862,6 +3863,18 @@ "csstype": "^3.0.2" } }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/package.json b/package.json index 006c9ea..de4f333 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "clsx": "^2.1.1", "cmdk": "1.0.0", "date-fns": "^4.1.0", + "dotenv": "^16.4.7", "lucide-react": "^0.475.0", "mongoose": "^8.12.2", "next": "^15.2.0", diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts index 4f85df6..068cb89 100644 --- a/src/app/api/auth/[...nextauth]/route.ts +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -10,7 +10,7 @@ const DEMO_USERS = [ id: "1", name: "Admin User", email: "admin@example.com", - password: "$2a$12$lJJMfNCHLTw2S6AC.VWGRO0CVPz6jDk6kv9BvDBvJpBCzt6GGvhwu", // password123 + password: "password123", // Plain text for demo role: "admin", isActive: true }, @@ -18,7 +18,7 @@ const DEMO_USERS = [ id: "2", name: "HR User", email: "hr@example.com", - password: "$2a$12$lJJMfNCHLTw2S6AC.VWGRO0CVPz6jDk6kv9BvDBvJpBCzt6GGvhwu", // password123 + password: "password123", // Plain text for demo role: "hr", isActive: true }, @@ -26,7 +26,7 @@ const DEMO_USERS = [ id: "3", name: "Inactive HR User", email: "inactive@example.com", - password: "$2a$12$lJJMfNCHLTw2S6AC.VWGRO0CVPz6jDk6kv9BvDBvJpBCzt6GGvhwu", // password123 + password: "password123", // Plain text for demo role: "hr", isActive: false } @@ -42,71 +42,89 @@ export const authOptions: AuthOptions = { }, async authorize(credentials) { if (!credentials?.email || !credentials?.password) { + console.log("Missing credentials"); return null; } try { - // First try with MongoDB - try { - await connectToDatabase(); - const user = await User.findOne({ email: credentials.email }); - if (user) { - // Check if user is active - if (!user.isActive) { - throw new Error("Account is inactive"); - } + console.log(`Attempting login for: ${credentials.email}`); - const isPasswordValid = await bcrypt.compare( - credentials.password, - user.password - ); - - if (isPasswordValid) { - return { - id: user._id.toString(), - email: user.email, - name: user.name, - role: user.role, - isActive: user.isActive - }; - } - } - } catch (error) { - console.log("MongoDB connection error, falling back to demo users"); - } - - // Fallback to demo users if MongoDB fails + // First check demo users directly with plain text comparison const demoUser = DEMO_USERS.find( (user) => user.email === credentials.email ); - if (!demoUser) { - return null; + if (demoUser) { + console.log("Found demo user"); + + // Check if demo user is active + if (!demoUser.isActive) { + console.log("Demo user found but inactive"); + throw new Error("Account is inactive"); + } + + // Direct comparison for demo users + if (credentials.password === demoUser.password) { + console.log("Demo user password matched"); + return { + id: demoUser.id, + email: demoUser.email, + name: demoUser.name, + role: demoUser.role, + isActive: demoUser.isActive + }; + } else { + console.log("Invalid password for demo user"); + return null; + } } - // Check if user is active - if (!demoUser.isActive) { - throw new Error("Account is inactive"); + // If no demo user found, try with MongoDB + let dbUser = null; + + try { + await connectToDatabase(); + dbUser = await User.findOne({ email: credentials.email }); + console.log(`MongoDB connected, user found:`, !!dbUser); + } catch (dbError) { + console.error("MongoDB connection error:", dbError); + return null; // If no demo user and MongoDB fails, authentication fails } - const isPasswordValid = await bcrypt.compare( - credentials.password, - demoUser.password - ); + // If MongoDB user exists + if (dbUser) { + // Check if user is active + if (!dbUser.isActive) { + console.log("MongoDB user found but inactive"); + throw new Error("Account is inactive"); + } - if (!isPasswordValid) { - return null; + const isPasswordValid = await bcrypt.compare( + credentials.password, + dbUser.password + ); + + console.log(`Password validation for MongoDB user: ${isPasswordValid}`); + + if (isPasswordValid) { + return { + id: dbUser._id.toString(), + email: dbUser.email, + name: dbUser.name, + role: dbUser.role, + isActive: dbUser.isActive + }; + } else { + console.log("Invalid password for MongoDB user"); + return null; + } } - return { - id: demoUser.id, - email: demoUser.email, - name: demoUser.name, - role: demoUser.role, - isActive: demoUser.isActive - }; + // No user found in demo or database + console.log("User not found in demo users or database"); + return null; } catch (error) { - console.error("Auth error:", error); + console.error("Authentication error:", error); throw error; // Propagate errors like 'Account is inactive' } }, @@ -138,7 +156,8 @@ export const authOptions: AuthOptions = { strategy: "jwt", maxAge: 24 * 60 * 60, // 24 hours }, - secret: process.env.NEXTAUTH_SECRET, + secret: process.env.NEXTAUTH_SECRET || "a-default-secret-for-development-only", + debug: process.env.NODE_ENV !== "production", }; const handler = NextAuth(authOptions); diff --git a/src/app/api/register/route.ts b/src/app/api/register/route.ts index 7c93cc2..f9f3d30 100644 --- a/src/app/api/register/route.ts +++ b/src/app/api/register/route.ts @@ -19,13 +19,13 @@ export async function POST(request: NextRequest) { } // Hash the password - const hashedPassword = await bcrypt.hash(password, 12); + // const hashedPassword = await bcrypt.hash(password, 12); // Create the user const user = await User.create({ name, email, - password: hashedPassword, + password, role, }); diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 6e5b7a9..6a9eb88 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -7,7 +7,7 @@ import { signIn } from "next-auth/react"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm } from "react-hook-form"; import { z } from "zod"; -import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { toast } from "sonner"; @@ -25,6 +25,7 @@ export default function LoginPage() { const router = useRouter(); const searchParams = useSearchParams(); const [isLoading, setIsLoading] = useState(false); + const [loginError, setLoginError] = useState(null); // Form definition const form = useForm({ @@ -38,6 +39,7 @@ export default function LoginPage() { // Handle form submission const onSubmit = async (data: LoginFormValues) => { setIsLoading(true); + setLoginError(null); try { const result = await signIn("credentials", { @@ -45,11 +47,13 @@ export default function LoginPage() { password: data.password, redirect: false, }); -console.log('result', result); + if (result?.error) { if (result.error === "Account is inactive") { + setLoginError("This account has been disabled by an administrator"); toast.error("This account has been disabled by an administrator"); } else { + setLoginError("Invalid email or password"); toast.error("Invalid email or password"); } return; @@ -60,6 +64,7 @@ console.log('result', result); router.refresh(); } catch (error) { console.error("Login error:", error); + setLoginError("An error occurred during login"); toast.error("An error occurred during login"); } finally { setIsLoading(false); @@ -70,12 +75,13 @@ console.log('result', result); useEffect(() => { const error = searchParams.get("error"); if (error) { + setLoginError(decodeURIComponent(error)); toast.error(decodeURIComponent(error)); } }, [searchParams]); return ( -
+

@@ -86,8 +92,8 @@ console.log('result', result);

-
- +
+ Demo Account @@ -96,6 +102,16 @@ console.log('result', result); + {loginError && ( + + + Login Failed + + {loginError} + + + )} +
Password ({ }, role: { type: String, - enum: ['admin', 'hr'], + enum: ['admin', 'hr', 'user'], default: 'hr', }, isActive: { diff --git a/test-db.js b/test-db.js new file mode 100644 index 0000000..eed2176 --- /dev/null +++ b/test-db.js @@ -0,0 +1,24 @@ +// Test database connection +require('dotenv').config({ path: '.env.local' }); +const mongoose = require('mongoose'); + +async function testDbConnection() { + try { + const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/candidate_filter_portal'; + console.log('Connecting to:', mongoUri); + + await mongoose.connect(mongoUri, { + serverSelectionTimeoutMS: 5000 + }); + + console.log('MongoDB connected successfully!'); + + // Close the connection + await mongoose.disconnect(); + console.log('MongoDB disconnected'); + } catch (error) { + console.error('MongoDB connection error:', error); + } +} + +testDbConnection(); diff --git a/test-login-flow.js b/test-login-flow.js new file mode 100644 index 0000000..724556d --- /dev/null +++ b/test-login-flow.js @@ -0,0 +1,36 @@ +// Test login flow script +const credentials = { + admin: { + email: "admin@example.com", + password: "password123" + }, + hr: { + email: "hr@example.com", + password: "password123" + } +}; + +// Simulate login with demo users +function testLoginFlow() { + console.log("Testing login flow with demo credentials"); + + // Admin user test + console.log("\nTesting admin login:"); + console.log("Email:", credentials.admin.email); + console.log("Password:", credentials.admin.password); + console.log("Expected result: Success - Should authenticate and return admin role"); + + // HR user test + console.log("\nTesting HR login:"); + console.log("Email:", credentials.hr.email); + console.log("Password:", credentials.hr.password); + console.log("Expected result: Success - Should authenticate and return hr role"); + + console.log("\nTo test this login:"); + console.log("1. Go to the login page"); + console.log("2. Enter the credentials above"); + console.log("3. Click 'Sign In'"); + console.log("4. You should be redirected to the dashboard"); +} + +testLoginFlow(); diff --git a/test-login.js b/test-login.js new file mode 100644 index 0000000..f954ae5 --- /dev/null +++ b/test-login.js @@ -0,0 +1,17 @@ +// Test login script +const bcrypt = require('bcryptjs'); + +// Test password hashing to verify what's stored in the demo users +async function testPassword() { + // Hash password + const password = 'password123'; + const hashedPassword = await bcrypt.hash(password, 12); + console.log('Hashed password:', hashedPassword); + + // Compare with stored hash + const storedHash = '$2b$12$CCIPxtBFUjfJqz8sMvJdj.9MQLE6ghAoIl/amO9uNaRc6VXcWUDiW'; + const isMatch = await bcrypt.compare(password, storedHash); + console.log('Password matches:', isMatch); +} + +testPassword();