use x-zitadel-forward-host header as target api, util

This commit is contained in:
Max Peintner
2025-01-28 09:41:03 +01:00
parent 6f295bce1b
commit 44bb8588de
6 changed files with 46 additions and 90 deletions

View File

@@ -35,6 +35,7 @@ const secureHeaders = [
];
const nextConfig = {
basePath: "/login",
reactStrictMode: true, // Recommended for the `pages` directory, default in `app`.
experimental: {
dynamicIO: true,

View File

@@ -1,27 +1,4 @@
import { newSystemToken } from "@zitadel/client/node";
import { getInstanceDomainByHost } from "./zitadel";
export async function getInstanceUrl(host: string): Promise<string> {
const [hostname, port] = host.split(":");
if (hostname === "localhost") {
console.log("fallback to ZITADEL_API_URL");
return process.env.ZITADEL_API_URL || "";
}
const instanceDomain = await getInstanceDomainByHost(host).catch((error) => {
console.error(`Could not get instance by host ${host}`, error);
return null;
});
if (!instanceDomain) {
throw new Error("No instance found");
}
console.log(`host: ${host}, api: ${instanceDomain}`);
return instanceDomain;
}
export async function systemAPIToken() {
const audience = process.env.AUDIENCE;

View File

@@ -3,24 +3,13 @@
import { createServerTransport } from "@zitadel/client/node";
import { createUserServiceClient } from "@zitadel/client/v2";
import { headers } from "next/headers";
import { getInstanceUrl } from "./api";
import { getSessionCookieById } from "./cookies";
import { getApiUrlOfHeaders } from "./service";
import { getSession } from "./zitadel";
const transport = async (host: string, token: string) => {
let instanceUrl;
try {
instanceUrl = await getInstanceUrl(host);
} catch (error) {
console.error(
`Could not get instance url for ${host}, fallback to ZITADEL_API_URL`,
error,
);
instanceUrl = process.env.ZITADEL_API_URL;
}
return createServerTransport(token, {
baseUrl: instanceUrl,
baseUrl: host,
});
};
@@ -36,16 +25,17 @@ export async function setMyPassword({
sessionId: string;
password: string;
}) {
const host = (await headers()).get("host");
const _headers = await headers();
const instanceUrl = getApiUrlOfHeaders(_headers);
if (!host || typeof host !== "string") {
if (!instanceUrl) {
throw new Error("No host found");
}
const sessionCookie = await getSessionCookieById({ sessionId });
const { session } = await getSession({
host,
host: instanceUrl,
sessionId: sessionCookie.id,
sessionToken: sessionCookie.token,
});
@@ -54,7 +44,7 @@ export async function setMyPassword({
return { error: "Could not load session" };
}
const service = await myUserService(host, `${sessionCookie.token}`);
const service = await myUserService(instanceUrl, `${sessionCookie.token}`);
if (!session?.factors?.user?.id) {
return { error: "No user id found in session" };

View File

@@ -28,9 +28,9 @@ import {
SetPasswordRequestSchema,
} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
import { headers } from "next/headers";
import { getInstanceUrl } from "../api";
import { getNextUrl } from "../client";
import { getSessionCookieById, getSessionCookieByLoginName } from "../cookies";
import { getApiUrlOfHeaders } from "../service";
import {
checkEmailVerification,
checkMFAFactors,
@@ -281,16 +281,17 @@ export async function checkSessionAndSetPassword({
sessionId,
password,
}: CheckSessionAndSetPasswordCommand) {
const host = (await headers()).get("host");
const _headers = await headers();
const instanceUrl = getApiUrlOfHeaders(_headers);
if (!host || typeof host !== "string") {
if (!instanceUrl) {
throw new Error("No host found");
}
const sessionCookie = await getSessionCookieById({ sessionId });
const { session } = await getSession({
host,
host: instanceUrl,
sessionId: sessionCookie.id,
sessionToken: sessionCookie.token,
});
@@ -308,7 +309,7 @@ export async function checkSessionAndSetPassword({
// check if the user has no password set in order to set a password
const authmethods = await listAuthenticationMethodTypes({
host,
host: instanceUrl,
userId: session.factors.user.id,
});
@@ -328,7 +329,7 @@ export async function checkSessionAndSetPassword({
);
const loginSettings = await getLoginSettings({
host,
host: instanceUrl,
organization: session.factors.user.organizationId,
});
@@ -348,19 +349,8 @@ export async function checkSessionAndSetPassword({
});
} else {
const transport = async (host: string, token: string) => {
let instanceUrl;
try {
instanceUrl = await getInstanceUrl(host);
} catch (error) {
console.error(
`Could not get instance url for ${host}, fallback to ZITADEL_API_URL`,
error,
);
instanceUrl = process.env.ZITADEL_API_URL;
}
return createServerTransport(token, {
baseUrl: instanceUrl,
baseUrl: host,
});
};
@@ -369,7 +359,10 @@ export async function checkSessionAndSetPassword({
return createUserServiceClient(transportPromise);
};
const selfService = await myUserService(host, `${sessionCookie.token}`);
const selfService = await myUserService(
instanceUrl,
`${sessionCookie.token}`,
);
return selfService
.setPassword(

View File

@@ -6,7 +6,8 @@ import { OrganizationService } from "@zitadel/proto/zitadel/org/v2/org_service_p
import { SessionService } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
import { SettingsService } from "@zitadel/proto/zitadel/settings/v2/settings_service_pb";
import { UserService } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
import { getInstanceUrl, systemAPIToken } from "./api";
import { ReadonlyHeaders } from "next/dist/server/web/spec-extension/adapters/headers";
import { systemAPIToken } from "./api";
type ServiceClass =
| typeof IdentityProviderService
@@ -20,26 +21,34 @@ export async function createServiceForHost<T extends ServiceClass>(
service: T,
host: string,
) {
let instanceUrl, token;
try {
instanceUrl = await getInstanceUrl(host);
token = await systemAPIToken();
} catch (error) {
console.error(
`Could not get instance url for ${host}, fallback to ZITADEL_API_URL`,
error,
);
instanceUrl = process.env.ZITADEL_API_URL;
token = process.env.ZITADEL_SERVICE_USER_TOKEN;
}
const token = await systemAPIToken();
if (!instanceUrl || !token) {
if (!host || !token) {
throw new Error("No instance url or token found");
}
const transport = createServerTransport(token, {
baseUrl: instanceUrl,
baseUrl: host,
});
return createClientFor<T>(service)(transport);
}
export function getApiUrlOfHeaders(headers: ReadonlyHeaders): string {
let instanceUrl: string = process.env.ZITADEL_API_URL;
if (headers.get("x-zitadel-forward-host")) {
instanceUrl = headers.get("x-zitadel-forward-host") as string;
} else {
const host = headers.get("host");
if (host) {
const [hostname, port] = host.split(":");
if (hostname !== "localhost") {
instanceUrl = host;
}
}
}
return instanceUrl;
}

View File

@@ -1,6 +1,6 @@
import { headers } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { getInstanceUrl } from "./lib/api";
import { getApiUrlOfHeaders } from "./lib/service";
export const config = {
matcher: [
@@ -21,22 +21,8 @@ export async function middleware(request: NextRequest) {
// return NextResponse.next();
// }
const _headers = await headers();
const _host = _headers.get("host");
console.log("host", _host);
const host = _host || request.nextUrl.host;
let instanceUrl;
try {
instanceUrl = await getInstanceUrl(host);
} catch (error) {
console.error(
`[Middleware]: Could not get instance url of ${host}, fallback to ZITADEL_API_URL ${process.env.ZITADEL_API_URL}`,
error,
);
instanceUrl = process.env.ZITADEL_API_URL;
}
const instanceUrl = getApiUrlOfHeaders(_headers);
const instanceHost = `${instanceUrl}`.replace("https://", "");