Files
zitadel/apps/login/src/lib/client.ts
T
RamonandGitHub 057fb8dff2 fix(login): prettier setup (#11798)
# Which Problems Are Solved
Invalid formatting in the login codebase due to invalid prettier
configuration

# How the Problems Are Solved
Fxed .prettierignore wildcards for login source files

# Additional Changes
Removed .prettierrc file because there is already a prettier.config.mjs
file

# Additional Context
2026-03-10 13:30:20 +00:00

156 lines
5.3 KiB
TypeScript

"use server";
import { headers } from "next/headers";
import { isSafeRedirectUri } from "./client-utils";
import { completeAuthFlow } from "./server/auth-flow";
import { getPublicHostWithProtocol } from "./server/host";
type FinishFlowCommand =
| {
sessionId: string;
requestId: string;
}
| { loginName: string };
function goToSignedInPage(
props:
| { sessionId: string; organization?: string; requestId?: string }
| { organization?: string; loginName: string; requestId?: string },
) {
const params = new URLSearchParams({});
if ("loginName" in props && props.loginName) {
params.append("loginName", props.loginName);
}
if ("sessionId" in props && props.sessionId) {
params.append("sessionId", props.sessionId);
}
if (props.organization) {
params.append("organization", props.organization);
}
// required to show conditional UI for device flow
if (props.requestId) {
params.append("requestId", props.requestId);
}
return `/signedin?` + params;
}
/**
* Complete authentication flow or get next URL for navigation
* - For OIDC/SAML flows with sessionId+requestId: completes flow directly via server action
* - For device flows: returns URL to signed-in page
* - For other cases: returns default redirect or fallback URL
*/
export async function completeFlowOrGetUrl(
command: FinishFlowCommand & { organization?: string },
defaultRedirectUri?: string,
): Promise<{ redirect: string } | { error: string } | { samlData: { url: string; fields: Record<string, string> } }> {
console.log("completeFlowOrGetUrl called with:", command, "defaultRedirectUri:", defaultRedirectUri);
// Complete OIDC/SAML flows directly with server action
if (
"sessionId" in command &&
"requestId" in command &&
(command.requestId.startsWith("saml_") || command.requestId.startsWith("oidc_"))
) {
console.log("completeFlowOrGetUrl: OIDC/SAML flow detected");
// This completes the flow and returns a redirect URL or error
const result = await completeAuthFlow({
sessionId: command.sessionId,
requestId: command.requestId,
});
console.log("completeFlowOrGetUrl: got OIDC/SAML flow result");
return result;
}
console.log("completeFlowOrGetUrl: Regular flow, getting next URL");
// For all other cases, return URL for navigation
const url = await getNextUrl(command, defaultRedirectUri);
console.log("completeFlowOrGetUrl: got Next URL:", url);
const result = { redirect: url };
console.log("completeFlowOrGetUrl: got final result");
return result;
}
/**
* for client: redirects user back to device flow completion, default redirect, or success page
* Note: OIDC/SAML flows now use completeAuthFlowAction() instead of URL navigation
* @param command
* @returns
*/
export async function getNextUrl(
command: FinishFlowCommand & { organization?: string },
defaultRedirectUri?: string,
): Promise<string> {
console.log("getNextUrl called with:", command, "defaultRedirectUri:", defaultRedirectUri);
// finish Device Authorization Flow
if (
"requestId" in command &&
command.requestId.startsWith("device_") &&
("loginName" in command || "sessionId" in command)
) {
const result = goToSignedInPage({
...command,
organization: command.organization,
});
console.log("getNextUrl: Got Device flow result");
return result;
}
// OIDC/SAML flows are now handled by completeAuthFlowAction() server action
// This function only handles device flows and fallback navigation
const result = await resolveRedirectUri(command, defaultRedirectUri);
console.log("getNextUrl: Resolved redirect URI:", result);
return result;
}
/**
* Resolves the redirect URI based on the following priority:
* 1. DEFAULT_REDIRECT_URI environment variable
* 2. defaultRedirectUri from organization settings
* 3. Relative signed-in page fallback
* 4. Reserved for future extensions
*/
export async function resolveRedirectUri(command: FinishFlowCommand, defaultRedirectUri?: string): Promise<string> {
// 1. Environment variable override
const envOverride = process.env.DEFAULT_REDIRECT_URI;
if (envOverride) {
if (envOverride.startsWith("/")) {
// Special state: trigger absolute host-based redirect with provided path
try {
const _headers = await headers();
const host = getPublicHostWithProtocol(_headers);
const result = `${host}${envOverride}`;
console.log("resolveRedirectUri: Using host-based redirect from override:", result);
return result;
} catch (error) {
console.warn("resolveRedirectUri: Could not determine host for override, falling back", error);
}
} else {
console.log("resolveRedirectUri: Using DEFAULT_REDIRECT_URI override:", envOverride);
return envOverride;
}
}
// 2. Default redirect URI from settings
if (defaultRedirectUri) {
if (isSafeRedirectUri(defaultRedirectUri)) {
console.log("resolveRedirectUri: Using defaultRedirectUri from settings:", defaultRedirectUri);
return defaultRedirectUri;
} else {
console.warn("resolveRedirectUri: Unsafe defaultRedirectUri prevented:", defaultRedirectUri);
}
}
// 3. Default signed-in page (relative)
const result = goToSignedInPage(command);
console.log("resolveRedirectUri: Using relative goToSignedInPage result:", result);
return result;
}