import { NextResponse } from "next/server"; import { withAuth, NextRequestWithAuth } from "next-auth/middleware"; const rewriteURL = ( request: NextRequestWithAuth, originBaseURL: string, destinationBaseURL: string, headers: any = {}, ) => { const destinationURL = request.url.replace(originBaseURL, destinationBaseURL); console.log(`Rewriting ${request.url} to ${destinationURL}`); const requestHeaders = new Headers(request.headers); requestHeaders.delete("x-forwarded-user"); requestHeaders.delete("connection"); for (const [key, value] of Object.entries(headers)) { requestHeaders.set(key, value as string); } return NextResponse.rewrite(new URL(destinationURL), { request: { headers: requestHeaders }, }); }; const checkRewrites = async (request: NextRequestWithAuth) => { const linkBaseURL = process.env.LINK_URL ?? "http://localhost:3000"; const zammadURL = process.env.ZAMMAD_URL ?? "http://zammad-nginx:8080"; const zammadPaths = [ "/zammad", "/auth/sso", "/assets", "/mobile", "/graphql", "/cable", ]; const { token } = request.nextauth; const email = token?.email?.toLowerCase() ?? "unknown"; let headers = { "x-forwarded-user": email }; if (request.nextUrl.pathname.startsWith("/zammad")) { return rewriteURL(request, `${linkBaseURL}/zammad`, zammadURL, headers); } else if (zammadPaths.some((p) => request.nextUrl.pathname.startsWith(p))) { return rewriteURL(request, linkBaseURL, zammadURL, headers); } else { const isDev = process.env.NODE_ENV === "development"; const nonce = Buffer.from(crypto.randomUUID()).toString("base64"); const cspHeader = ` default-src 'self'; connect-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic' ${isDev ? "'unsafe-eval'" : ""}; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data:; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests; `; const contentSecurityPolicyHeaderValue = cspHeader .replace(/\s{2,}/g, " ") .trim(); const requestHeaders = new Headers(request.headers); requestHeaders.set("x-nonce", nonce); requestHeaders.set( "Content-Security-Policy", contentSecurityPolicyHeaderValue, ); const response = NextResponse.next({ request: { headers: requestHeaders, }, }); response.headers.set( "Content-Security-Policy", contentSecurityPolicyHeaderValue, ); return response; } }; export default withAuth(checkRewrites, { pages: { signIn: `/login`, }, callbacks: { authorized: ({ token, req }) => { if (process.env.SETUP_MODE === "true") { return true; } const path = req.nextUrl.pathname; const roles: any = token?.roles ?? []; if (path.startsWith("/admin") && !roles.includes("admin")) { return false; } if (roles.includes("admin") || roles.includes("agent")) { return true; } return false; }, }, }); export const config = { matcher: ["/((?!ws|wss|api|_next/static|_next/image|favicon.ico).*)"], };