import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; import { jwtVerify } from "jose"; const COOKIE_NAME = "auth_token"; const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET!); function isExpired(exp?: number) { return exp ? exp * 1000 <= Date.now() : false; } async function validateToken(token: string) { const raw = token.startsWith("Bearer ") ? token.slice(7) : token; try { const { payload } = await jwtVerify(raw, JWT_SECRET, { algorithms: ["HS256"], }); return payload as { exp?: number; MustChangePassword?: boolean; [key: string]: unknown; }; } catch (err) { console.error("Token validation error:", err); return null; } } export async function middleware(request: NextRequest) { const token = request.cookies.get(COOKIE_NAME)?.value; const loginUrl = new URL("/login", request.url); const currentPath = request.nextUrl.pathname; // 1️⃣ No token if (!token) { loginUrl.searchParams.set("reason", "no-token"); loginUrl.searchParams.set("redirect", currentPath); return NextResponse.redirect(loginUrl); } // 2️⃣ Validate + decode const payload = await validateToken(token); if (!payload) { const res = NextResponse.redirect(loginUrl); res.cookies.delete(COOKIE_NAME); loginUrl.searchParams.set("reason", "invalid-token"); loginUrl.searchParams.set("redirect", currentPath); return res; } // 3️⃣ Expiry check if (isExpired(payload.exp)) { const res = NextResponse.redirect(loginUrl); res.cookies.delete(COOKIE_NAME); loginUrl.searchParams.set("reason", "expired-token"); loginUrl.searchParams.set("redirect", currentPath); return res; } // 4️⃣ Must change password check if (payload.MustChangePassword) { loginUrl.searchParams.set("reason", "change-password"); loginUrl.searchParams.set("redirect", currentPath); return NextResponse.redirect(loginUrl); } // ✅ All good return NextResponse.next(); } export const config = { matcher: [ "/dashboard/:path*", "/settings/:path*", "/admin/:path*", "/change-password/:path*", ], };