From 04f9b47960f8a89b3d75f9c4f0f1296dcf525547 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 29 Jan 2025 10:34:33 +0100 Subject: [PATCH] cleanup serviceUrl, host, basepath configurable --- apps/login/next.config.mjs | 2 +- apps/login/src/app/(login)/accounts/page.tsx | 17 +-- .../app/(login)/authenticator/set/page.tsx | 55 ++++--- .../(login)/idp/[provider]/failure/page.tsx | 9 +- .../(login)/idp/[provider]/success/page.tsx | 31 ++-- apps/login/src/app/(login)/idp/page.tsx | 11 +- apps/login/src/app/(login)/invite/page.tsx | 15 +- .../src/app/(login)/invite/success/page.tsx | 13 +- apps/login/src/app/(login)/loginname/page.tsx | 20 ++- apps/login/src/app/(login)/mfa/page.tsx | 23 ++- apps/login/src/app/(login)/mfa/set/page.tsx | 53 ++++--- .../src/app/(login)/otp/[method]/page.tsx | 16 +- .../src/app/(login)/otp/[method]/set/page.tsx | 19 +-- apps/login/src/app/(login)/passkey/page.tsx | 19 +-- .../src/app/(login)/passkey/set/page.tsx | 11 +- .../src/app/(login)/password/change/page.tsx | 15 +- apps/login/src/app/(login)/password/page.tsx | 15 +- .../src/app/(login)/password/set/page.tsx | 17 +-- apps/login/src/app/(login)/register/page.tsx | 17 +-- .../app/(login)/register/password/page.tsx | 17 +-- apps/login/src/app/(login)/signedin/page.tsx | 23 ++- apps/login/src/app/(login)/u2f/page.tsx | 12 +- apps/login/src/app/(login)/u2f/set/page.tsx | 11 +- apps/login/src/app/(login)/verify/page.tsx | 17 ++- apps/login/src/app/login/route.ts | 54 ++++--- apps/login/src/components/login-otp.tsx | 4 +- apps/login/src/lib/self.ts | 18 +-- apps/login/src/lib/server/cookie.ts | 33 ++-- apps/login/src/lib/server/idp.ts | 14 +- apps/login/src/lib/server/invite.ts | 10 +- apps/login/src/lib/server/loginname.ts | 34 ++--- apps/login/src/lib/server/otp.ts | 9 +- apps/login/src/lib/server/passkeys.ts | 32 ++-- apps/login/src/lib/server/password.ts | 57 +++---- apps/login/src/lib/server/register.ts | 10 +- apps/login/src/lib/server/session.ts | 35 ++--- apps/login/src/lib/server/u2f.ts | 16 +- apps/login/src/lib/server/verify.ts | 76 ++++------ apps/login/src/lib/service.ts | 6 +- apps/login/src/lib/session.ts | 6 +- apps/login/src/lib/zitadel.ts | 142 ++++++++---------- apps/login/src/middleware.ts | 6 +- turbo.json | 3 +- 43 files changed, 426 insertions(+), 597 deletions(-) diff --git a/apps/login/next.config.mjs b/apps/login/next.config.mjs index c3771f9dd4d..32209b11e7a 100755 --- a/apps/login/next.config.mjs +++ b/apps/login/next.config.mjs @@ -35,7 +35,7 @@ const secureHeaders = [ ]; const nextConfig = { - basePath: "/new-login", + basePath: process.env.NEXT_PUBLIC_BASE_PATH, reactStrictMode: true, // Recommended for the `pages` directory, default in `app`. experimental: { dynamicIO: true, diff --git a/apps/login/src/app/(login)/accounts/page.tsx b/apps/login/src/app/(login)/accounts/page.tsx index f17c799a93c..bd0b428aae8 100644 --- a/apps/login/src/app/(login)/accounts/page.tsx +++ b/apps/login/src/app/(login)/accounts/page.tsx @@ -13,12 +13,12 @@ import { getLocale, getTranslations } from "next-intl/server"; import { headers } from "next/headers"; import Link from "next/link"; -async function loadSessions(host: string) { +async function loadSessions({ serviceUrl }: { serviceUrl: string }) { const ids: (string | undefined)[] = await getAllSessionCookieIds(); if (ids && ids.length) { const response = await listSessions({ - host, + serviceUrl, ids: ids.filter((id) => !!id) as string[], }); return response?.sessions ?? []; @@ -39,25 +39,20 @@ export default async function Page(props: { const organization = searchParams?.organization; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); let defaultOrganization; if (!organization) { - const org: Organization | null = await getDefaultOrg({ host }); + const org: Organization | null = await getDefaultOrg({ serviceUrl }); if (org) { defaultOrganization = org.id; } } - let sessions = await loadSessions(host); + let sessions = await loadSessions({ serviceUrl }); const branding = await getBrandingSettings({ - host, + serviceUrl, organization: organization ?? defaultOrganization, }); diff --git a/apps/login/src/app/(login)/authenticator/set/page.tsx b/apps/login/src/app/(login)/authenticator/set/page.tsx index 1c880144166..dd99ad043f2 100644 --- a/apps/login/src/app/(login)/authenticator/set/page.tsx +++ b/apps/login/src/app/(login)/authenticator/set/page.tsx @@ -30,16 +30,11 @@ export default async function Page(props: { const { loginName, authRequestId, organization, sessionId } = searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const sessionWithData = sessionId - ? await loadSessionById(host, sessionId, organization) - : await loadSessionByLoginname(host, loginName, organization); + ? await loadSessionById(serviceUrl, sessionId, organization) + : await loadSessionByLoginname(serviceUrl, loginName, organization); async function getAuthMethodsAndUser(host: string, session?: Session) { const userId = session?.factors?.user?.id; @@ -48,20 +43,24 @@ export default async function Page(props: { throw Error("Could not get user id from session"); } - return listAuthenticationMethodTypes({ host, userId }).then((methods) => { - return getUserByID({ host, userId }).then((user) => { - const humanUser = - user.user?.type.case === "human" ? user.user?.type.value : undefined; + return listAuthenticationMethodTypes({ serviceUrl, userId }).then( + (methods) => { + return getUserByID({ serviceUrl, userId }).then((user) => { + const humanUser = + user.user?.type.case === "human" + ? user.user?.type.value + : undefined; - return { - factors: session?.factors, - authMethods: methods.authMethodTypes ?? [], - phoneVerified: humanUser?.phone?.isVerified ?? false, - emailVerified: humanUser?.email?.isVerified ?? false, - expirationDate: session?.expirationDate, - }; - }); - }); + return { + factors: session?.factors, + authMethods: methods.authMethodTypes ?? [], + phoneVerified: humanUser?.phone?.isVerified ?? false, + emailVerified: humanUser?.email?.isVerified ?? false, + expirationDate: session?.expirationDate, + }; + }); + }, + ); } async function loadSessionByLoginname( @@ -70,13 +69,13 @@ export default async function Page(props: { organization?: string, ) { return loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization, }, }).then((session) => { - return getAuthMethodsAndUser(host, session); + return getAuthMethodsAndUser(serviceUrl, session); }); } @@ -87,11 +86,11 @@ export default async function Page(props: { ) { const recent = await getSessionCookieById({ sessionId, organization }); return getSession({ - host, + serviceUrl, sessionId: recent.id, sessionToken: recent.token, }).then((sessionResponse) => { - return getAuthMethodsAndUser(host, sessionResponse.session); + return getAuthMethodsAndUser(serviceUrl, sessionResponse.session); }); } @@ -100,17 +99,17 @@ export default async function Page(props: { } const branding = await getBrandingSettings({ - host, + serviceUrl, organization: sessionWithData.factors?.user?.organizationId, }); const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: sessionWithData.factors?.user?.organizationId, }); const identityProviders = await getActiveIdentityProviders({ - host, + serviceUrl, orgId: sessionWithData.factors?.user?.organizationId, linking_allowed: true, }).then((resp) => { diff --git a/apps/login/src/app/(login)/idp/[provider]/failure/page.tsx b/apps/login/src/app/(login)/idp/[provider]/failure/page.tsx index e368b8c070d..ed7b63092bd 100644 --- a/apps/login/src/app/(login)/idp/[provider]/failure/page.tsx +++ b/apps/login/src/app/(login)/idp/[provider]/failure/page.tsx @@ -25,14 +25,9 @@ export default async function Page(props: { const { organization } = searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } - - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); return ( diff --git a/apps/login/src/app/(login)/idp/[provider]/success/page.tsx b/apps/login/src/app/(login)/idp/[provider]/success/page.tsx index 88b4c6bdeba..c39f2515ab7 100644 --- a/apps/login/src/app/(login)/idp/[provider]/success/page.tsx +++ b/apps/login/src/app/(login)/idp/[provider]/success/page.tsx @@ -40,21 +40,16 @@ export default async function Page(props: { const { provider } = params; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } - - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); if (!provider || !id || !token) { return loginFailed(branding, "IDP context missing"); } const intent = await retrieveIDPIntent({ - serviceUrl: instanceUrl, + serviceUrl, id, token, }); @@ -77,7 +72,7 @@ export default async function Page(props: { return loginFailed(branding, "IDP information missing"); } - const idp = await getIDPByID({ host, id: idpInformation.idpId }); + const idp = await getIDPByID({ serviceUrl, id: idpInformation.idpId }); const options = idp?.config?.options; if (!idp) { @@ -95,7 +90,7 @@ export default async function Page(props: { let idpLink; try { idpLink = await addIDPLink({ - host, + serviceUrl, idp: { id: idpInformation.idpId, userId: idpInformation.userId, @@ -126,20 +121,20 @@ export default async function Page(props: { const email = PROVIDER_MAPPING[providerType](idpInformation).email?.email; if (options.autoLinking === AutoLinkingOption.EMAIL && email) { - foundUser = await listUsers({ host, email }).then((response) => { + foundUser = await listUsers({ serviceUrl, email }).then((response) => { return response.result ? response.result[0] : null; }); } else if (options.autoLinking === AutoLinkingOption.USERNAME) { foundUser = await listUsers( options.autoLinking === AutoLinkingOption.USERNAME - ? { host, userName: idpInformation.userName } - : { host, email }, + ? { serviceUrl, userName: idpInformation.userName } + : { serviceUrl, email }, ).then((response) => { return response.result ? response.result[0] : null; }); } else { foundUser = await listUsers({ - host, + serviceUrl, userName: idpInformation.userName, email, }).then((response) => { @@ -151,7 +146,7 @@ export default async function Page(props: { let idpLink; try { idpLink = await addIDPLink({ - host, + serviceUrl, idp: { id: idpInformation.idpId, userId: idpInformation.userId, @@ -192,12 +187,12 @@ export default async function Page(props: { const suffix = matched?.[1] ?? ""; // this just returns orgs where the suffix is set as primary domain - const orgs = await getOrgsByDomain({ host, domain: suffix }); + const orgs = await getOrgsByDomain({ serviceUrl, domain: suffix }); const orgToCheckForDiscovery = orgs.result && orgs.result.length === 1 ? orgs.result[0].id : undefined; const orgLoginSettings = await getLoginSettings({ - host, + serviceUrl, organization: orgToCheckForDiscovery, }); if (orgLoginSettings?.allowDomainDiscovery) { @@ -216,7 +211,7 @@ export default async function Page(props: { }); } - const newUser = await addHuman({ host, request: userData }); + const newUser = await addHuman({ serviceUrl, request: userData }); if (newUser) { return ( diff --git a/apps/login/src/app/(login)/idp/page.tsx b/apps/login/src/app/(login)/idp/page.tsx index 56897c28f34..da545f7a6e0 100644 --- a/apps/login/src/app/(login)/idp/page.tsx +++ b/apps/login/src/app/(login)/idp/page.tsx @@ -16,21 +16,16 @@ export default async function Page(props: { const organization = searchParams?.organization; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const identityProviders = await getActiveIdentityProviders({ - host, + serviceUrl, orgId: organization, }).then((resp) => { return resp.identityProviders; }); - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); return ( diff --git a/apps/login/src/app/(login)/invite/page.tsx b/apps/login/src/app/(login)/invite/page.tsx index 9b92be53817..4378a3e2d68 100644 --- a/apps/login/src/app/(login)/invite/page.tsx +++ b/apps/login/src/app/(login)/invite/page.tsx @@ -21,15 +21,10 @@ export default async function Page(props: { let { firstname, lastname, email, organization } = searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); if (!organization) { - const org = await getDefaultOrg({ host }); + const org = await getDefaultOrg({ serviceUrl }); if (!org) { throw new Error("No default organization found"); } @@ -37,14 +32,14 @@ export default async function Page(props: { organization = org.id; } - const loginSettings = await getLoginSettings({ host, organization }); + const loginSettings = await getLoginSettings({ serviceUrl, organization }); const passwordComplexitySettings = await getPasswordComplexitySettings({ - host, + serviceUrl, organization, }); - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); return ( diff --git a/apps/login/src/app/(login)/invite/success/page.tsx b/apps/login/src/app/(login)/invite/success/page.tsx index 5ae7300ea08..5ef81fc3cb8 100644 --- a/apps/login/src/app/(login)/invite/success/page.tsx +++ b/apps/login/src/app/(login)/invite/success/page.tsx @@ -19,15 +19,10 @@ export default async function Page(props: { let { userId, organization } = searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); if (!organization) { - const org = await getDefaultOrg({ host }); + const org = await getDefaultOrg({ serviceUrl }); if (!org) { throw new Error("No default organization found"); } @@ -35,12 +30,12 @@ export default async function Page(props: { organization = org.id; } - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); let user: User | undefined; let human: HumanUser | undefined; if (userId) { - const userResponse = await getUserByID({ host, userId }); + const userResponse = await getUserByID({ serviceUrl, userId }); if (userResponse) { user = userResponse.user; if (user?.type.case === "human") { diff --git a/apps/login/src/app/(login)/loginname/page.tsx b/apps/login/src/app/(login)/loginname/page.tsx index 21881d22531..b1c2d89570b 100644 --- a/apps/login/src/app/(login)/loginname/page.tsx +++ b/apps/login/src/app/(login)/loginname/page.tsx @@ -26,37 +26,35 @@ export default async function Page(props: { const submit: boolean = searchParams?.submit === "true"; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); let defaultOrganization; if (!organization) { - const org: Organization | null = await getDefaultOrg({ host }); + const org: Organization | null = await getDefaultOrg({ serviceUrl }); if (org) { defaultOrganization = org.id; } } const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: organization ?? defaultOrganization, }); - const contextLoginSettings = await getLoginSettings({ host, organization }); + const contextLoginSettings = await getLoginSettings({ + serviceUrl, + organization, + }); const identityProviders = await getActiveIdentityProviders({ - host, + serviceUrl, orgId: organization ?? defaultOrganization, }).then((resp) => { return resp.identityProviders; }); const branding = await getBrandingSettings({ - host, + serviceUrl, organization: organization ?? defaultOrganization, }); diff --git a/apps/login/src/app/(login)/mfa/page.tsx b/apps/login/src/app/(login)/mfa/page.tsx index f2df51847d4..53a5324682c 100644 --- a/apps/login/src/app/(login)/mfa/page.tsx +++ b/apps/login/src/app/(login)/mfa/page.tsx @@ -25,24 +25,19 @@ export default async function Page(props: { const { loginName, authRequestId, organization, sessionId } = searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const sessionFactors = sessionId - ? await loadSessionById(host, sessionId, organization) - : await loadSessionByLoginname(host, loginName, organization); + ? await loadSessionById(serviceUrl, sessionId, organization) + : await loadSessionByLoginname(serviceUrl, loginName, organization); async function loadSessionByLoginname( - host: string, + serviceUrl: string, loginName?: string, organization?: string, ) { return loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization, @@ -50,7 +45,7 @@ export default async function Page(props: { }).then((session) => { if (session && session.factors?.user?.id) { return listAuthenticationMethodTypes({ - host, + serviceUrl, userId: session.factors.user.id, }).then((methods) => { return { @@ -69,13 +64,13 @@ export default async function Page(props: { ) { const recent = await getSessionCookieById({ sessionId, organization }); return getSession({ - host, + serviceUrl, sessionId: recent.id, sessionToken: recent.token, }).then((response) => { if (response?.session && response.session.factors?.user?.id) { return listAuthenticationMethodTypes({ - host, + serviceUrl, userId: response.session.factors.user.id, }).then((methods) => { return { @@ -87,7 +82,7 @@ export default async function Page(props: { }); } - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); return ( diff --git a/apps/login/src/app/(login)/mfa/set/page.tsx b/apps/login/src/app/(login)/mfa/set/page.tsx index 4adfa87fdb8..e757f4139d1 100644 --- a/apps/login/src/app/(login)/mfa/set/page.tsx +++ b/apps/login/src/app/(login)/mfa/set/page.tsx @@ -52,16 +52,11 @@ export default async function Page(props: { } = searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const sessionWithData = sessionId - ? await loadSessionById(host, sessionId, organization) - : await loadSessionByLoginname(host, loginName, organization); + ? await loadSessionById(serviceUrl, sessionId, organization) + : await loadSessionByLoginname(serviceUrl, loginName, organization); async function getAuthMethodsAndUser(host: string, session?: Session) { const userId = session?.factors?.user?.id; @@ -70,20 +65,24 @@ export default async function Page(props: { throw Error("Could not get user id from session"); } - return listAuthenticationMethodTypes({ host, userId }).then((methods) => { - return getUserByID({ host, userId }).then((user) => { - const humanUser = - user.user?.type.case === "human" ? user.user?.type.value : undefined; + return listAuthenticationMethodTypes({ serviceUrl, userId }).then( + (methods) => { + return getUserByID({ serviceUrl, userId }).then((user) => { + const humanUser = + user.user?.type.case === "human" + ? user.user?.type.value + : undefined; - return { - factors: session?.factors, - authMethods: methods.authMethodTypes ?? [], - phoneVerified: humanUser?.phone?.isVerified ?? false, - emailVerified: humanUser?.email?.isVerified ?? false, - expirationDate: session?.expirationDate, - }; - }); - }); + return { + factors: session?.factors, + authMethods: methods.authMethodTypes ?? [], + phoneVerified: humanUser?.phone?.isVerified ?? false, + emailVerified: humanUser?.email?.isVerified ?? false, + expirationDate: session?.expirationDate, + }; + }); + }, + ); } async function loadSessionByLoginname( @@ -92,13 +91,13 @@ export default async function Page(props: { organization?: string, ) { return loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization, }, }).then((session) => { - return getAuthMethodsAndUser(host, session); + return getAuthMethodsAndUser(serviceUrl, session); }); } @@ -109,17 +108,17 @@ export default async function Page(props: { ) { const recent = await getSessionCookieById({ sessionId, organization }); return getSession({ - host, + serviceUrl, sessionId: recent.id, sessionToken: recent.token, }).then((sessionResponse) => { - return getAuthMethodsAndUser(host, sessionResponse.session); + return getAuthMethodsAndUser(serviceUrl, sessionResponse.session); }); } - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: sessionWithData.factors?.user?.organizationId, }); diff --git a/apps/login/src/app/(login)/otp/[method]/page.tsx b/apps/login/src/app/(login)/otp/[method]/page.tsx index 2fc21d764bf..f66d0ed8b01 100644 --- a/apps/login/src/app/(login)/otp/[method]/page.tsx +++ b/apps/login/src/app/(login)/otp/[method]/page.tsx @@ -24,17 +24,13 @@ export default async function Page(props: { const tError = await getTranslations({ locale, namespace: "error" }); const _headers = await headers(); + const serviceUrl = getApiUrlOfHeaders(_headers); const host = _headers.get("host"); - const instanceUrl = getApiUrlOfHeaders(_headers); if (!host || typeof host !== "string") { throw new Error("No host found"); } - if (!instanceUrl) { - throw new Error("No instanceUrl found"); - } - const { loginName, // send from password page userId, // send from email link @@ -48,9 +44,9 @@ export default async function Page(props: { const { method } = params; const session = sessionId - ? await loadSessionById(instanceUrl, sessionId, organization) + ? await loadSessionById(serviceUrl, sessionId, organization) : await loadMostRecentSession({ - host: instanceUrl, + serviceUrl, sessionParams: { loginName, organization }, }); @@ -61,7 +57,7 @@ export default async function Page(props: { ) { const recent = await getSessionCookieById({ sessionId, organization }); return getSession({ - host, + serviceUrl, sessionId: recent.id, sessionToken: recent.token, }).then((response) => { @@ -73,12 +69,12 @@ export default async function Page(props: { // email links do not come with organization, thus we need to use the session's organization const branding = await getBrandingSettings({ - host: instanceUrl, + serviceUrl, organization: organization ?? session?.factors?.user?.organizationId, }); const loginSettings = await getLoginSettings({ - host: instanceUrl, + serviceUrl, organization: organization ?? session?.factors?.user?.organizationId, }); diff --git a/apps/login/src/app/(login)/otp/[method]/set/page.tsx b/apps/login/src/app/(login)/otp/[method]/set/page.tsx index e9c3da88480..dc10eee4367 100644 --- a/apps/login/src/app/(login)/otp/[method]/set/page.tsx +++ b/apps/login/src/app/(login)/otp/[method]/set/page.tsx @@ -34,18 +34,13 @@ export default async function Page(props: { const { method } = params; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } - - const branding = await getBrandingSettings({ host, organization }); - const loginSettings = await getLoginSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); + const loginSettings = await getLoginSettings({ serviceUrl, organization }); const session = await loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization, @@ -55,7 +50,7 @@ export default async function Page(props: { let totpResponse: RegisterTOTPResponse | undefined, error: Error | undefined; if (session && session.factors?.user?.id) { if (method === "time-based") { - await registerTOTP({ host, userId: session.factors.user.id }) + await registerTOTP({ serviceUrl, userId: session.factors.user.id }) .then((resp) => { if (resp) { totpResponse = resp; @@ -66,14 +61,14 @@ export default async function Page(props: { }); } else if (method === "sms") { // does not work - await addOTPSMS({ host, userId: session.factors.user.id }).catch( + await addOTPSMS({ serviceUrl, userId: session.factors.user.id }).catch( (error) => { error = new Error("Could not add OTP via SMS"); }, ); } else if (method === "email") { // works - await addOTPEmail({ host, userId: session.factors.user.id }).catch( + await addOTPEmail({ serviceUrl, userId: session.factors.user.id }).catch( (error) => { error = new Error("Could not add OTP via Email"); }, diff --git a/apps/login/src/app/(login)/passkey/page.tsx b/apps/login/src/app/(login)/passkey/page.tsx index fad64ebac0c..e28426e0a97 100644 --- a/apps/login/src/app/(login)/passkey/page.tsx +++ b/apps/login/src/app/(login)/passkey/page.tsx @@ -25,28 +25,23 @@ export default async function Page(props: { searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const sessionFactors = sessionId - ? await loadSessionById(host, sessionId, organization) + ? await loadSessionById(serviceUrl, sessionId, organization) : await loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization }, }); async function loadSessionById( - host: string, + serviceUrl: string, sessionId: string, organization?: string, ) { const recent = await getSessionCookieById({ sessionId, organization }); return getSession({ - host, + serviceUrl, sessionId: recent.id, sessionToken: recent.token, }).then((response) => { @@ -56,9 +51,9 @@ export default async function Page(props: { }); } - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); - const loginSettings = await getLoginSettings({ host, organization }); + const loginSettings = await getLoginSettings({ serviceUrl, organization }); return ( diff --git a/apps/login/src/app/(login)/passkey/set/page.tsx b/apps/login/src/app/(login)/passkey/set/page.tsx index 184eed82961..20b2038ced5 100644 --- a/apps/login/src/app/(login)/passkey/set/page.tsx +++ b/apps/login/src/app/(login)/passkey/set/page.tsx @@ -20,22 +20,17 @@ export default async function Page(props: { searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const session = await loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization, }, }); - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); return ( diff --git a/apps/login/src/app/(login)/password/change/page.tsx b/apps/login/src/app/(login)/password/change/page.tsx index 304ad4ec7b2..1da5945f676 100644 --- a/apps/login/src/app/(login)/password/change/page.tsx +++ b/apps/login/src/app/(login)/password/change/page.tsx @@ -16,12 +16,7 @@ export default async function Page(props: { searchParams: Promise>; }) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const searchParams = await props.searchParams; const locale = getLocale(); @@ -32,22 +27,22 @@ export default async function Page(props: { // also allow no session to be found (ignoreUnkownUsername) const sessionFactors = await loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization, }, }); - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); const passwordComplexity = await getPasswordComplexitySettings({ - host, + serviceUrl, organization: sessionFactors?.factors?.user?.organizationId, }); const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: sessionFactors?.factors?.user?.organizationId, }); diff --git a/apps/login/src/app/(login)/password/page.tsx b/apps/login/src/app/(login)/password/page.tsx index 6adfb827546..b2075ba4631 100644 --- a/apps/login/src/app/(login)/password/page.tsx +++ b/apps/login/src/app/(login)/password/page.tsx @@ -25,16 +25,11 @@ export default async function Page(props: { let { loginName, organization, authRequestId, alt } = searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); let defaultOrganization; if (!organization) { - const org: Organization | null = await getDefaultOrg({ host }); + const org: Organization | null = await getDefaultOrg({ serviceUrl }); if (org) { defaultOrganization = org.id; @@ -45,7 +40,7 @@ export default async function Page(props: { let sessionFactors; try { sessionFactors = await loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization, @@ -57,11 +52,11 @@ export default async function Page(props: { } const branding = await getBrandingSettings({ - host, + serviceUrl, organization: organization ?? defaultOrganization, }); const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: organization ?? defaultOrganization, }); diff --git a/apps/login/src/app/(login)/password/set/page.tsx b/apps/login/src/app/(login)/password/set/page.tsx index f951e40a6a9..a289798c5da 100644 --- a/apps/login/src/app/(login)/password/set/page.tsx +++ b/apps/login/src/app/(login)/password/set/page.tsx @@ -27,18 +27,13 @@ export default async function Page(props: { searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); // also allow no session to be found (ignoreUnkownUsername) let session: Session | undefined; if (loginName) { session = await loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization, @@ -46,19 +41,19 @@ export default async function Page(props: { }); } - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); const passwordComplexity = await getPasswordComplexitySettings({ - host, + serviceUrl, organization: session?.factors?.user?.organizationId, }); - const loginSettings = await getLoginSettings({ host, organization }); + const loginSettings = await getLoginSettings({ serviceUrl, organization }); let user: User | undefined; let displayName: string | undefined; if (userId) { - const userResponse = await getUserByID({ host, userId }); + const userResponse = await getUserByID({ serviceUrl, userId }); user = userResponse.user; if (user?.type.case === "human") { diff --git a/apps/login/src/app/(login)/register/page.tsx b/apps/login/src/app/(login)/register/page.tsx index 0a1deb6a239..e1bf29bff47 100644 --- a/apps/login/src/app/(login)/register/page.tsx +++ b/apps/login/src/app/(login)/register/page.tsx @@ -23,29 +23,24 @@ export default async function Page(props: { searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); if (!organization) { - const org: Organization | null = await getDefaultOrg({ host }); + const org: Organization | null = await getDefaultOrg({ serviceUrl }); if (org) { organization = org.id; } } - const legal = await getLegalAndSupportSettings({ host, organization }); + const legal = await getLegalAndSupportSettings({ serviceUrl, organization }); const passwordComplexitySettings = await getPasswordComplexitySettings({ - host, + serviceUrl, organization, }); - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); - const loginSettings = await getLoginSettings({ host, organization }); + const loginSettings = await getLoginSettings({ serviceUrl, organization }); if (!loginSettings?.allowRegister) { return ( diff --git a/apps/login/src/app/(login)/register/password/page.tsx b/apps/login/src/app/(login)/register/password/page.tsx index 549b7a90ef5..b5d6fd36fc5 100644 --- a/apps/login/src/app/(login)/register/password/page.tsx +++ b/apps/login/src/app/(login)/register/password/page.tsx @@ -23,15 +23,10 @@ export default async function Page(props: { searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); if (!organization) { - const org: Organization | null = await getDefaultOrg({ host }); + const org: Organization | null = await getDefaultOrg({ serviceUrl }); if (org) { organization = org.id; } @@ -39,15 +34,15 @@ export default async function Page(props: { const missingData = !firstname || !lastname || !email; - const legal = await getLegalAndSupportSettings({ host, organization }); + const legal = await getLegalAndSupportSettings({ serviceUrl, organization }); const passwordComplexitySettings = await getPasswordComplexitySettings({ - host, + serviceUrl, organization, }); - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); - const loginSettings = await getLoginSettings({ host, organization }); + const loginSettings = await getLoginSettings({ serviceUrl, organization }); return missingData ? ( diff --git a/apps/login/src/app/(login)/signedin/page.tsx b/apps/login/src/app/(login)/signedin/page.tsx index 2658a915ca0..a7e8da1254b 100644 --- a/apps/login/src/app/(login)/signedin/page.tsx +++ b/apps/login/src/app/(login)/signedin/page.tsx @@ -21,7 +21,7 @@ import Link from "next/link"; import { redirect } from "next/navigation"; async function loadSession( - host: string, + serviceUrl: string, loginName: string, authRequestId?: string, ) { @@ -29,7 +29,7 @@ async function loadSession( if (authRequestId) { return createCallback({ - host, + serviceUrl, req: create(CreateCallbackRequestSchema, { authRequestId, callbackKind: { @@ -45,7 +45,7 @@ async function loadSession( }); } return getSession({ - host, + serviceUrl, sessionId: recent.id, sessionToken: recent.token, }).then((response) => { @@ -61,21 +61,20 @@ export default async function Page(props: { searchParams: Promise }) { const t = await getTranslations({ locale, namespace: "signedin" }); const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const { loginName, authRequestId, organization } = searchParams; - const sessionFactors = await loadSession(host, loginName, authRequestId); + const sessionFactors = await loadSession( + serviceUrl, + loginName, + authRequestId, + ); - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); let loginSettings; if (!authRequestId) { - loginSettings = await getLoginSettings({ host, organization }); + loginSettings = await getLoginSettings({ serviceUrl, organization }); } return ( diff --git a/apps/login/src/app/(login)/u2f/page.tsx b/apps/login/src/app/(login)/u2f/page.tsx index f588c7d62cd..4961773ab97 100644 --- a/apps/login/src/app/(login)/u2f/page.tsx +++ b/apps/login/src/app/(login)/u2f/page.tsx @@ -20,19 +20,19 @@ export default async function Page(props: { const { loginName, authRequestId, sessionId, organization } = searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host || typeof host !== "string") { throw new Error("No host found"); } - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); const sessionFactors = sessionId - ? await loadSessionById(host, sessionId, organization) + ? await loadSessionById(serviceUrl, sessionId, organization) : await loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization }, }); @@ -43,7 +43,7 @@ export default async function Page(props: { ) { const recent = await getSessionCookieById({ sessionId, organization }); return getSession({ - host, + serviceUrl, sessionId: recent.id, sessionToken: recent.token, }).then((response) => { diff --git a/apps/login/src/app/(login)/u2f/set/page.tsx b/apps/login/src/app/(login)/u2f/set/page.tsx index 1ebe4d5edd4..de4a0ff6d4a 100644 --- a/apps/login/src/app/(login)/u2f/set/page.tsx +++ b/apps/login/src/app/(login)/u2f/set/page.tsx @@ -19,22 +19,17 @@ export default async function Page(props: { const { loginName, organization, authRequestId, checkAfter } = searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const sessionFactors = await loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization, }, }); - const branding = await getBrandingSettings({ host, organization }); + const branding = await getBrandingSettings({ serviceUrl, organization }); return ( diff --git a/apps/login/src/app/(login)/verify/page.tsx b/apps/login/src/app/(login)/verify/page.tsx index 1dde49aabdc..21354cfe4f4 100644 --- a/apps/login/src/app/(login)/verify/page.tsx +++ b/apps/login/src/app/(login)/verify/page.tsx @@ -26,7 +26,7 @@ export default async function Page(props: { searchParams: Promise }) { searchParams; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); + const serviceUrl = getApiUrlOfHeaders(_headers); const host = _headers.get("host"); if (!host || typeof host !== "string") { @@ -34,7 +34,7 @@ export default async function Page(props: { searchParams: Promise }) { } const branding = await getBrandingSettings({ - host: instanceUrl, + serviceUrl, organization, }); @@ -47,7 +47,7 @@ export default async function Page(props: { searchParams: Promise }) { if ("loginName" in searchParams) { sessionFactors = await loadMostRecentSession({ - host: instanceUrl, + serviceUrl, sessionParams: { loginName, organization, @@ -56,12 +56,11 @@ export default async function Page(props: { searchParams: Promise }) { if (doSend && sessionFactors?.factors?.user?.id) { await sendEmailCode({ - host, + serviceUrl, + userId: sessionFactors?.factors?.user?.id, urlTemplate: `${host.includes("localhost") ? "http://" : "https://"}${host}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true` + (authRequestId ? `&authRequestId=${authRequestId}` : ""), - userId: sessionFactors?.factors?.user?.id, - authRequestId, }).catch((error) => { console.error("Could not resend verification email", error); throw Error("Failed to send verification email"); @@ -70,9 +69,11 @@ export default async function Page(props: { searchParams: Promise }) { } else if ("userId" in searchParams && userId) { if (doSend) { await sendEmailCode({ - host, + serviceUrl, userId, - authRequestId, + urlTemplate: + `${host.includes("localhost") ? "http://" : "https://"}${host}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true` + + (authRequestId ? `&authRequestId=${authRequestId}` : ""), }).catch((error) => { console.error("Could not resend verification email", error); throw Error("Failed to send verification email"); diff --git a/apps/login/src/app/login/route.ts b/apps/login/src/app/login/route.ts index 42599f00b35..7c4d0bbd25d 100644 --- a/apps/login/src/app/login/route.ts +++ b/apps/login/src/app/login/route.ts @@ -30,9 +30,15 @@ export const dynamic = "force-dynamic"; export const revalidate = false; export const fetchCache = "default-no-store"; -async function loadSessions(host: string, ids: string[]): Promise { +async function loadSessions({ + serviceUrl, + ids, +}: { + serviceUrl: string; + ids: string[]; +}): Promise { const response = await listSessions({ - host, + serviceUrl, ids: ids.filter((id: string | undefined) => !!id), }); @@ -48,7 +54,7 @@ const IDP_SCOPE_REGEX = /urn:zitadel:iam:org:idp:id:(.+)/; * to check for mfa for automatically selected session -> const response = await listAuthenticationMethodTypes(userId); **/ async function isSessionValid( - host: string, + serviceUrl: string, session: Session, ): Promise { // session can't be checked without user @@ -60,7 +66,7 @@ async function isSessionValid( let mfaValid = true; const authMethodTypes = await listAuthenticationMethodTypes({ - host, + serviceUrl, userId: session.factors.user.id, }); @@ -109,7 +115,7 @@ async function isSessionValid( } else { // only check settings if no auth methods are available, as this would require a setup const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: session.factors?.user?.organizationId, }); if (loginSettings?.forceMfa || loginSettings?.forceMfaLocalOnly) { @@ -152,7 +158,7 @@ async function isSessionValid( } async function findValidSession( - host: string, + serviceUrl: string, sessions: Session[], authRequest: AuthRequest, ): Promise { @@ -179,7 +185,7 @@ async function findValidSession( // return the first valid session according to settings for (const session of sessionsWithHint) { - if (await isSessionValid(host, session)) { + if (await isSessionValid(serviceUrl, session)) { return session; } } @@ -193,12 +199,7 @@ export async function GET(request: NextRequest) { const sessionId = searchParams.get("sessionId"); const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); // TODO: find a better way to handle _rsc (react server components) requests and block them to avoid conflicts when creating oidc callback const _rsc = searchParams.get("_rsc"); @@ -210,7 +211,7 @@ export async function GET(request: NextRequest) { const ids = sessionCookies.map((s) => s.id); let sessions: Session[] = []; if (ids && ids.length) { - sessions = await loadSessions(host, ids); + sessions = await loadSessions({ serviceUrl, ids }); } if (authRequestId && sessionId) { @@ -223,7 +224,7 @@ export async function GET(request: NextRequest) { if (selectedSession && selectedSession.id) { console.log(`Found session ${selectedSession.id}`); - const isValid = await isSessionValid(host, selectedSession); + const isValid = await isSessionValid(serviceUrl, selectedSession); console.log("Session is valid:", isValid); @@ -257,7 +258,7 @@ export async function GET(request: NextRequest) { // works not with _rsc request try { const { callbackUrl } = await createCallback({ - host, + serviceUrl, req: create(CreateCallbackRequestSchema, { authRequestId, callbackKind: { @@ -284,7 +285,7 @@ export async function GET(request: NextRequest) { error?.code === 9 ) { const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: selectedSession.factors?.user?.organizationId, }); @@ -316,7 +317,7 @@ export async function GET(request: NextRequest) { } if (authRequestId) { - const { authRequest } = await getAuthRequest({ host, authRequestId }); + const { authRequest } = await getAuthRequest({ serviceUrl, authRequestId }); let organization = ""; let suffix = ""; @@ -343,7 +344,10 @@ export async function GET(request: NextRequest) { const matched = ORG_DOMAIN_SCOPE_REGEX.exec(orgDomainScope); const orgDomain = matched?.[1] ?? ""; if (orgDomain) { - const orgs = await getOrgsByDomain({ host, domain: orgDomain }); + const orgs = await getOrgsByDomain({ + serviceUrl, + domain: orgDomain, + }); if (orgs.result && orgs.result.length === 1) { organization = orgs.result[0].id ?? ""; suffix = orgDomain; @@ -357,7 +361,7 @@ export async function GET(request: NextRequest) { idpId = matched?.[1] ?? ""; const identityProviders = await getActiveIdentityProviders({ - host, + serviceUrl, orgId: organization ? organization : undefined, }).then((resp) => { return resp.identityProviders; @@ -382,7 +386,7 @@ export async function GET(request: NextRequest) { } return startIdentityProviderFlow({ - host, + serviceUrl, idpId, urls: { successUrl: @@ -482,7 +486,7 @@ export async function GET(request: NextRequest) { * Instead, the server attempts to silently authenticate the user using an existing session or other authentication mechanisms that do not require user interaction **/ const selectedSession = await findValidSession( - host, + serviceUrl, sessions, authRequest, ); @@ -511,7 +515,7 @@ export async function GET(request: NextRequest) { }; const { callbackUrl } = await createCallback({ - host, + serviceUrl, req: create(CreateCallbackRequestSchema, { authRequestId, callbackKind: { @@ -524,7 +528,7 @@ export async function GET(request: NextRequest) { } else { // check for loginHint, userId hint and valid sessions let selectedSession = await findValidSession( - host, + serviceUrl, sessions, authRequest, ); @@ -548,7 +552,7 @@ export async function GET(request: NextRequest) { try { const { callbackUrl } = await createCallback({ - host, + serviceUrl, req: create(CreateCallbackRequestSchema, { authRequestId, callbackKind: { diff --git a/apps/login/src/components/login-otp.tsx b/apps/login/src/components/login-otp.tsx index 21d895e3708..c5be74d2524 100644 --- a/apps/login/src/components/login-otp.tsx +++ b/apps/login/src/components/login-otp.tsx @@ -18,6 +18,7 @@ import { Spinner } from "./spinner"; // either loginName or sessionId must be provided type Props = { + host: string | null; loginName?: string; sessionId?: string; authRequestId?: string; @@ -25,7 +26,6 @@ type Props = { method: string; code?: string; loginSettings?: LoginSettings; - host: string | null; }; type Inputs = { @@ -33,6 +33,7 @@ type Inputs = { }; export function LoginOTP({ + host, loginName, sessionId, authRequestId, @@ -40,7 +41,6 @@ export function LoginOTP({ method, code, loginSettings, - host, }: Props) { const t = useTranslations("otp"); diff --git a/apps/login/src/lib/self.ts b/apps/login/src/lib/self.ts index b7aaea3ea5e..e04caa6e38d 100644 --- a/apps/login/src/lib/self.ts +++ b/apps/login/src/lib/self.ts @@ -7,14 +7,14 @@ import { getSessionCookieById } from "./cookies"; import { getApiUrlOfHeaders } from "./service"; import { getSession } from "./zitadel"; -const transport = async (host: string, token: string) => { +const transport = async (serviceUrl: string, token: string) => { return createServerTransport(token, { - baseUrl: host, + baseUrl: serviceUrl, }); }; -const myUserService = async (host: string, sessionToken: string) => { - const transportPromise = await transport(host, sessionToken); +const myUserService = async (serviceUrl: string, sessionToken: string) => { + const transportPromise = await transport(serviceUrl, sessionToken); return createUserServiceClient(transportPromise); }; @@ -26,16 +26,12 @@ export async function setMyPassword({ password: string; }) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - - if (!instanceUrl) { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const sessionCookie = await getSessionCookieById({ sessionId }); const { session } = await getSession({ - host: instanceUrl, + serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, }); @@ -44,7 +40,7 @@ export async function setMyPassword({ return { error: "Could not load session" }; } - const service = await myUserService(instanceUrl, `${sessionCookie.token}`); + const service = await myUserService(serviceUrl, `${sessionCookie.token}`); if (!session?.factors?.user?.id) { return { error: "No user id found in session" }; diff --git a/apps/login/src/lib/server/cookie.ts b/apps/login/src/lib/server/cookie.ts index e9cdd80493a..8e9f83e7c0e 100644 --- a/apps/login/src/lib/server/cookie.ts +++ b/apps/login/src/lib/server/cookie.ts @@ -35,15 +35,10 @@ export async function createSessionAndUpdateCookie( lifetime?: Duration, ): Promise { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host) { - throw new Error("Could not get domain"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const createdSession = await createSessionFromChecks({ - host, + serviceUrl, checks, challenges, lifetime, @@ -51,7 +46,7 @@ export async function createSessionAndUpdateCookie( if (createdSession) { return getSession({ - host, + serviceUrl, sessionId: createdSession.sessionId, sessionToken: createdSession.sessionToken, }).then((response) => { @@ -102,15 +97,10 @@ export async function createSessionForIdpAndUpdateCookie( lifetime?: Duration, ): Promise { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host) { - throw new Error("Could not get domain"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const createdSession = await createSessionForUserIdAndIdpIntent({ - host, + serviceUrl, userId, idpIntent, lifetime, @@ -121,7 +111,7 @@ export async function createSessionForIdpAndUpdateCookie( } const { session } = await getSession({ - host, + serviceUrl, sessionId: createdSession.sessionId, sessionToken: createdSession.sessionToken, }); @@ -169,15 +159,10 @@ export async function setSessionAndUpdateCookie( lifetime?: Duration, ) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host) { - throw new Error("Could not get domain"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); return setSession({ - host, + serviceUrl, sessionId: recentCookie.id, sessionToken: recentCookie.token, challenges, @@ -203,7 +188,7 @@ export async function setSessionAndUpdateCookie( } return getSession({ - host, + serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, }).then((response) => { diff --git a/apps/login/src/lib/server/idp.ts b/apps/login/src/lib/server/idp.ts index 4f0bd4d930d..396bebd6b27 100644 --- a/apps/login/src/lib/server/idp.ts +++ b/apps/login/src/lib/server/idp.ts @@ -19,6 +19,7 @@ export type StartIDPFlowCommand = { export async function startIDPFlow(command: StartIDPFlowCommand) { const _headers = await headers(); + const serviceUrl = getApiUrlOfHeaders(_headers); const host = _headers.get("host"); if (!host) { @@ -26,7 +27,7 @@ export async function startIDPFlow(command: StartIDPFlowCommand) { } return startIdentityProviderFlow({ - host, + serviceUrl, idpId: command.idpId, urls: { successUrl: `${host.includes("localhost") ? "http://" : "https://"}${host}${command.successUrl}`, @@ -59,8 +60,8 @@ export async function createNewSessionFromIdpIntent( command: CreateNewSessionCommand, ) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host) { return { error: "Could not get domain" }; @@ -70,14 +71,17 @@ export async function createNewSessionFromIdpIntent( throw new Error("No userId or loginName provided"); } - const userResponse = await getUserByID({ host, userId: command.userId }); + const userResponse = await getUserByID({ + serviceUrl, + userId: command.userId, + }); if (!userResponse || !userResponse.user) { return { error: "User not found in the system" }; } const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: userResponse.user.details?.resourceOwner, }); diff --git a/apps/login/src/lib/server/invite.ts b/apps/login/src/lib/server/invite.ts index 5393e9547d5..4249cd8d778 100644 --- a/apps/login/src/lib/server/invite.ts +++ b/apps/login/src/lib/server/invite.ts @@ -22,7 +22,7 @@ export type RegisterUserResponse = { export async function inviteUser(command: InviteUserCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); + const serviceUrl = getApiUrlOfHeaders(_headers); const host = _headers.get("host"); if (!host) { @@ -30,7 +30,7 @@ export async function inviteUser(command: InviteUserCommand) { } const human = await addHumanUser({ - host: instanceUrl, + serviceUrl, email: command.email, firstName: command.firstName, lastName: command.lastName, @@ -42,7 +42,11 @@ export async function inviteUser(command: InviteUserCommand) { return { error: "Could not create user" }; } - const codeResponse = await createInviteCode({ userId: human.userId, host }); + const codeResponse = await createInviteCode({ + serviceUrl, + urlTemplate: `${host.includes("localhost") ? "http://" : "https://"}${host}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true`, + userId: human.userId, + }); if (!codeResponse || !human) { return { error: "Could not create invite code" }; diff --git a/apps/login/src/lib/server/loginname.ts b/apps/login/src/lib/server/loginname.ts index bf9664c29db..1177d683e38 100644 --- a/apps/login/src/lib/server/loginname.ts +++ b/apps/login/src/lib/server/loginname.ts @@ -34,15 +34,15 @@ const ORG_SUFFIX_REGEX = /(?<=@)(.+)/; export async function sendLoginname(command: SendLoginnameCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host) { throw new Error("Could not get domain"); } const loginSettingsByContext = await getLoginSettings({ - host, + serviceUrl, organization: command.organization, }); @@ -51,7 +51,7 @@ export async function sendLoginname(command: SendLoginnameCommand) { } let searchUsersRequest: SearchUsersCommand = { - host, + serviceUrl, searchValue: command.loginName, organizationId: command.organization, loginSettings: loginSettingsByContext, @@ -72,7 +72,7 @@ export async function sendLoginname(command: SendLoginnameCommand) { const redirectUserToSingleIDPIfAvailable = async () => { const identityProviders = await getActiveIdentityProviders({ - host, + serviceUrl, orgId: command.organization, }).then((resp) => { return resp.identityProviders; @@ -80,8 +80,8 @@ export async function sendLoginname(command: SendLoginnameCommand) { if (identityProviders.length === 1) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host) { return { error: "Could not get host" }; @@ -102,7 +102,7 @@ export async function sendLoginname(command: SendLoginnameCommand) { } const resp = await startIdentityProviderFlow({ - host, + serviceUrl, idpId: identityProviders[0].id, urls: { successUrl: @@ -121,7 +121,7 @@ export async function sendLoginname(command: SendLoginnameCommand) { }; const redirectUserToIDP = async (userId: string) => { - const identityProviders = await listIDPLinks({ host, userId }).then( + const identityProviders = await listIDPLinks({ serviceUrl, userId }).then( (resp) => { return resp.result; }, @@ -129,8 +129,8 @@ export async function sendLoginname(command: SendLoginnameCommand) { if (identityProviders.length === 1) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host) { return { error: "Could not get host" }; @@ -138,7 +138,7 @@ export async function sendLoginname(command: SendLoginnameCommand) { const identityProviderId = identityProviders[0].idpId; - const idp = await getIDPByID({ host, id: identityProviderId }); + const idp = await getIDPByID({ serviceUrl, id: identityProviderId }); const idpType = idp?.type; @@ -160,7 +160,7 @@ export async function sendLoginname(command: SendLoginnameCommand) { } const resp = await startIdentityProviderFlow({ - host, + serviceUrl, idpId: idp.id, urls: { successUrl: @@ -185,7 +185,7 @@ export async function sendLoginname(command: SendLoginnameCommand) { const userId = potentialUsers[0].userId; const userLoginSettings = await getLoginSettings({ - host, + serviceUrl, organization: user.details?.resourceOwner, }); @@ -243,7 +243,7 @@ export async function sendLoginname(command: SendLoginnameCommand) { } const methods = await listAuthenticationMethodTypes({ - host, + serviceUrl, userId: session.factors?.user?.id, }); @@ -400,12 +400,12 @@ export async function sendLoginname(command: SendLoginnameCommand) { const suffix = matched?.[1] ?? ""; // this just returns orgs where the suffix is set as primary domain - const orgs = await getOrgsByDomain({ host, domain: suffix }); + const orgs = await getOrgsByDomain({ serviceUrl, domain: suffix }); const orgToCheckForDiscovery = orgs.result && orgs.result.length === 1 ? orgs.result[0].id : undefined; const orgLoginSettings = await getLoginSettings({ - host, + serviceUrl, organization: orgToCheckForDiscovery, }); if (orgLoginSettings?.allowDomainDiscovery) { diff --git a/apps/login/src/lib/server/otp.ts b/apps/login/src/lib/server/otp.ts index d70fffaa2cd..3618b6e8c0d 100644 --- a/apps/login/src/lib/server/otp.ts +++ b/apps/login/src/lib/server/otp.ts @@ -27,12 +27,7 @@ export type SetOTPCommand = { export async function setOTP(command: SetOTPCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host) { - throw new Error("Could not get domain"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const recentSession = command.sessionId ? await getSessionCookieById({ sessionId: command.sessionId }).catch( @@ -68,7 +63,7 @@ export async function setOTP(command: SetOTPCommand) { } const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: command.organization, }); diff --git a/apps/login/src/lib/server/passkeys.ts b/apps/login/src/lib/server/passkeys.ts index 2ffa594ff50..98d89ab01e7 100644 --- a/apps/login/src/lib/server/passkeys.ts +++ b/apps/login/src/lib/server/passkeys.ts @@ -43,8 +43,8 @@ export async function registerPasskeyLink( const { sessionId } = command; const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host) { throw new Error("Could not get domain"); @@ -52,7 +52,7 @@ export async function registerPasskeyLink( const sessionCookie = await getSessionCookieById({ sessionId }); const session = await getSession({ - host, + serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, }); @@ -72,7 +72,7 @@ export async function registerPasskeyLink( // use session token to add the passkey const registerLink = await createPasskeyRegistrationLink({ - host, + serviceUrl, userId, }); @@ -81,7 +81,7 @@ export async function registerPasskeyLink( } return registerPasskey({ - host, + serviceUrl, userId, code: registerLink.code, domain: hostname, @@ -90,12 +90,7 @@ export async function registerPasskeyLink( export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host) { - throw new Error("Could not get domain"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); // if no name is provided, try to generate one from the user agent let passkeyName = command.passkeyName; @@ -113,7 +108,7 @@ export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) { sessionId: command.sessionId, }); const session = await getSession({ - host, + serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, }); @@ -124,7 +119,7 @@ export async function verifyPasskeyRegistration(command: VerifyPasskeyCommand) { } return zitadelVerifyPasskeyRegistration({ - host, + serviceUrl, request: create(VerifyPasskeyRegistrationRequestSchema, { passkeyId: command.passkeyId, publicKeyCredential: command.publicKeyCredential, @@ -158,14 +153,9 @@ export async function sendPasskey(command: SendPasskeyCommand) { } const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); - if (!host) { - return { error: "Could not get host" }; - } - - const loginSettings = await getLoginSettings({ host, organization }); + const loginSettings = await getLoginSettings({ serviceUrl, organization }); const lifetime = checks?.webAuthN ? loginSettings?.multiFactorCheckLifetime // TODO different lifetime for webauthn u2f/passkey @@ -186,7 +176,7 @@ export async function sendPasskey(command: SendPasskeyCommand) { } const userResponse = await getUserByID({ - host, + serviceUrl, userId: session?.factors?.user?.id, }); diff --git a/apps/login/src/lib/server/password.ts b/apps/login/src/lib/server/password.ts index 4ec6be613d8..91af9e1cb43 100644 --- a/apps/login/src/lib/server/password.ts +++ b/apps/login/src/lib/server/password.ts @@ -45,15 +45,15 @@ type ResetPasswordCommand = { export async function resetPassword(command: ResetPasswordCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host || typeof host !== "string") { throw new Error("No host found"); } const users = await listUsers({ - serviceUrl: instanceUrl, + serviceUrl, loginName: command.loginName, organizationId: command.organization, }); @@ -68,7 +68,7 @@ export async function resetPassword(command: ResetPasswordCommand) { const userId = users.result[0].userId; return passwordReset({ - serviceUrl: instanceUrl, + serviceUrl, userId, urlTemplate: `${host.includes("localhost") ? "http://" : "https://"}${host}/password/set?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` + @@ -85,12 +85,7 @@ export type UpdateSessionCommand = { export async function sendPassword(command: UpdateSessionCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); let sessionCookie = await getSessionCookieByLoginName({ loginName: command.loginName, @@ -105,7 +100,7 @@ export async function sendPassword(command: UpdateSessionCommand) { if (!sessionCookie) { const users = await listUsers({ - host, + serviceUrl, loginName: command.loginName, organizationId: command.organization, }); @@ -119,7 +114,7 @@ export async function sendPassword(command: UpdateSessionCommand) { }); loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: command.organization, }); @@ -147,7 +142,7 @@ export async function sendPassword(command: UpdateSessionCommand) { } const userResponse = await getUserByID({ - host, + serviceUrl, userId: session?.factors?.user?.id, }); @@ -160,7 +155,7 @@ export async function sendPassword(command: UpdateSessionCommand) { if (!loginSettings) { loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: command.organization ?? session.factors?.user?.organizationId, }); @@ -205,7 +200,7 @@ export async function sendPassword(command: UpdateSessionCommand) { let authMethods; if (command.checks && command.checks.password && session.factors?.user?.id) { const response = await listAuthenticationMethodTypes({ - host, + serviceUrl, userId: session.factors.user.id, }); if (response.authMethodTypes && response.authMethodTypes.length) { @@ -260,15 +255,10 @@ export async function changePassword(command: { password: string; }) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); // check for init state - const { user } = await getUserByID({ host, userId: command.userId }); + const { user } = await getUserByID({ serviceUrl, userId: command.userId }); if (!user || user.userId !== command.userId) { return { error: "Could not send Password Reset Link" }; @@ -276,7 +266,7 @@ export async function changePassword(command: { const userId = user.userId; return setUserPassword({ - host, + serviceUrl, userId, password: command.password, user, @@ -294,17 +284,12 @@ export async function checkSessionAndSetPassword({ password, }: CheckSessionAndSetPasswordCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - - if (!instanceUrl) { - throw new Error("No host found"); - } - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); const sessionCookie = await getSessionCookieById({ sessionId }); const { session } = await getSession({ - host: instanceUrl, + serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, }); @@ -322,7 +307,7 @@ export async function checkSessionAndSetPassword({ // check if the user has no password set in order to set a password const authmethods = await listAuthenticationMethodTypes({ - host, + serviceUrl, userId: session.factors.user.id, }); @@ -342,7 +327,7 @@ export async function checkSessionAndSetPassword({ ); const loginSettings = await getLoginSettings({ - host: instanceUrl, + serviceUrl, organization: session.factors.user.organizationId, }); @@ -352,7 +337,7 @@ export async function checkSessionAndSetPassword({ // if the user has no MFA but MFA is enforced, we can set a password otherwise we use the token of the user if (forceMfa && hasNoMFAMethods) { - return setPassword({ host, payload }).catch((error) => { + return setPassword({ serviceUrl, payload }).catch((error) => { // throw error if failed precondition (ex. User is not yet initialized) if (error.code === 9 && error.message) { return { error: "Failed precondition" }; @@ -363,17 +348,17 @@ export async function checkSessionAndSetPassword({ } else { const transport = async (host: string, token: string) => { return createServerTransport(token, { - baseUrl: host, + baseUrl: serviceUrl, }); }; const myUserService = async (host: string, sessionToken: string) => { - const transportPromise = await transport(host, sessionToken); + const transportPromise = await transport(serviceUrl, sessionToken); return createUserServiceClient(transportPromise); }; const selfService = await myUserService( - instanceUrl, + serviceUrl, `${sessionCookie.token}`, ); diff --git a/apps/login/src/lib/server/register.ts b/apps/login/src/lib/server/register.ts index 1aa2f2efdd0..071b008a875 100644 --- a/apps/login/src/lib/server/register.ts +++ b/apps/login/src/lib/server/register.ts @@ -29,15 +29,15 @@ export type RegisterUserResponse = { }; export async function registerUser(command: RegisterUserCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host || typeof host !== "string") { throw new Error("No host found"); } const addResponse = await addHumanUser({ - host, + serviceUrl, email: command.email, firstName: command.firstName, lastName: command.lastName, @@ -50,7 +50,7 @@ export async function registerUser(command: RegisterUserCommand) { } const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: command.organization, }); @@ -91,7 +91,7 @@ export async function registerUser(command: RegisterUserCommand) { return { redirect: "/passkey/set?" + params }; } else { const userResponse = await getUserByID({ - host, + serviceUrl, userId: session?.factors?.user?.id, }); diff --git a/apps/login/src/lib/server/session.ts b/apps/login/src/lib/server/session.ts index 9b95e77a6ca..9387dfea707 100644 --- a/apps/login/src/lib/server/session.ts +++ b/apps/login/src/lib/server/session.ts @@ -25,15 +25,10 @@ export async function continueWithSession({ ...session }: Session & { authRequestId?: string }) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: session.factors?.user?.organizationId, }); @@ -93,8 +88,8 @@ export async function updateSession(options: UpdateSessionCommand) { } const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host) { return { error: "Could not get host" }; @@ -111,7 +106,7 @@ export async function updateSession(options: UpdateSessionCommand) { challenges.webAuthN.domain = hostname; } - const loginSettings = await getLoginSettings({ host, organization }); + const loginSettings = await getLoginSettings({ serviceUrl, organization }); const lifetime = checks?.webAuthN ? loginSettings?.multiFactorCheckLifetime // TODO different lifetime for webauthn u2f/passkey @@ -135,7 +130,7 @@ export async function updateSession(options: UpdateSessionCommand) { let authMethods; if (checks && checks.password && session.factors?.user?.id) { const response = await listAuthenticationMethodTypes({ - host, + serviceUrl, userId: session.factors.user.id, }); if (response.authMethodTypes && response.authMethodTypes.length) { @@ -157,19 +152,14 @@ type ClearSessionOptions = { export async function clearSession(options: ClearSessionOptions) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const { sessionId } = options; const session = await getSessionCookieById({ sessionId }); const deletedSession = await deleteSession({ - host, + serviceUrl, sessionId: session.id, sessionToken: session.token, }); @@ -185,17 +175,12 @@ type CleanupSessionCommand = { export async function cleanupSession({ sessionId }: CleanupSessionCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const sessionCookie = await getSessionCookieById({ sessionId }); const deleteResponse = await deleteSession({ - host, + serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, }); diff --git a/apps/login/src/lib/server/u2f.ts b/apps/login/src/lib/server/u2f.ts index b544b8d75ba..c621eaab0d4 100644 --- a/apps/login/src/lib/server/u2f.ts +++ b/apps/login/src/lib/server/u2f.ts @@ -21,8 +21,8 @@ type VerifyU2FCommand = { export async function addU2F(command: RegisterU2FCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host || typeof host !== "string") { throw new Error("No host found"); @@ -37,7 +37,7 @@ export async function addU2F(command: RegisterU2FCommand) { } const session = await getSession({ - host, + serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, }); @@ -54,13 +54,13 @@ export async function addU2F(command: RegisterU2FCommand) { return { error: "Could not get session" }; } - return registerU2F({ host, userId, domain: hostname }); + return registerU2F({ serviceUrl, userId, domain: hostname }); } export async function verifyU2F(command: VerifyU2FCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; + const serviceUrl = getApiUrlOfHeaders(_headers); + const host = _headers.get("host"); if (!host || typeof host !== "string") { throw new Error("No host found"); @@ -81,7 +81,7 @@ export async function verifyU2F(command: VerifyU2FCommand) { }); const session = await getSession({ - host, + serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, }); @@ -99,5 +99,5 @@ export async function verifyU2F(command: VerifyU2FCommand) { userId, }); - return verifyU2FRegistration({ host, request }); + return verifyU2FRegistration({ serviceUrl, request }); } diff --git a/apps/login/src/lib/server/verify.ts b/apps/login/src/lib/server/verify.ts index c1407fcd961..4ce3031eeee 100644 --- a/apps/login/src/lib/server/verify.ts +++ b/apps/login/src/lib/server/verify.ts @@ -30,15 +30,10 @@ export async function verifyTOTP( organization?: string, ) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); return loadMostRecentSession({ - host, + serviceUrl, sessionParams: { loginName, organization, @@ -46,7 +41,7 @@ export async function verifyTOTP( }).then((session) => { if (session?.factors?.user?.id) { return verifyTOTPRegistration({ - host, + serviceUrl, code, userId: session.factors.user.id, }); @@ -67,23 +62,18 @@ type VerifyUserByEmailCommand = { export async function sendVerification(command: VerifyUserByEmailCommand) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); const verifyResponse = command.isInvite ? await verifyInviteCode({ - host, + serviceUrl, userId: command.userId, verificationCode: command.code, }).catch(() => { return { error: "Could not verify invite" }; }) : await verifyEmail({ - host, + serviceUrl, userId: command.userId, verificationCode: command.code, }).catch(() => { @@ -114,7 +104,7 @@ export async function sendVerification(command: VerifyUserByEmailCommand) { } session = await getSession({ - host, + serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, }).then((response) => { @@ -128,7 +118,7 @@ export async function sendVerification(command: VerifyUserByEmailCommand) { } const userResponse = await getUserByID({ - host, + serviceUrl, userId: session?.factors?.user?.id, }); @@ -138,7 +128,10 @@ export async function sendVerification(command: VerifyUserByEmailCommand) { user = userResponse.user; } else { - const userResponse = await getUserByID({ host, userId: command.userId }); + const userResponse = await getUserByID({ + serviceUrl, + userId: command.userId, + }); if (!userResponse || !userResponse.user) { return { error: "Could not load user" }; @@ -175,12 +168,12 @@ export async function sendVerification(command: VerifyUserByEmailCommand) { } const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: user.details?.resourceOwner, }); const authMethodResponse = await listAuthenticationMethodTypes({ - host, + serviceUrl, userId: user.userId, }); @@ -252,41 +245,36 @@ type resendVerifyEmailCommand = { export async function resendVerification(command: resendVerifyEmailCommand) { const _headers = await headers(); const serviceUrl = getApiUrlOfHeaders(_headers); - const host = _headers.get("host"); - if (!serviceUrl) { - return { error: "No host found" }; - } - if (!host) { return { error: "No host found" }; } return command.isInvite - ? resendInviteCode({ serviceUrl, host, userId: command.userId }) + ? resendInviteCode({ serviceUrl, userId: command.userId }) : resendEmailCode({ userId: command.userId, serviceUrl, - host, - authRequestId: command.authRequestId, + urlTemplate: + `${host.includes("localhost") ? "http://" : "https://"}${host}/password/set?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` + + (command.authRequestId + ? `&authRequestId=${command.authRequestId}` + : ""), }); } type sendEmailCommand = { serviceUrl: string; userId: string; - authRequestId?: string; + urlTemplate: string; }; export async function sendEmailCode(command: sendEmailCommand) { return zitadelSendEmailCode({ userId: command.userId, serviceUrl: command.serviceUrl, - authRequestId: command.authRequestId, - urlTemplate: - `${host.includes("localhost") ? "http://" : "https://"}${host}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true` + - (authRequestId ? `&authRequestId=${authRequestId}` : ""), + urlTemplate: command.urlTemplate, }); } @@ -302,12 +290,7 @@ export async function sendVerificationRedirectWithoutCheck( command: SendVerificationRedirectWithoutCheckCommand, ) { const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); - const host = instanceUrl; - - if (!host || typeof host !== "string") { - throw new Error("No host found"); - } + const serviceUrl = getApiUrlOfHeaders(_headers); if (!("loginName" in command || "userId" in command)) { return { error: "No userId, nor loginname provided" }; @@ -329,7 +312,7 @@ export async function sendVerificationRedirectWithoutCheck( } session = await getSession({ - host, + serviceUrl, sessionId: sessionCookie.id, sessionToken: sessionCookie.token, }).then((response) => { @@ -343,7 +326,7 @@ export async function sendVerificationRedirectWithoutCheck( } const userResponse = await getUserByID({ - host, + serviceUrl, userId: session?.factors?.user?.id, }); @@ -353,7 +336,10 @@ export async function sendVerificationRedirectWithoutCheck( user = userResponse.user; } else if ("userId" in command) { - const userResponse = await getUserByID({ host, userId: command.userId }); + const userResponse = await getUserByID({ + serviceUrl, + userId: command.userId, + }); if (!userResponse?.user) { return { error: "Could not load user" }; @@ -390,7 +376,7 @@ export async function sendVerificationRedirectWithoutCheck( } const authMethodResponse = await listAuthenticationMethodTypes({ - host, + serviceUrl, userId: user.userId, }); @@ -415,7 +401,7 @@ export async function sendVerificationRedirectWithoutCheck( } const loginSettings = await getLoginSettings({ - host, + serviceUrl, organization: user.details?.resourceOwner, }); diff --git a/apps/login/src/lib/service.ts b/apps/login/src/lib/service.ts index 0be711debef..e3e6ca1b0ce 100644 --- a/apps/login/src/lib/service.ts +++ b/apps/login/src/lib/service.ts @@ -19,16 +19,16 @@ type ServiceClass = export async function createServiceForHost( service: T, - host: string, + serviceUrl: string, ) { const token = await systemAPIToken(); - if (!host || !token) { + if (!serviceUrl || !token) { throw new Error("No instance url or token found"); } const transport = createServerTransport(token, { - baseUrl: host, + baseUrl: serviceUrl, }); return createClientFor(service)(transport); diff --git a/apps/login/src/lib/session.ts b/apps/login/src/lib/session.ts index 3aee1532468..758594845c1 100644 --- a/apps/login/src/lib/session.ts +++ b/apps/login/src/lib/session.ts @@ -4,7 +4,7 @@ import { getMostRecentCookieWithLoginname } from "./cookies"; import { getSession } from "./zitadel"; type LoadMostRecentSessionParams = { - host: string; + serviceUrl: string; sessionParams: { loginName?: string; organization?: string; @@ -12,7 +12,7 @@ type LoadMostRecentSessionParams = { }; export async function loadMostRecentSession({ - host, + serviceUrl, sessionParams, }: LoadMostRecentSessionParams): Promise { const recent = await getMostRecentCookieWithLoginname({ @@ -21,7 +21,7 @@ export async function loadMostRecentSession({ }); return getSession({ - host, + serviceUrl, sessionId: recent.id, sessionToken: recent.token, }).then((resp: GetSessionResponse) => resp.session); diff --git a/apps/login/src/lib/zitadel.ts b/apps/login/src/lib/zitadel.ts index 352b844e57a..5a03e5ece07 100644 --- a/apps/login/src/lib/zitadel.ts +++ b/apps/login/src/lib/zitadel.ts @@ -1,6 +1,4 @@ import { Client, create, Duration } from "@zitadel/client"; -import { createServerTransport } from "@zitadel/client/node"; -import { createSystemServiceClient } from "@zitadel/client/v1"; import { makeReqCtx } from "@zitadel/client/v2"; import { IdentityProviderService } from "@zitadel/proto/zitadel/idp/v2/idp_service_pb"; import { TextQueryMethod } from "@zitadel/proto/zitadel/object/v2/object_pb"; @@ -44,7 +42,6 @@ import { VerifyU2FRegistrationRequest, } from "@zitadel/proto/zitadel/user/v2/user_service_pb"; import { unstable_cacheLife as cacheLife } from "next/cache"; -import { systemAPIToken } from "./api"; import { createServiceForHost } from "./service"; const useCache = process.env.DEBUG !== "true"; @@ -56,16 +53,6 @@ async function cacheWrapper(callback: Promise) { return callback; } -const systemService = async () => { - const systemToken = await systemAPIToken(); - - const transport = createServerTransport(systemToken, { - baseUrl: process.env.AUDIENCE, - }); - - return createSystemServiceClient(transport); -}; - export async function getBrandingSettings({ serviceUrl, organization, @@ -87,11 +74,11 @@ export async function getLoginSettings({ serviceUrl, organization, }: { - serviceurl: string; + serviceUrl: string; organization?: string; }) { const settingsService: Client = - await createServiceForHost(SettingsService, host); + await createServiceForHost(SettingsService, serviceUrl); const callback = settingsService .getLoginSettings({ ctx: makeReqCtx(organization) }, {}) @@ -104,7 +91,7 @@ export async function listIDPLinks({ serviceUrl, userId, }: { - serviceurl: string; + serviceUrl: string; userId: string; }) { const userService: Client = await createServiceForHost( @@ -119,7 +106,7 @@ export async function addOTPEmail({ serviceUrl, userId, }: { - serviceurl: string; + serviceUrl: string; userId: string; }) { const userService: Client = await createServiceForHost( @@ -134,7 +121,7 @@ export async function addOTPSMS({ serviceUrl, userId, }: { - serviceurl: string; + serviceUrl: string; userId: string; }) { const userService: Client = await createServiceForHost( @@ -149,7 +136,7 @@ export async function registerTOTP({ serviceUrl, userId, }: { - serviceurl: string; + serviceUrl: string; userId: string; }) { const userService: Client = await createServiceForHost( @@ -160,9 +147,13 @@ export async function registerTOTP({ return userService.registerTOTP({ userId }, {}); } -export async function getGeneralSettings({ host }: { serviceurl: string }) { +export async function getGeneralSettings({ + serviceUrl, +}: { + serviceUrl: string; +}) { const settingsService: Client = - await createServiceForHost(SettingsService, host); + await createServiceForHost(SettingsService, serviceUrl); const callback = settingsService .getGeneralSettings({}, {}) @@ -175,11 +166,11 @@ export async function getLegalAndSupportSettings({ serviceUrl, organization, }: { - serviceurl: string; + serviceUrl: string; organization?: string; }) { const settingsService: Client = - await createServiceForHost(SettingsService, host); + await createServiceForHost(SettingsService, serviceUrl); const callback = settingsService .getLegalAndSupportSettings({ ctx: makeReqCtx(organization) }, {}) @@ -192,11 +183,11 @@ export async function getPasswordComplexitySettings({ serviceUrl, organization, }: { - serviceurl: string; + serviceUrl: string; organization?: string; }) { const settingsService: Client = - await createServiceForHost(SettingsService, host); + await createServiceForHost(SettingsService, serviceUrl); const callback = settingsService .getPasswordComplexitySettings({ ctx: makeReqCtx(organization) }) @@ -211,13 +202,13 @@ export async function createSessionFromChecks({ challenges, lifetime, }: { - serviceurl: string; + serviceUrl: string; checks: Checks; challenges: RequestChallenges | undefined; lifetime?: Duration; }) { const sessionService: Client = - await createServiceForHost(SessionService, host); + await createServiceForHost(SessionService, serviceUrl); return sessionService.createSession({ checks, challenges, lifetime }, {}); } @@ -228,7 +219,7 @@ export async function createSessionForUserIdAndIdpIntent({ idpIntent, lifetime, }: { - serviceurl: string; + serviceUrl: string; userId: string; idpIntent: { idpIntentId?: string | undefined; @@ -237,7 +228,7 @@ export async function createSessionForUserIdAndIdpIntent({ lifetime?: Duration; }) { const sessionService: Client = - await createServiceForHost(SessionService, host); + await createServiceForHost(SessionService, serviceUrl); return sessionService.createSession({ checks: { @@ -261,7 +252,7 @@ export async function setSession({ checks, lifetime, }: { - serviceurl: string; + serviceUrl: string; sessionId: string; sessionToken: string; challenges: RequestChallenges | undefined; @@ -269,7 +260,7 @@ export async function setSession({ lifetime?: Duration; }) { const sessionService: Client = - await createServiceForHost(SessionService, host); + await createServiceForHost(SessionService, serviceUrl); return sessionService.setSession( { @@ -289,12 +280,12 @@ export async function getSession({ sessionId, sessionToken, }: { - serviceurl: string; + serviceUrl: string; sessionId: string; sessionToken: string; }) { const sessionService: Client = - await createServiceForHost(SessionService, host); + await createServiceForHost(SessionService, serviceUrl); return sessionService.getSession({ sessionId, sessionToken }, {}); } @@ -304,12 +295,12 @@ export async function deleteSession({ sessionId, sessionToken, }: { - serviceurl: string; + serviceUrl: string; sessionId: string; sessionToken: string; }) { const sessionService: Client = - await createServiceForHost(SessionService, host); + await createServiceForHost(SessionService, serviceUrl); return sessionService.deleteSession({ sessionId, sessionToken }, {}); } @@ -321,7 +312,7 @@ type ListSessionsCommand = { export async function listSessions({ serviceUrl, ids }: ListSessionsCommand) { const sessionService: Client = - await createServiceForHost(SessionService, host); + await createServiceForHost(SessionService, serviceUrl); return sessionService.listSessions( { @@ -460,27 +451,24 @@ export async function resendInviteCode({ export async function sendEmailCode({ serviceUrl, - urlTemplate userId, - authRequestId, + urlTemplate, }: { serviceUrl: string; userId: string; - authRequestId?: string; + urlTemplate: string; }) { let medium = create(SendEmailCodeRequestSchema, { userId }); - if (host) { - medium = create(SendEmailCodeRequestSchema, { - ...medium, - verification: { - case: "sendCode", - value: create(SendEmailVerificationCodeSchema, { - urlTemplate, - }), - }, - }); - } + medium = create(SendEmailCodeRequestSchema, { + ...medium, + verification: { + case: "sendCode", + value: create(SendEmailVerificationCodeSchema, { + urlTemplate, + }), + }, + }); const userService: Client = await createServiceForHost( UserService, @@ -492,21 +480,21 @@ export async function sendEmailCode({ export async function createInviteCode({ serviceUrl, + urlTemplate, userId, }: { serviceUrl: string; + urlTemplate: string; userId: string; }) { let medium = create(SendInviteCodeSchema, { applicationName: "Typescript Login", }); - if (host) { - medium = { - ...medium, - urlTemplate: `${host.includes("localhost") ? "http://" : "https://"}${host}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true`, - }; - } + medium = { + ...medium, + urlTemplate, + }; const userService: Client = await createServiceForHost( UserService, @@ -802,7 +790,7 @@ export async function getDefaultOrg({ serviceUrl: string; }): Promise { const orgService: Client = - await createServiceForHost(OrganizationService, host); + await createServiceForHost(OrganizationService, serviceUrl); return orgService .listOrganizations( @@ -829,7 +817,7 @@ export async function getOrgsByDomain({ domain: string; }) { const orgService: Client = - await createServiceForHost(OrganizationService, host); + await createServiceForHost(OrganizationService, serviceUrl); return orgService.listOrganizations( { @@ -896,7 +884,7 @@ export async function getAuthRequest({ serviceUrl: string; authRequestId: string; }) { - const oidcService = await createServiceForHost(OIDCService, host); + const oidcService = await createServiceForHost(OIDCService, serviceUrl); return oidcService.getAuthRequest({ authRequestId, @@ -910,7 +898,7 @@ export async function createCallback({ serviceUrl: string; req: CreateCallbackRequest; }) { - const oidcService = await createServiceForHost(OIDCService, host); + const oidcService = await createServiceForHost(OIDCService, serviceUrl); return oidcService.createCallback(req); } @@ -940,28 +928,22 @@ export async function verifyEmail({ export async function resendEmailCode({ serviceUrl, - host, userId, - authRequestId, + urlTemplate, }: { serviceUrl: string; - host: string; userId: string; - authRequestId?: string; + urlTemplate: string; }) { let request: ResendEmailCodeRequest = create(ResendEmailCodeRequestSchema, { userId, }); - if (host) { - const medium = create(SendEmailVerificationCodeSchema, { - urlTemplate: - `${host.includes("localhost") ? "http://" : "https://"}${host}/password/set?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` + - (authRequestId ? `&authRequestId=${authRequestId}` : ""), - }); + const medium = create(SendEmailVerificationCodeSchema, { + urlTemplate, + }); - request = { ...request, verification: { case: "sendCode", value: medium } }; - } + request = { ...request, verification: { case: "sendCode", value: medium } }; const userService: Client = await createServiceForHost( UserService, @@ -999,7 +981,7 @@ export async function getIDPByID({ id: string; }) { const idpService: Client = - await createServiceForHost(IdentityProviderService, host); + await createServiceForHost(IdentityProviderService, serviceUrl); return idpService.getIDPByID({ id }, {}).then((resp) => resp.idp); } @@ -1033,21 +1015,21 @@ export async function addIDPLink({ export async function passwordReset({ serviceUrl, - urlTemplate, userId, + urlTemplate, }: { serviceUrl: string; - urlTemplate: string; userId: string; + urlTemplate?: string; }) { let medium = create(SendPasswordResetLinkSchema, { notificationType: NotificationType.Email, }); - medium = { - ...medium, - urlTemplate, - }; + medium = { + ...medium, + urlTemplate, + }; const userService: Client = await createServiceForHost( UserService, @@ -1238,7 +1220,7 @@ export async function getActiveIdentityProviders({ props.linkingAllowed = linking_allowed; } const settingsService: Client = - await createServiceForHost(SettingsService, host); + await createServiceForHost(SettingsService, serviceUrl); return settingsService.getActiveIdentityProviders(props, {}); } diff --git a/apps/login/src/middleware.ts b/apps/login/src/middleware.ts index e059aa032f2..816ea0f066d 100644 --- a/apps/login/src/middleware.ts +++ b/apps/login/src/middleware.ts @@ -22,9 +22,9 @@ export async function middleware(request: NextRequest) { // } const _headers = await headers(); - const instanceUrl = getApiUrlOfHeaders(_headers); + const serviceUrl = getApiUrlOfHeaders(_headers); - const instanceHost = `${instanceUrl}`.replace("https://", ""); + const instanceHost = `${serviceUrl}`.replace("https://", ""); const requestHeaders = new Headers(request.headers); requestHeaders.set( @@ -43,7 +43,7 @@ export async function middleware(request: NextRequest) { responseHeaders.set("Access-Control-Allow-Origin", "*"); responseHeaders.set("Access-Control-Allow-Headers", "*"); - request.nextUrl.href = `${instanceUrl}${request.nextUrl.pathname}${request.nextUrl.search}`; + request.nextUrl.href = `${serviceUrl}${request.nextUrl.pathname}${request.nextUrl.search}`; return NextResponse.rewrite(request.nextUrl, { request: { headers: requestHeaders, diff --git a/turbo.json b/turbo.json index 003d6c11cc7..1db73e60c96 100644 --- a/turbo.json +++ b/turbo.json @@ -11,7 +11,8 @@ "SYSTEM_USER_PRIVATE_KEY", "ZITADEL_API_URL", "ZITADEL_SERVICE_USER_ID", - "ZITADEL_SERVICE_USER_TOKEN" + "ZITADEL_SERVICE_USER_TOKEN", + "NEXT_PUBLIC_BASE_PATH" ], "tasks": { "generate": {