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
+16 -47
View File
@@ -20,10 +20,10 @@ import { headers } from "next/headers";
import { userAgent } from "next/server";
import { getTranslations } from "next-intl/server";
import { getMostRecentSessionCookie, getSessionCookieById, getSessionCookieByLoginName } from "../cookies";
import { getServiceUrlFromHeaders } from "../service-url";
import { getServiceConfig } from "../service-url";
import { checkEmailVerification, checkUserVerification } from "../verify-helper";
import { createSessionAndUpdateCookie, setSessionAndUpdateCookie } from "./cookie";
import { getOriginalHost } from "./host";
import { getPublicHost } from "./host";
import { completeFlowOrGetUrl } from "../client";
type VerifyPasskeyCommand = {
@@ -63,8 +63,8 @@ export async function registerPasskeyLink(
}
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const host = await getOriginalHost();
const { serviceConfig } = getServiceConfig(_headers);
const host = getPublicHost(_headers);
let session: GetSessionResponse | undefined;
let createdSession: Session | undefined;
@@ -74,11 +74,7 @@ export async function registerPasskeyLink(
if (command.sessionId) {
// Session-based flow (existing logic)
const sessionCookie = await getSessionCookieById({ sessionId: command.sessionId });
session = await getSession({
serviceUrl,
sessionId: sessionCookie.id,
sessionToken: sessionCookie.token,
});
session = await getSession({ serviceConfig, sessionId: sessionCookie.id, sessionToken: sessionCookie.token });
if (!session?.session?.factors?.user?.id) {
return { error: "Could not determine user from session" };
@@ -89,10 +85,7 @@ export async function registerPasskeyLink(
const sessionValid = isSessionValid(session.session);
if (!sessionValid.valid) {
const authmethods = await listAuthenticationMethodTypes({
serviceUrl,
userId: currentUserId,
});
const authmethods = await listAuthenticationMethodTypes({ serviceConfig, userId: currentUserId });
// if the user has no authmethods set, we need to check if the user was verified
if (authmethods.authMethodTypes.length !== 0) {
@@ -117,10 +110,7 @@ export async function registerPasskeyLink(
code: command.code,
};
} else {
const codeResponse = await createPasskeyRegistrationLink({
serviceUrl,
userId: currentUserId,
});
const codeResponse = await createPasskeyRegistrationLink({ serviceConfig, userId: currentUserId });
if (!codeResponse?.code?.code) {
return { error: "Could not create registration link" };
@@ -136,10 +126,7 @@ export async function registerPasskeyLink(
};
// Check if user exists
const userResponse = await getUserByID({
serviceUrl,
userId: currentUserId,
});
const userResponse = await getUserByID({ serviceConfig, userId: currentUserId });
if (!userResponse || !userResponse.user) {
return { error: "User not found" };
@@ -179,17 +166,12 @@ export async function registerPasskeyLink(
throw new Error("Could not determine user");
}
return registerPasskey({
serviceUrl,
userId: currentUserId,
code: registerCode,
domain: hostname,
});
return registerPasskey({ serviceConfig, userId: currentUserId, code: registerCode, domain: hostname });
}
export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const { serviceConfig } = getServiceConfig(_headers);
if (!command.sessionId && !command.userId) {
throw new Error("Either sessionId or userId must be provided");
@@ -214,11 +196,7 @@ export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) {
const sessionCookie = await getSessionCookieById({
sessionId: command.sessionId,
});
const session = await getSession({
serviceUrl,
sessionId: sessionCookie.id,
sessionToken: sessionCookie.token,
});
const session = await getSession({ serviceConfig, sessionId: sessionCookie.id, sessionToken: sessionCookie.token });
const userId = session?.session?.factors?.user?.id;
if (!userId) {
@@ -231,10 +209,7 @@ export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) {
currentUserId = command.userId!;
// Verify user exists
const userResponse = await getUserByID({
serviceUrl,
userId: currentUserId,
});
const userResponse = await getUserByID({ serviceConfig, userId: currentUserId });
if (!userResponse || !userResponse.user) {
throw new Error("User not found");
@@ -242,7 +217,7 @@ export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) {
}
return zitadelVerifyPasskeyRegistration({
serviceUrl,
serviceConfig,
request: create(VerifyPasskeyRegistrationRequestSchema, {
passkeyId: command.passkeyId,
publicKeyCredential: command.publicKeyCredential,
@@ -279,12 +254,9 @@ export async function sendPasskey(command: SendPasskeyCommand) {
}
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const { serviceConfig } = getServiceConfig(_headers);
const loginSettings = await getLoginSettings({
serviceUrl,
organization,
});
const loginSettings = await getLoginSettings({ serviceConfig, organization });
let lifetime = command.lifetime; // Use provided lifetime first
@@ -318,10 +290,7 @@ export async function sendPasskey(command: SendPasskeyCommand) {
let userResponse;
try {
userResponse = await getUserByID({
serviceUrl,
userId: session?.factors?.user?.id,
});
userResponse = await getUserByID({ serviceConfig, userId: session?.factors?.user?.id });
} catch (error) {
console.error("Error fetching user by ID:", error);
return { error: t("verify.errors.couldNotGetUser") };