mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-02 04:12:16 +00:00
94 lines
3.2 KiB
TypeScript
94 lines
3.2 KiB
TypeScript
import { SecuritySettings } from "@zitadel/proto/zitadel/settings/v2/security_settings_pb";
|
|
|
|
import { headers } from "next/headers";
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
import { buildCSP } from "./lib/csp";
|
|
import { createLogger } from "./lib/logger";
|
|
import { getServiceConfig } from "./lib/service-url";
|
|
|
|
export const runtime = "nodejs";
|
|
|
|
const logger = createLogger("middleware");
|
|
export const config = {
|
|
matcher: ["/.well-known/:path*", "/oauth/:path*", "/oidc/:path*", "/idps/callback/:path*", "/saml/:path*", "/:path*"],
|
|
};
|
|
|
|
async function loadSecuritySettings(request: NextRequest): Promise<SecuritySettings | null> {
|
|
const securityResponse = await fetch(`${request.nextUrl.origin}/security`);
|
|
|
|
if (!securityResponse.ok) {
|
|
logger.error("Failed to fetch security settings:", { status: securityResponse.statusText });
|
|
return null;
|
|
}
|
|
|
|
const response = await securityResponse.json();
|
|
|
|
if (!response || !response.settings) {
|
|
logger.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);
|
|
|
|
const iframeOrigins =
|
|
securitySettings?.embeddedIframe?.enabled && securitySettings.embeddedIframe.allowedOrigins.length > 0
|
|
? securitySettings.embeddedIframe.allowedOrigins
|
|
: undefined;
|
|
|
|
responseHeaders.set("Content-Security-Policy", buildCSP({ serviceUrl: serviceConfig.baseUrl, iframeOrigins }));
|
|
|
|
if (!iframeOrigins) {
|
|
responseHeaders.set("X-Frame-Options", "deny");
|
|
}
|
|
|
|
request.nextUrl.href = `${serviceConfig.baseUrl}${request.nextUrl.pathname}${request.nextUrl.search}`;
|
|
|
|
return NextResponse.rewrite(request.nextUrl, {
|
|
request: {
|
|
headers: requestHeaders,
|
|
},
|
|
headers: responseHeaders,
|
|
});
|
|
}
|