Merge commit from fork

* fix: Improve host header handling and deployment mode detection

* ZITADEL_CLOUD env

* refactor service config

* service config

* cleanup

* cleanup

* util for original and public host

* instanceHost, publicHost

* cleanup constructUrl

* cleanup middleware

* add x-zitadel-forward-host as fallback for current cloud setup

* fix build

* fix: lint

* fix: update test mocks

* fix: provide basepath to unit tests

* host handling

* unit tests

* imports

* fix: getPublicHost instead of getInstanceHost

* unit tests

* update nextjs, implement jwt verify for logout_token

* pass logout information in jwt to login v2

* handle hosts in jwks url

* point idp callbacks to the api and not the login UI

* logout_hint out of logout token, typecheck

* rm unused imports

* fix oidc integration test

---------

Co-authored-by: Livio Spring <[email protected]>
This commit is contained in:
Max Peintner
2025-12-08 10:01:49 +01:00
committed by GitHub
co-authored by Livio Spring
parent f9e3871bb3
commit df75be96ff
75 changed files with 2075 additions and 1817 deletions
+15 -23
View File
@@ -6,21 +6,22 @@ import {
listAuthenticationMethodTypes,
startIdentityProviderFlow,
startLDAPIdentityProviderFlow,
ServiceConfig,
} from "@/lib/zitadel";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { completeFlowOrGetUrl } from "../client";
import { getServiceUrlFromHeaders } from "../service-url";
import { getServiceConfig } from "../service-url";
import { checkEmailVerification, checkMFAFactors } from "../verify-helper";
import { createSessionForIdpAndUpdateCookie } from "./cookie";
import { getOriginalHost } from "./host";
import { getPublicHost } from "./host";
export type RedirectToIdpState = { error?: string | null } | undefined;
export async function redirectToIdp(prevState: RedirectToIdpState, formData: FormData): Promise<RedirectToIdpState> {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const host = await getOriginalHost();
const { serviceConfig } = getServiceConfig(_headers);
const host = getPublicHost(_headers);
const params = new URLSearchParams();
@@ -43,7 +44,7 @@ export async function redirectToIdp(prevState: RedirectToIdpState, formData: For
}
const response = await startIDPFlow({
serviceUrl,
serviceConfig,
host,
idpId,
successUrl: `/idp/${provider}/process?` + params.toString(),
@@ -62,7 +63,7 @@ export async function redirectToIdp(prevState: RedirectToIdpState, formData: For
}
export type StartIDPFlowCommand = {
serviceUrl: string;
serviceConfig: ServiceConfig;
host: string;
idpId: string;
successUrl: string;
@@ -73,7 +74,7 @@ async function startIDPFlow(command: StartIDPFlowCommand) {
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
const url = await startIdentityProviderFlow({
serviceUrl: command.serviceUrl,
serviceConfig: command.serviceConfig,
idpId: command.idpId,
urls: {
successUrl: `${command.host.includes("localhost") ? "http://" : "https://"}${command.host}${basePath}${command.successUrl}`,
@@ -103,25 +104,19 @@ export type CreateNewSessionCommand = {
export async function createNewSessionFromIdpIntent(command: CreateNewSessionCommand) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const { serviceConfig } = getServiceConfig(_headers);
if (!command.userId || !command.idpIntent) {
throw new Error("No userId or loginName provided");
}
const userResponse = await getUserByID({
serviceUrl,
userId: command.userId,
});
const userResponse = await getUserByID({ serviceConfig, userId: command.userId });
if (!userResponse || !userResponse.user) {
return { error: "User not found in the system" };
}
const loginSettings = await getLoginSettings({
serviceUrl,
organization: userResponse.user.details?.resourceOwner,
});
const loginSettings = await getLoginSettings({ serviceConfig, organization: userResponse.user.details?.resourceOwner });
const session = await createSessionForIdpAndUpdateCookie({
userId: command.userId,
@@ -146,17 +141,14 @@ export async function createNewSessionFromIdpIntent(command: CreateNewSessionCom
// check if user has MFA methods
let authMethods;
if (session.factors?.user?.id) {
const response = await listAuthenticationMethodTypes({
serviceUrl,
userId: session.factors.user.id,
});
const response = await listAuthenticationMethodTypes({ serviceConfig, userId: session.factors.user.id });
if (response.authMethodTypes && response.authMethodTypes.length) {
authMethods = response.authMethodTypes;
}
}
const mfaFactorCheck = await checkMFAFactors(
serviceUrl,
serviceConfig,
session,
loginSettings,
authMethods || [], // Pass empty array if no auth methods
@@ -193,14 +185,14 @@ type createNewSessionForLDAPCommand = {
export async function createNewSessionForLDAP(command: createNewSessionForLDAPCommand) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const { serviceConfig } = getServiceConfig(_headers);
if (!command.username || !command.password) {
return { error: "No username or password provided" };
}
const response = await startLDAPIdentityProviderFlow({
serviceUrl,
serviceConfig,
idpId: command.idpId,
username: command.username,
password: command.password,