From 84f010cb875a2a3d7ad61bf2d52c9b3b259f3ed0 Mon Sep 17 00:00:00 2001 From: mudabbir-ahmad Date: Fri, 14 Mar 2025 23:50:25 +0000 Subject: [PATCH] fixed issue with login page. --- app/(auth)/login/page.js | 11 ++--- app/(main)/team-selection/page.js | 22 +++------- app/api/auth/[...nextauth]/route.js | 24 ++++++++++- app/api/auth/check/route.js | 55 +++++++++++++++++++++++++ app/api/user/route.js | 9 ++--- middleware.js | 62 +++++++++++++++++++++++++++++ 6 files changed, 153 insertions(+), 30 deletions(-) create mode 100644 app/api/auth/check/route.js create mode 100644 middleware.js diff --git a/app/(auth)/login/page.js b/app/(auth)/login/page.js index c9b32db..5c39ae1 100644 --- a/app/(auth)/login/page.js +++ b/app/(auth)/login/page.js @@ -14,14 +14,9 @@ export default function Login() { const { data: session, status } = useSession(); const searchParams = useSearchParams(); - // Redirect if already authenticated useEffect(() => { - if (status === "authenticated") { - const callbackUrl = searchParams.get("callbackUrl") || "/team-selection"; - router.push(callbackUrl); - } - - // Check for error param + // Removed redirection for authenticated users; do not auto-navigate away from login. + // Retain error handling from search params. const errorParam = searchParams.get("error"); if (errorParam) { if (errorParam === "AuthError") { @@ -30,7 +25,7 @@ export default function Login() { setError("Authentication failed. Please try again."); } } - }, [status, router, searchParams]); + }, [searchParams]); const handleLogin = async (e) => { e.preventDefault(); diff --git a/app/(main)/team-selection/page.js b/app/(main)/team-selection/page.js index 8347866..6768f41 100644 --- a/app/(main)/team-selection/page.js +++ b/app/(main)/team-selection/page.js @@ -12,13 +12,12 @@ export default function TeamSelection() { const router = useRouter(); useEffect(() => { - // Clear any leftover team data cache from previous teams + // Clear any leftover team data localStorage.removeItem("teamData"); sessionStorage.removeItem("teamData"); localStorage.removeItem("userLeftTeam"); if (status === "authenticated") { - // Always verify team status with a fresh API call to avoid stale session data fetch(`/api/user?_t=${Date.now()}`, { headers: { "Cache-Control": "no-cache, no-store, must-revalidate", @@ -28,20 +27,12 @@ export default function TeamSelection() { }) .then((res) => res.json()) .then((userData) => { - console.log("User data:", userData); // Debug user data - - // Check if user is an admin first + console.log("User data:", userData); if (userData.role === "admin") { - // Redirect admin to admin dashboard with correct path - router.push("/admin"); // Changed from /admin-dashboard to /admin + router.push("/admin"); } else if (userData.teamId) { - // Only attempt to construct URL if teamId is valid - if (userData.teamId && !isNaN(parseInt(userData.teamId))) { - window.location.href = `/${userData.teamId}`; - } else { - console.error("Invalid teamId received:", userData.teamId); - setIsLoading(false); - } + // Use router.push for client-side navigation + router.push(`/${userData.teamId}`); } else { setIsLoading(false); } @@ -53,10 +44,9 @@ export default function TeamSelection() { } else if (status === "unauthenticated") { router.push("/login"); } else { - // Still loading session setIsLoading(true); } - }, [status, session, router]); + }, [status, router]); const handleJoinTeam = async (e) => { e.preventDefault(); diff --git a/app/api/auth/[...nextauth]/route.js b/app/api/auth/[...nextauth]/route.js index f31a3d2..1d8d874 100644 --- a/app/api/auth/[...nextauth]/route.js +++ b/app/api/auth/[...nextauth]/route.js @@ -93,7 +93,29 @@ export function createToken(payload) { export function verifyToken(token) { try { - return jwt.verify(token, JWT_SECRET); + if (!token) return null; + const parts = token.split("."); + if (parts.length === 2) { + // Custom token verification + const [encodedData, signature] = parts; + const expectedSignature = Buffer.from( + `${encodedData}.${JWT_SECRET}` + ).toString("base64"); + if (signature !== expectedSignature) { + console.error("Signature mismatch"); + return null; + } + const data = JSON.parse( + Buffer.from(encodedData, "base64").toString("utf-8") + ); + if (data.exp && data.exp < Math.floor(Date.now() / 1000)) return null; + return data; + } else if (parts.length === 3) { + // Standard JWT verification + return jwt.verify(token, JWT_SECRET); + } else { + throw new Error("Invalid token format"); + } } catch (error) { console.error("Token verification error:", error); return null; diff --git a/app/api/auth/check/route.js b/app/api/auth/check/route.js new file mode 100644 index 0000000..1df473f --- /dev/null +++ b/app/api/auth/check/route.js @@ -0,0 +1,55 @@ +import { getServerSession } from "next-auth/next"; +import { authOptions } from "../[...nextauth]/route"; + +export async function GET(req) { + try { + const session = await getServerSession(authOptions); + + if (!session) { + return new Response( + JSON.stringify({ + authenticated: false, + message: "Not authenticated", + }), + { + status: 401, + headers: { + "Content-Type": "application/json", + }, + } + ); + } + + return new Response( + JSON.stringify({ + authenticated: true, + user: { + id: session.user.id, + username: session.user.username, + role: session.user.role, + teamId: session.user.teamId, + }, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); + } catch (error) { + console.error("Session check error:", error); + + return new Response( + JSON.stringify({ + error: "Internal server error checking authentication", + }), + { + status: 500, + headers: { + "Content-Type": "application/json", + }, + } + ); + } +} diff --git a/app/api/user/route.js b/app/api/user/route.js index 70a1527..bfe930a 100644 --- a/app/api/user/route.js +++ b/app/api/user/route.js @@ -6,15 +6,15 @@ export const dynamic = "force-dynamic"; // Disable caching export async function GET() { try { - // Get the token from cookies const cookieStore = await cookies(); - const sessionToken = cookieStore.get("next-auth.session-token")?.value; + const sessionToken = + cookieStore.get("next-auth.session-token")?.value || + cookieStore.get("__Secure-next-auth.session-token")?.value; if (!sessionToken) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } - // Import the token verification function const { verifyToken } = await import("../auth/[...nextauth]/route"); try { @@ -24,7 +24,6 @@ export async function GET() { return Response.json({ error: "Invalid session" }, { status: 401 }); } - // Always fetch the latest user data from the database to ensure it's accurate const freshUserData = await queryOne( "SELECT id, username, role, team_id FROM users WHERE id = ?", [userData.id] @@ -40,7 +39,7 @@ export async function GET() { username: freshUserData.username, role: freshUserData.role, teamId: freshUserData.team_id, - timestamp: new Date().toISOString(), // Add timestamp for debugging + timestamp: new Date().toISOString(), }, { headers: { diff --git a/middleware.js b/middleware.js new file mode 100644 index 0000000..f5b0a74 --- /dev/null +++ b/middleware.js @@ -0,0 +1,62 @@ +import { NextResponse } from "next/server"; +import { getToken } from "next-auth/jwt"; + +// Configure which paths require authentication and which don't +export async function middleware(request) { + const { pathname } = request.nextUrl; + + // Define paths that don't require authentication + // Include the full NextAuth path pattern to avoid conflicts + const publicPaths = [ + "/login", + "/register", + "/api/register", + "/api/auth", + "/api/auth/signin", + "/api/auth/signout", + "/api/auth/session", + "/api/auth/csrf", + ]; + + const isPathPublic = publicPaths.some((path) => { + return pathname === path || pathname.startsWith(`${path}/`); + }); + + // If it's a public path, allow the request + if (isPathPublic) { + return NextResponse.next(); + } + + try { + // For protected paths, check authentication + const token = await getToken({ + req: request, + secret: + process.env.NEXTAUTH_SECRET || + "your-fallback-secret-should-be-at-least-32-chars", + }); + + // If not authenticated, redirect to login + if (!token) { + const url = new URL("/login", request.url); + url.searchParams.set("callbackUrl", encodeURI(request.url)); + return NextResponse.redirect(url); + } + + return NextResponse.next(); + } catch (error) { + console.error("Middleware authentication error:", error); + // If there's an error in authentication, redirect to login + const url = new URL("/login", request.url); + url.searchParams.set("error", "AuthError"); + return NextResponse.redirect(url); + } +} + +// Configure which paths this middleware runs on +export const config = { + matcher: [ + // Match all paths except those starting with _next, public, or ending with specific file extensions + "/((?!_next/static|_next/image|favicon.ico|.*\\.png$|.*\\.jpg$|.*\\.svg$).*)", + ], +};