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
+27 -58
View File
@@ -20,30 +20,26 @@ import { cookies, headers } from "next/headers";
import { completeFlowOrGetUrl } from "../client";
import { getSessionCookieByLoginName } from "../cookies";
import { getOrSetFingerprintId } from "../fingerprint";
import { getServiceUrlFromHeaders } from "../service-url";
import { getServiceConfig } from "../service-url";
import { loadMostRecentSession } from "../session";
import { checkMFAFactors } from "../verify-helper";
import { createSessionAndUpdateCookie } from "./cookie";
import { getOriginalHostWithProtocol } from "./host";
import { getPublicHostWithProtocol } from "./host";
import { getTranslations } from "next-intl/server";
export async function verifyTOTP(code: string, loginName?: string, organization?: string) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const { serviceConfig } = getServiceConfig(_headers);
return loadMostRecentSession({
serviceUrl,
serviceConfig,
sessionParams: {
loginName,
organization,
},
}).then((session) => {
if (session?.factors?.user?.id) {
return verifyTOTPRegistration({
serviceUrl,
code,
userId: session.factors.user.id,
});
return verifyTOTPRegistration({ serviceConfig, code, userId: session.factors.user.id });
} else {
throw Error("No user id found in session.");
}
@@ -62,22 +58,14 @@ type VerifyUserByEmailCommand = {
export async function sendVerification(command: VerifyUserByEmailCommand) {
const t = await getTranslations("verify");
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const { serviceConfig } = getServiceConfig(_headers);
const verifyResponse = command.isInvite
? await verifyInviteCode({
serviceUrl,
userId: command.userId,
verificationCode: command.code,
}).catch((error) => {
? await verifyInviteCode({ serviceConfig, userId: command.userId, verificationCode: command.code }).catch((error) => {
console.warn(error);
return { error: t("errors.couldNotVerifyInvite") };
})
: await verifyEmail({
serviceUrl,
userId: command.userId,
verificationCode: command.code,
}).catch((error) => {
: await verifyEmail({ serviceConfig, userId: command.userId, verificationCode: command.code }).catch((error) => {
console.warn(error);
return { error: t("errors.couldNotVerifyEmail") };
});
@@ -91,10 +79,7 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
}
let session: Session | undefined;
const userResponse = await getUserByID({
serviceUrl,
userId: command.userId,
});
const userResponse = await getUserByID({ serviceConfig, userId: command.userId });
if (!userResponse || !userResponse.user) {
return { error: t("errors.couldNotLoadUser") };
@@ -110,22 +95,17 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
});
if (sessionCookie) {
session = await getSession({
serviceUrl,
sessionId: sessionCookie.id,
sessionToken: sessionCookie.token,
}).then((response) => {
if (response?.session) {
return response.session;
}
});
session = await getSession({ serviceConfig, sessionId: sessionCookie.id, sessionToken: sessionCookie.token }).then(
(response) => {
if (response?.session) {
return response.session;
}
},
);
}
// load auth methods for user
const authMethodResponse = await listAuthenticationMethodTypes({
serviceUrl,
userId: user.userId,
});
const authMethodResponse = await listAuthenticationMethodTypes({ serviceConfig, userId: user.userId });
if (!authMethodResponse || !authMethodResponse.authMethodTypes) {
return { error: t("errors.couldNotLoadAuthenticators") };
@@ -203,14 +183,11 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
return { redirect: `/verify/success?${verifySuccessParams}` };
}
const loginSettings = await getLoginSettings({
serviceUrl,
organization: user.details?.resourceOwner,
});
const loginSettings = await getLoginSettings({ serviceConfig, organization: user.details?.resourceOwner });
// redirect to mfa factor if user has one, or redirect to set one up
const mfaFactorCheck = await checkMFAFactors(
serviceUrl,
serviceConfig,
session,
loginSettings,
authMethodResponse.authMethodTypes,
@@ -253,14 +230,14 @@ type resendVerifyEmailCommand = {
export async function resendVerification(command: resendVerifyEmailCommand) {
const t = await getTranslations("verify");
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const hostWithProtocol = await getOriginalHostWithProtocol();
const { serviceConfig } = getServiceConfig(_headers);
const hostWithProtocol = await getPublicHostWithProtocol(_headers);
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
return command.isInvite
? createInviteCode({
serviceUrl,
serviceConfig,
userId: command.userId,
urlTemplate:
`${hostWithProtocol}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true` +
@@ -272,8 +249,8 @@ export async function resendVerification(command: resendVerifyEmailCommand) {
return { error: t("errors.couldNotResendInvite") };
})
: zitadelSendEmailCode({
serviceConfig,
userId: command.userId,
serviceUrl,
urlTemplate:
`${hostWithProtocol}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` +
(command.requestId ? `&requestId=${command.requestId}` : ""),
@@ -287,22 +264,14 @@ type SendEmailCommand = {
export async function sendEmailCode(command: SendEmailCommand) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const { serviceConfig } = getServiceConfig(_headers);
return zitadelSendEmailCode({
serviceUrl,
userId: command.userId,
urlTemplate: command.urlTemplate,
});
return zitadelSendEmailCode({ serviceConfig, userId: command.userId, urlTemplate: command.urlTemplate });
}
export async function sendInviteEmailCode(command: SendEmailCommand) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const { serviceConfig } = getServiceConfig(_headers);
return createInviteCode({
serviceUrl,
userId: command.userId,
urlTemplate: command.urlTemplate,
});
return createInviteCode({ serviceConfig, userId: command.userId, urlTemplate: command.urlTemplate });
}