From 97376a8f8e6735ca9884d18f03f711557ac6c77a Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 3 Mar 2025 18:58:54 +0100 Subject: [PATCH 01/10] fix: construct url using host --- apps/login/src/app/login/route.ts | 23 ++++++++++++++++++----- apps/login/src/lib/oidc.ts | 5 +++-- apps/login/src/lib/saml.ts | 5 +++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/apps/login/src/app/login/route.ts b/apps/login/src/app/login/route.ts index dd91519ee4..123a6d12a2 100644 --- a/apps/login/src/app/login/route.ts +++ b/apps/login/src/app/login/route.ts @@ -39,7 +39,7 @@ const gotoAccounts = ({ requestId: string; organization?: string; }): NextResponse => { - const accountsUrl = new URL("/accounts", request.url); + const accountsUrl = constructUrl(request, "/accounts"); if (requestId) { accountsUrl.searchParams.set("requestId", requestId); @@ -66,6 +66,19 @@ async function loadSessions({ return response?.sessions ?? []; } +export function constructUrl(request: NextRequest, path: string) { + const forwardedHost = + request.headers.get("x-zitadel-forward-host") ?? + request.headers.get("host"); + const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ""; + return new URL( + `${basePath}${path}`, + forwardedHost?.startsWith("http") + ? forwardedHost + : `https://${forwardedHost}`, + ); +} + const ORG_SCOPE_REGEX = /urn:zitadel:iam:org:id:([0-9]+)/; const ORG_DOMAIN_SCOPE_REGEX = /urn:zitadel:iam:org:domain:primary:(.+)/; // TODO: check regex for all domain character options const IDP_SCOPE_REGEX = /urn:zitadel:iam:org:idp:id:(.+)/; @@ -223,7 +236,7 @@ export async function GET(request: NextRequest) { } if (authRequest && authRequest.prompt.includes(Prompt.CREATE)) { - const registerUrl = new URL("/register", request.url); + const registerUrl = constructUrl(request, "/register"); if (authRequest.id) { registerUrl.searchParams.set("requestId", `oidc_${authRequest.id}`); } @@ -263,7 +276,7 @@ export async function GET(request: NextRequest) { const res = await sendLoginname(command); if (res && "redirect" in res && res?.redirect) { - const absoluteUrl = new URL(res.redirect, request.url); + const absoluteUrl = constructUrl(request, res.redirect); return NextResponse.redirect(absoluteUrl.toString()); } } catch (error) { @@ -271,7 +284,7 @@ export async function GET(request: NextRequest) { } } - const loginNameUrl = new URL("/loginname", request.url); + const loginNameUrl = constructUrl(request, "/loginname"); if (authRequest.id) { loginNameUrl.searchParams.set("requestId", `oidc_${authRequest.id}`); } @@ -398,7 +411,7 @@ export async function GET(request: NextRequest) { } } } else { - const loginNameUrl = new URL("/loginname", request.url); + const loginNameUrl = constructUrl(request, "/loginname"); loginNameUrl.searchParams.set("requestId", requestId); if (authRequest?.loginHint) { diff --git a/apps/login/src/lib/oidc.ts b/apps/login/src/lib/oidc.ts index fd74e57027..290b0ee7f1 100644 --- a/apps/login/src/lib/oidc.ts +++ b/apps/login/src/lib/oidc.ts @@ -1,3 +1,4 @@ +import { constructUrl } from "@/app/login/route"; import { Cookie } from "@/lib/cookies"; import { sendLoginname, SendLoginnameCommand } from "@/lib/server/loginname"; import { createCallback, getLoginSettings } from "@/lib/zitadel"; @@ -54,7 +55,7 @@ export async function loginWithOIDCandSession({ const res = await sendLoginname(command); if (res && "redirect" in res && res?.redirect) { - const absoluteUrl = new URL(res.redirect, request.url); + const absoluteUrl = constructUrl(request, res.redirect); return NextResponse.redirect(absoluteUrl.toString()); } } @@ -107,7 +108,7 @@ export async function loginWithOIDCandSession({ return NextResponse.redirect(loginSettings.defaultRedirectUri); } - const signedinUrl = new URL("/signedin", request.url); + const signedinUrl = constructUrl(request, "/signedin"); if (selectedSession.factors?.user?.loginName) { signedinUrl.searchParams.set( diff --git a/apps/login/src/lib/saml.ts b/apps/login/src/lib/saml.ts index d559c8e7df..b8241d7c8c 100644 --- a/apps/login/src/lib/saml.ts +++ b/apps/login/src/lib/saml.ts @@ -1,3 +1,4 @@ +import { constructUrl } from "@/app/login/route"; import { Cookie } from "@/lib/cookies"; import { sendLoginname, SendLoginnameCommand } from "@/lib/server/loginname"; import { createResponse, getLoginSettings } from "@/lib/zitadel"; @@ -52,7 +53,7 @@ export async function loginWithSAMLandSession({ const res = await sendLoginname(command); if (res && "redirect" in res && res?.redirect) { - const absoluteUrl = new URL(res.redirect, request.url); + const absoluteUrl = constructUrl(request, res.redirect); return NextResponse.redirect(absoluteUrl.toString()); } } @@ -105,7 +106,7 @@ export async function loginWithSAMLandSession({ return NextResponse.redirect(loginSettings.defaultRedirectUri); } - const signedinUrl = new URL("/signedin", request.url); + const signedinUrl = constructUrl(request, "/signedin"); if (selectedSession.factors?.user?.loginName) { signedinUrl.searchParams.set( From 9e101caa2ec510c5e97ef1c439cae6c81bcd14ec Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 3 Mar 2025 19:02:01 +0100 Subject: [PATCH 02/10] reorganize imports --- apps/login/src/app/login/route.ts | 13 ------------- apps/login/src/lib/oidc.ts | 2 +- apps/login/src/lib/saml.ts | 2 +- apps/login/src/lib/service.ts | 14 ++++++++++++++ 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/apps/login/src/app/login/route.ts b/apps/login/src/app/login/route.ts index 123a6d12a2..9661247d19 100644 --- a/apps/login/src/app/login/route.ts +++ b/apps/login/src/app/login/route.ts @@ -66,19 +66,6 @@ async function loadSessions({ return response?.sessions ?? []; } -export function constructUrl(request: NextRequest, path: string) { - const forwardedHost = - request.headers.get("x-zitadel-forward-host") ?? - request.headers.get("host"); - const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ""; - return new URL( - `${basePath}${path}`, - forwardedHost?.startsWith("http") - ? forwardedHost - : `https://${forwardedHost}`, - ); -} - const ORG_SCOPE_REGEX = /urn:zitadel:iam:org:id:([0-9]+)/; const ORG_DOMAIN_SCOPE_REGEX = /urn:zitadel:iam:org:domain:primary:(.+)/; // TODO: check regex for all domain character options const IDP_SCOPE_REGEX = /urn:zitadel:iam:org:idp:id:(.+)/; diff --git a/apps/login/src/lib/oidc.ts b/apps/login/src/lib/oidc.ts index 290b0ee7f1..c1038d90c4 100644 --- a/apps/login/src/lib/oidc.ts +++ b/apps/login/src/lib/oidc.ts @@ -1,4 +1,3 @@ -import { constructUrl } from "@/app/login/route"; import { Cookie } from "@/lib/cookies"; import { sendLoginname, SendLoginnameCommand } from "@/lib/server/loginname"; import { createCallback, getLoginSettings } from "@/lib/zitadel"; @@ -9,6 +8,7 @@ import { } from "@zitadel/proto/zitadel/oidc/v2/oidc_service_pb"; import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb"; import { NextRequest, NextResponse } from "next/server"; +import { constructUrl } from "./service"; import { isSessionValid } from "./session"; type LoginWithOIDCandSession = { diff --git a/apps/login/src/lib/saml.ts b/apps/login/src/lib/saml.ts index b8241d7c8c..9b12e48d25 100644 --- a/apps/login/src/lib/saml.ts +++ b/apps/login/src/lib/saml.ts @@ -1,4 +1,3 @@ -import { constructUrl } from "@/app/login/route"; import { Cookie } from "@/lib/cookies"; import { sendLoginname, SendLoginnameCommand } from "@/lib/server/loginname"; import { createResponse, getLoginSettings } from "@/lib/zitadel"; @@ -6,6 +5,7 @@ import { create } from "@zitadel/client"; import { CreateResponseRequestSchema } from "@zitadel/proto/zitadel/saml/v2/saml_service_pb"; import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb"; import { NextRequest, NextResponse } from "next/server"; +import { constructUrl } from "./service"; import { isSessionValid } from "./session"; type LoginWithSAMLandSession = { diff --git a/apps/login/src/lib/service.ts b/apps/login/src/lib/service.ts index b6d320c0f4..e38ec21d1a 100644 --- a/apps/login/src/lib/service.ts +++ b/apps/login/src/lib/service.ts @@ -8,6 +8,7 @@ import { SessionService } from "@zitadel/proto/zitadel/session/v2/session_servic import { SettingsService } from "@zitadel/proto/zitadel/settings/v2/settings_service_pb"; import { UserService } from "@zitadel/proto/zitadel/user/v2/user_service_pb"; import { ReadonlyHeaders } from "next/dist/server/web/spec-extension/adapters/headers"; +import { NextRequest } from "next/server"; import { systemAPIToken } from "./api"; type ServiceClass = @@ -108,3 +109,16 @@ export function getServiceUrlFromHeaders(headers: ReadonlyHeaders): { serviceUrl: instanceUrl, }; } + +export function constructUrl(request: NextRequest, path: string) { + const forwardedHost = + request.headers.get("x-zitadel-forward-host") ?? + request.headers.get("host"); + const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ""; + return new URL( + `${basePath}${path}`, + forwardedHost?.startsWith("http") + ? forwardedHost + : `https://${forwardedHost}`, + ); +} From 6be63a18859f14f4aab95e35a4be5d07e51cf6bb Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 3 Mar 2025 19:02:17 +0100 Subject: [PATCH 03/10] imports --- apps/login/src/app/login/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/login/src/app/login/route.ts b/apps/login/src/app/login/route.ts index 9661247d19..2db81e070e 100644 --- a/apps/login/src/app/login/route.ts +++ b/apps/login/src/app/login/route.ts @@ -3,7 +3,7 @@ import { idpTypeToSlug } from "@/lib/idp"; import { loginWithOIDCandSession } from "@/lib/oidc"; import { loginWithSAMLandSession } from "@/lib/saml"; import { sendLoginname, SendLoginnameCommand } from "@/lib/server/loginname"; -import { getServiceUrlFromHeaders } from "@/lib/service"; +import { constructUrl, getServiceUrlFromHeaders } from "@/lib/service"; import { findValidSession } from "@/lib/session"; import { createCallback, From 530ab4405aa05ac3f80e087f5149a77174274b45 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 4 Mar 2025 08:37:55 +0100 Subject: [PATCH 04/10] proto --- apps/login/src/lib/service.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/login/src/lib/service.ts b/apps/login/src/lib/service.ts index e38ec21d1a..7d4b1cb940 100644 --- a/apps/login/src/lib/service.ts +++ b/apps/login/src/lib/service.ts @@ -111,14 +111,19 @@ export function getServiceUrlFromHeaders(headers: ReadonlyHeaders): { } export function constructUrl(request: NextRequest, path: string) { + const forwardedProto = + request.headers.get("x-forwarded-proto") ?? request.nextUrl.protocol; + const forwardedHost = request.headers.get("x-zitadel-forward-host") ?? request.headers.get("host"); const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ""; return new URL( `${basePath}${path}`, + + // keep this check to allow localhost for local development forwardedHost?.startsWith("http") ? forwardedHost - : `https://${forwardedHost}`, + : `${forwardedProto}//${forwardedHost}`, ); } From 56cd9ce453c231aa2b4d8d2173cae1440c920822 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 4 Mar 2025 08:40:57 +0100 Subject: [PATCH 05/10] fallback to https --- apps/login/src/lib/service.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/login/src/lib/service.ts b/apps/login/src/lib/service.ts index 7d4b1cb940..a4e85e3ae3 100644 --- a/apps/login/src/lib/service.ts +++ b/apps/login/src/lib/service.ts @@ -111,8 +111,7 @@ export function getServiceUrlFromHeaders(headers: ReadonlyHeaders): { } export function constructUrl(request: NextRequest, path: string) { - const forwardedProto = - request.headers.get("x-forwarded-proto") ?? request.nextUrl.protocol; + const forwardedProto = request.headers.get("x-forwarded-proto") ?? "https"; const forwardedHost = request.headers.get("x-zitadel-forward-host") ?? @@ -124,6 +123,6 @@ export function constructUrl(request: NextRequest, path: string) { // keep this check to allow localhost for local development forwardedHost?.startsWith("http") ? forwardedHost - : `${forwardedProto}//${forwardedHost}`, + : `${forwardedProto}://${forwardedHost}`, ); } From 58fad21202c033e88d5013b352cb30d5f6fb5184 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 4 Mar 2025 16:07:57 +0100 Subject: [PATCH 06/10] Update apps/login/src/lib/service.ts Co-authored-by: Elio Bischof --- apps/login/src/lib/service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/login/src/lib/service.ts b/apps/login/src/lib/service.ts index a4e85e3ae3..d06f850247 100644 --- a/apps/login/src/lib/service.ts +++ b/apps/login/src/lib/service.ts @@ -111,7 +111,7 @@ export function getServiceUrlFromHeaders(headers: ReadonlyHeaders): { } export function constructUrl(request: NextRequest, path: string) { - const forwardedProto = request.headers.get("x-forwarded-proto") ?? "https"; + const forwardedProto = request.headers.get("x-forwarded-proto") ? `${request.headers.get("x-forwarded-proto")}:` : request.protocol; const forwardedHost = request.headers.get("x-zitadel-forward-host") ?? From 4e7bfb5779713fb9c728b3a3129fac4fa604f0c4 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 5 Mar 2025 08:50:07 +0100 Subject: [PATCH 07/10] Update apps/login/src/lib/service.ts Co-authored-by: Elio Bischof --- apps/login/src/lib/service.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/login/src/lib/service.ts b/apps/login/src/lib/service.ts index d06f850247..9916ee7c36 100644 --- a/apps/login/src/lib/service.ts +++ b/apps/login/src/lib/service.ts @@ -119,10 +119,6 @@ export function constructUrl(request: NextRequest, path: string) { const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ""; return new URL( `${basePath}${path}`, - - // keep this check to allow localhost for local development - forwardedHost?.startsWith("http") - ? forwardedHost - : `${forwardedProto}://${forwardedHost}`, + `${forwardedProto}//${forwardedHost}`, ); } From 5251834dad26d04dcad37f326cd5dcccad9a0647 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 5 Mar 2025 08:50:15 +0100 Subject: [PATCH 08/10] Update apps/login/src/lib/service.ts Co-authored-by: Elio Bischof --- apps/login/src/lib/service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/login/src/lib/service.ts b/apps/login/src/lib/service.ts index 9916ee7c36..da28dd0867 100644 --- a/apps/login/src/lib/service.ts +++ b/apps/login/src/lib/service.ts @@ -115,6 +115,7 @@ export function constructUrl(request: NextRequest, path: string) { const forwardedHost = request.headers.get("x-zitadel-forward-host") ?? + request.headers.get("x-forwarded-host") ?? request.headers.get("host"); const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ""; return new URL( From 498b589ebffaf278807a67e66cf384427e5aea73 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 5 Mar 2025 08:51:09 +0100 Subject: [PATCH 09/10] lint --- apps/login/src/lib/service.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/login/src/lib/service.ts b/apps/login/src/lib/service.ts index da28dd0867..5626086baa 100644 --- a/apps/login/src/lib/service.ts +++ b/apps/login/src/lib/service.ts @@ -111,15 +111,14 @@ export function getServiceUrlFromHeaders(headers: ReadonlyHeaders): { } export function constructUrl(request: NextRequest, path: string) { - const forwardedProto = request.headers.get("x-forwarded-proto") ? `${request.headers.get("x-forwarded-proto")}:` : request.protocol; + const forwardedProto = request.headers.get("x-forwarded-proto") + ? `${request.headers.get("x-forwarded-proto")}:` + : request.protocol; const forwardedHost = request.headers.get("x-zitadel-forward-host") ?? request.headers.get("x-forwarded-host") ?? request.headers.get("host"); const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ""; - return new URL( - `${basePath}${path}`, - `${forwardedProto}//${forwardedHost}`, - ); + return new URL(`${basePath}${path}`, `${forwardedProto}//${forwardedHost}`); } From ef1c2a7425254132278086bbee1e9d36e3d0016f Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Wed, 5 Mar 2025 08:52:29 +0100 Subject: [PATCH 10/10] nexturl protocol --- apps/login/src/lib/service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/login/src/lib/service.ts b/apps/login/src/lib/service.ts index 5626086baa..ec01c619f5 100644 --- a/apps/login/src/lib/service.ts +++ b/apps/login/src/lib/service.ts @@ -113,7 +113,7 @@ export function getServiceUrlFromHeaders(headers: ReadonlyHeaders): { export function constructUrl(request: NextRequest, path: string) { const forwardedProto = request.headers.get("x-forwarded-proto") ? `${request.headers.get("x-forwarded-proto")}:` - : request.protocol; + : request.nextUrl.protocol; const forwardedHost = request.headers.get("x-zitadel-forward-host") ??