mirror of
https://github.com/zitadel/zitadel.git
synced 2026-08-18 17:05:03 -05:00
fix(login): preserve org domain suffix through account chooser navigation (#12304)
Closes #12024 # Which Problems Are Solved When the login flow is started with an `urn:zitadel:iam:org:domain:primary:{domain}` scope, the resolved domain suffix is lost if the user navigates through the account chooser (e.g. clicking "Use another account"). This causes the login to fail with "User not found" because the organization can no longer be resolved. Additionally, the `hideLoginNameSuffix` branding setting was never respected by the login app. # How the Problems Are Solved - Forward the `orgDomain` parameter through `gotoAccounts()` and the `/accounts` page, so it survives navigation to `/loginname` — the same way `organization` and `requestId` are already forwarded. - Respect `BrandingSettings.hideLoginNameSuffix`: when enabled, the `@domain.com` suffix is hidden from the input UI but still used internally for user search. --------- Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
This commit is contained in:
co-authored by
Livio Spring
parent
6b629c2290
commit
1beabed8cf
@@ -43,6 +43,7 @@ export default async function Page(props: { searchParams: Promise<Record<string
|
||||
|
||||
const requestId = searchParams?.requestId;
|
||||
const organization = searchParams?.organization;
|
||||
const orgDomain = searchParams?.orgDomain;
|
||||
|
||||
const _headers = await headers();
|
||||
const { serviceConfig } = getServiceConfig(_headers);
|
||||
@@ -69,6 +70,10 @@ export default async function Page(props: { searchParams: Promise<Record<string
|
||||
params.append("organization", organization);
|
||||
}
|
||||
|
||||
if (orgDomain) {
|
||||
params.append("orgDomain", orgDomain);
|
||||
}
|
||||
|
||||
return (
|
||||
<DynamicTheme branding={branding}>
|
||||
<div className="flex flex-col space-y-4">
|
||||
|
||||
@@ -20,7 +20,7 @@ export default async function Page(props: { searchParams: Promise<Record<string
|
||||
const loginName = searchParams?.loginName;
|
||||
const requestId = searchParams?.requestId;
|
||||
const organization = searchParams?.organization;
|
||||
const suffix = searchParams?.suffix;
|
||||
const orgDomain = searchParams?.orgDomain;
|
||||
const submit: boolean = searchParams?.submit === "true";
|
||||
|
||||
const _headers = await headers();
|
||||
@@ -64,7 +64,8 @@ export default async function Page(props: { searchParams: Promise<Record<string
|
||||
organization={organization} // stick to "organization" as we still want to do user discovery based on the searchParams not the default organization, later the organization is determined by the found user
|
||||
defaultOrganization={defaultOrganization}
|
||||
loginSettings={loginSettings}
|
||||
suffix={suffix}
|
||||
suffix={orgDomain}
|
||||
hideSuffix={branding?.hideLoginNameSuffix}
|
||||
submit={submit}
|
||||
allowRegister={!!loginSettings?.allowRegister}
|
||||
></UsernameForm>
|
||||
|
||||
@@ -26,6 +26,7 @@ type Props = {
|
||||
organization?: string;
|
||||
defaultOrganization?: string;
|
||||
suffix?: string;
|
||||
hideSuffix?: boolean;
|
||||
submit: boolean;
|
||||
allowRegister: boolean;
|
||||
};
|
||||
@@ -36,6 +37,7 @@ export function UsernameForm({
|
||||
organization,
|
||||
defaultOrganization,
|
||||
suffix,
|
||||
hideSuffix,
|
||||
loginSettings,
|
||||
submit,
|
||||
allowRegister,
|
||||
@@ -111,7 +113,7 @@ export function UsernameForm({
|
||||
{...register("loginName", { required: t("required.loginName") })}
|
||||
label={inputLabel}
|
||||
data-testid="username-text-input"
|
||||
suffix={suffix}
|
||||
suffix={hideSuffix ? undefined : suffix}
|
||||
/>
|
||||
{allowRegister && (
|
||||
<button
|
||||
|
||||
@@ -57,10 +57,12 @@ const gotoAccounts = ({
|
||||
request,
|
||||
requestId,
|
||||
organization,
|
||||
orgDomain,
|
||||
}: {
|
||||
request: NextRequest;
|
||||
requestId: string;
|
||||
organization?: string;
|
||||
orgDomain?: string;
|
||||
}): NextResponse<unknown> => {
|
||||
const accountsUrl = constructUrl(request, "/accounts");
|
||||
|
||||
@@ -70,6 +72,9 @@ const gotoAccounts = ({
|
||||
if (organization) {
|
||||
accountsUrl.searchParams.set("organization", organization);
|
||||
}
|
||||
if (orgDomain) {
|
||||
accountsUrl.searchParams.set("orgDomain", orgDomain);
|
||||
}
|
||||
|
||||
return NextResponse.redirect(accountsUrl);
|
||||
};
|
||||
@@ -79,13 +84,13 @@ const gotoLoginname = ({
|
||||
requestId,
|
||||
loginHint,
|
||||
organization,
|
||||
suffix,
|
||||
orgDomain,
|
||||
}: {
|
||||
request: NextRequest;
|
||||
requestId: string;
|
||||
loginHint?: string;
|
||||
organization?: string;
|
||||
suffix?: string;
|
||||
orgDomain?: string;
|
||||
}): NextResponse<unknown> => {
|
||||
const loginNameUrl = constructUrl(request, "/loginname");
|
||||
loginNameUrl.searchParams.set("requestId", requestId);
|
||||
@@ -97,8 +102,8 @@ const gotoLoginname = ({
|
||||
if (organization) {
|
||||
loginNameUrl.searchParams.set("organization", organization);
|
||||
}
|
||||
if (suffix) {
|
||||
loginNameUrl.searchParams.set("suffix", suffix);
|
||||
if (orgDomain) {
|
||||
loginNameUrl.searchParams.set("orgDomain", orgDomain);
|
||||
}
|
||||
|
||||
return NextResponse.redirect(loginNameUrl);
|
||||
@@ -140,7 +145,7 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
|
||||
}
|
||||
|
||||
let organization = "";
|
||||
let suffix = "";
|
||||
let orgDomain = "";
|
||||
let idpId = "";
|
||||
|
||||
if (authRequest?.scope) {
|
||||
@@ -155,15 +160,15 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
|
||||
|
||||
if (orgDomainScope) {
|
||||
const matched = ORG_DOMAIN_SCOPE_REGEX.exec(orgDomainScope);
|
||||
const orgDomain = matched?.[1] ?? "";
|
||||
const scopeDomain = matched?.[1] ?? "";
|
||||
|
||||
logger.info("Extracted org domain:", { orgDomain });
|
||||
if (orgDomain) {
|
||||
const orgs = await getOrgsByDomain({ serviceConfig, domain: orgDomain });
|
||||
logger.info("Extracted org domain:", { orgDomain: scopeDomain });
|
||||
if (scopeDomain) {
|
||||
const orgs = await getOrgsByDomain({ serviceConfig, domain: scopeDomain });
|
||||
|
||||
if (orgs.result && orgs.result.length === 1) {
|
||||
organization = orgs.result[0].id ?? "";
|
||||
suffix = orgDomain;
|
||||
orgDomain = scopeDomain;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,13 +280,14 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
|
||||
requestId,
|
||||
loginHint: authRequest.loginHint,
|
||||
organization,
|
||||
suffix,
|
||||
orgDomain,
|
||||
});
|
||||
}
|
||||
return gotoAccounts({
|
||||
request,
|
||||
requestId,
|
||||
organization,
|
||||
orgDomain,
|
||||
});
|
||||
} else if (authRequest.prompt.includes(Prompt.LOGIN)) {
|
||||
if (authRequest.loginHint) {
|
||||
@@ -315,8 +321,8 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
|
||||
if (organization) {
|
||||
loginNameUrl.searchParams.set("organization", organization);
|
||||
}
|
||||
if (suffix) {
|
||||
loginNameUrl.searchParams.set("suffix", suffix);
|
||||
if (orgDomain) {
|
||||
loginNameUrl.searchParams.set("orgDomain", orgDomain);
|
||||
}
|
||||
return NextResponse.redirect(loginNameUrl);
|
||||
} else if (authRequest.prompt.includes(Prompt.NONE)) {
|
||||
@@ -378,13 +384,14 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
|
||||
requestId,
|
||||
loginHint: authRequest.loginHint,
|
||||
organization,
|
||||
suffix,
|
||||
orgDomain,
|
||||
});
|
||||
}
|
||||
return gotoAccounts({
|
||||
request,
|
||||
requestId,
|
||||
organization,
|
||||
orgDomain,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -395,6 +402,7 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
|
||||
request,
|
||||
requestId,
|
||||
organization,
|
||||
orgDomain,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -425,6 +433,7 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
|
||||
return gotoAccounts({
|
||||
request,
|
||||
organization,
|
||||
orgDomain,
|
||||
requestId,
|
||||
});
|
||||
}
|
||||
@@ -434,6 +443,7 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
|
||||
request,
|
||||
requestId,
|
||||
organization,
|
||||
orgDomain,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -450,8 +460,8 @@ export async function handleOIDCFlowInitiation(params: FlowInitiationParams): Pr
|
||||
loginNameUrl.searchParams.set("organization", organization);
|
||||
}
|
||||
|
||||
if (suffix) {
|
||||
loginNameUrl.searchParams.set("suffix", suffix);
|
||||
if (orgDomain) {
|
||||
loginNameUrl.searchParams.set("orgDomain", orgDomain);
|
||||
}
|
||||
|
||||
return NextResponse.redirect(loginNameUrl);
|
||||
|
||||
Reference in New Issue
Block a user