import { getNewTokensWithRefreshToken, getSession, } from "@/features/auth/services/auth.service"; import { envVars } from "@/lib/env"; import { getDefaultDashboardRoute, getRouteOwner, isAuthRoute, UserRole, } from "@/lib/utils/auth"; import { jwtUtils } from "@/lib/utils/jwt"; import { isTokenExpiringSoon } from "@/lib/utils/token"; import { NextRequest, NextResponse } from "next/server"; async function refreshTokenMiddleware(refreshToken: string): Promise { try { const refresh = await getNewTokensWithRefreshToken(refreshToken); if (!refresh) { return false; } return true; } catch (error) { console.error("Error refreshing token in middleware:", error); return false; } } export async function proxy(request: NextRequest) { try { const { pathname } = request.nextUrl; const accessToken = request.cookies.get("accessToken")?.value; const refreshToken = request.cookies.get("refreshToken")?.value; const decodedAccessToken = accessToken && jwtUtils.verifyToken(accessToken, envVars.JWT_ACCESS_SECRET as string) .data; const isValidAccessToken = accessToken && jwtUtils.verifyToken(accessToken, envVars.JWT_ACCESS_SECRET as string) .success; let userRole: UserRole | null = null; if (decodedAccessToken) { userRole = (decodedAccessToken.role as UserRole) || null; } const normalizedUserRole = userRole === "ADMIN" ? "ADMIN" : "USER"; const routerOwner = getRouteOwner(pathname); const isAuth = isAuthRoute(pathname); if ( isValidAccessToken && refreshToken && (await isTokenExpiringSoon(accessToken)) ) { const requestHeaders = new Headers(request.headers); const response = NextResponse.next({ request: { headers: requestHeaders, }, }); try { const refreshed = await refreshTokenMiddleware(refreshToken); if (refreshed) { requestHeaders.set("x-token-refreshed", "1"); } return NextResponse.next({ request: { headers: requestHeaders, }, headers: response.headers, }); } catch (error) { console.error("Error refreshing token:", error); } return response; } if (isAuth && isValidAccessToken) { return NextResponse.redirect( new URL(getDefaultDashboardRoute(userRole as UserRole), request.url), ); } if (pathname === "/reset-password") { const email = request.nextUrl.searchParams.get("email"); if (accessToken && email) { const userInfo = await getSession(); if (userInfo.needPasswordChange) { return NextResponse.next(); } else { return NextResponse.redirect( new URL( getDefaultDashboardRoute(userRole as UserRole), request.url, ), ); } } if (email) { return NextResponse.next(); } const loginUrl = new URL("/login", request.url); loginUrl.searchParams.set("redirect", pathname); return NextResponse.redirect(loginUrl); } if (routerOwner === null || routerOwner === "COMMON") { return NextResponse.next(); } if (!accessToken || !isValidAccessToken) { const loginUrl = new URL("/login", request.url); loginUrl.searchParams.set("redirect", pathname); return NextResponse.redirect(loginUrl); } if (accessToken) { const userInfo = await getSession(); if (userInfo) { if (userInfo.emailVerified === false && pathname !== "/verify-email") { const verifyEmailUrl = new URL("/verify-email", request.url); verifyEmailUrl.searchParams.set("email", userInfo.email); return NextResponse.redirect(verifyEmailUrl); } if (userInfo.needPasswordChange && pathname !== "/reset-password") { const resetPasswordUrl = new URL("/reset-password", request.url); resetPasswordUrl.searchParams.set("email", userInfo.email); return NextResponse.redirect(resetPasswordUrl); } } } const routeOwnerNormalized = routerOwner === "ADMIN" ? "ADMIN" : "USER"; if (routeOwnerNormalized === "ADMIN" && normalizedUserRole !== "ADMIN") { return NextResponse.redirect( new URL( getDefaultDashboardRoute(normalizedUserRole as UserRole), request.url, ), ); } return NextResponse.next(); } catch (error) { console.error("Error in proxy middleware:", error); } } export const config = { matcher: [ "/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|.well-known).*)", ], };