import { SecuritySettings } from "@zitadel/proto/zitadel/settings/v2/security_settings_pb"; import { headers } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; import { DEFAULT_CSP } from "../constants/csp"; import { getServiceConfig } from "./lib/service-url"; export const config = { matcher: ["/.well-known/:path*", "/oauth/:path*", "/oidc/:path*", "/idps/callback/:path*", "/saml/:path*", "/:path*"], }; async function loadSecuritySettings(request: NextRequest): Promise { const securityResponse = await fetch(`${request.nextUrl.origin}/security`); if (!securityResponse.ok) { console.error("Failed to fetch security settings:", securityResponse.statusText); return null; } const response = await securityResponse.json(); if (!response || !response.settings) { console.error("No security settings found in the response."); return null; } return response.settings; } export async function middleware(request: NextRequest) { // Add the original URL as a header to all requests const requestHeaders = new Headers(request.headers); // Extract "organization" search param from the URL and set it as a header if available const organization = request.nextUrl.searchParams.get("organization"); if (organization) { requestHeaders.set("x-zitadel-i18n-organization", organization); } // Only run the rest of the logic for the original matcher paths const proxyPaths = ["/.well-known/", "/oauth/", "/oidc/", "/idps/callback/", "/saml/"]; const isMatched = proxyPaths.some((prefix) => request.nextUrl.pathname.startsWith(prefix)); // Only proxy in self-hosted mode if (!isMatched) { // For multi-tenant or non-proxied routes, just add the header and continue return NextResponse.next({ request: { headers: requestHeaders }, }); } const _headers = await headers(); const { serviceConfig } = getServiceConfig(_headers); // add additional headers if available if (serviceConfig.publicHost) { requestHeaders.set("x-zitadel-public-host", serviceConfig.publicHost); } if (serviceConfig.instanceHost) { requestHeaders.set("x-zitadel-instance-host", serviceConfig.instanceHost); } const responseHeaders = new Headers(); responseHeaders.set("Access-Control-Allow-Origin", "*"); responseHeaders.set("Access-Control-Allow-Headers", "*"); const securitySettings = await loadSecuritySettings(request); if (securitySettings?.embeddedIframe?.enabled) { responseHeaders.set( "Content-Security-Policy", `${DEFAULT_CSP} frame-ancestors ${securitySettings.embeddedIframe.allowedOrigins.join(" ")};`, ); responseHeaders.delete("X-Frame-Options"); } request.nextUrl.href = `${serviceConfig.baseUrl}${request.nextUrl.pathname}${request.nextUrl.search}`; return NextResponse.rewrite(request.nextUrl, { request: { headers: requestHeaders, }, headers: responseHeaders, }); }