mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-01 20:02:15 +00:00
# Which Problems Are Solved This PR introduces robust redirect configuration options and enhances security for Server Actions in multi-domain setups. # How the Problems Are Solved - Redirect Fallback Logic: Implemented a unified `resolveRedirectUri` utility that prioritizes redirect targets as follows: - Environment override (`DEFAULT_REDIRECT_URI`) - Organization settings (`defaultRedirectUri`). - Relative fallback (`/signedin?...`). - Host Reflection: `DEFAULT_REDIRECT_URI` supports absolute URLs or relative paths via host reflection. - Server Action Security: Configured `serverActions.allowedOrigins` to be dynamically manageable via the `SERVER_ACTION_ALLOWED_ORIGINS` environment variable (comma-separated list). It defaults to an empty list for maximum security. - UI Integration: Updated the /signedin page to use the unified redirect logic, ensuring consistent behavior for the "Continue" button. - Documentation: Added type definitions and detailed descriptions for new environment variables in `next-env-vars.d.ts` --------- Co-authored-by: Copilot <[email protected]>
112 lines
3.4 KiB
TypeScript
112 lines
3.4 KiB
TypeScript
"use client";
|
|
|
|
import { skipMFAAndContinueWithNextUrl } from "@/lib/server/session";
|
|
import { LoginSettings, SecondFactorType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
|
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
|
|
import { useRouter } from "next/navigation";
|
|
import { EMAIL, SMS, TOTP, U2F } from "./auth-methods";
|
|
import { Translated } from "./translated";
|
|
import { useState } from "react";
|
|
import { handleServerActionResponse } from "@/lib/client-utils";
|
|
import { AutoSubmitForm } from "./auto-submit-form";
|
|
import { Alert } from "./alert";
|
|
|
|
type Props = {
|
|
userId: string;
|
|
loginName?: string;
|
|
sessionId?: string;
|
|
requestId?: string;
|
|
organization?: string;
|
|
loginSettings: LoginSettings;
|
|
userMethods: AuthenticationMethodType[];
|
|
checkAfter: boolean;
|
|
phoneVerified: boolean;
|
|
emailVerified: boolean;
|
|
force: boolean;
|
|
};
|
|
|
|
export function ChooseSecondFactorToSetup({
|
|
userId,
|
|
loginName,
|
|
sessionId,
|
|
requestId,
|
|
organization,
|
|
loginSettings,
|
|
userMethods,
|
|
checkAfter,
|
|
phoneVerified,
|
|
emailVerified,
|
|
force,
|
|
}: Props) {
|
|
const router = useRouter();
|
|
const params = new URLSearchParams({});
|
|
|
|
const [error, setError] = useState<string>("");
|
|
const [samlData, setSamlData] = useState<{ url: string; fields: Record<string, string> } | null>(null);
|
|
|
|
if (loginName) {
|
|
params.append("loginName", loginName);
|
|
}
|
|
if (sessionId) {
|
|
params.append("sessionId", sessionId);
|
|
}
|
|
if (requestId) {
|
|
params.append("requestId", requestId);
|
|
}
|
|
if (organization) {
|
|
params.append("organization", organization);
|
|
}
|
|
if (checkAfter) {
|
|
params.append("checkAfter", "true");
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{samlData && <AutoSubmitForm url={samlData.url} fields={samlData.fields} />}
|
|
<div className="grid w-full grid-cols-1 gap-5 pt-4">
|
|
{loginSettings.secondFactors.map((factor) => {
|
|
switch (factor) {
|
|
case SecondFactorType.OTP:
|
|
return TOTP(userMethods.includes(AuthenticationMethodType.TOTP), "/otp/time-based/set?" + params);
|
|
case SecondFactorType.U2F:
|
|
return U2F(userMethods.includes(AuthenticationMethodType.U2F), "/u2f/set?" + params);
|
|
case SecondFactorType.OTP_EMAIL:
|
|
return (
|
|
emailVerified && EMAIL(userMethods.includes(AuthenticationMethodType.OTP_EMAIL), "/otp/email/set?" + params)
|
|
);
|
|
case SecondFactorType.OTP_SMS:
|
|
return phoneVerified && SMS(userMethods.includes(AuthenticationMethodType.OTP_SMS), "/otp/sms/set?" + params);
|
|
default:
|
|
return null;
|
|
}
|
|
})}
|
|
</div>
|
|
{!force && (
|
|
<button
|
|
className="text-sm transition-all hover:text-primary-light-500 dark:hover:text-primary-dark-500"
|
|
onClick={async () => {
|
|
const skipResponse = await skipMFAAndContinueWithNextUrl({
|
|
userId,
|
|
loginName,
|
|
sessionId,
|
|
organization,
|
|
requestId,
|
|
});
|
|
|
|
handleServerActionResponse(skipResponse, router, setSamlData, setError);
|
|
}}
|
|
type="button"
|
|
data-testid="reset-button"
|
|
>
|
|
<Translated i18nKey="set.skip" namespace="mfa" />
|
|
</button>
|
|
)}
|
|
{error && (
|
|
<div className="py-4" data-testid="error">
|
|
<Alert>{error}</Alert>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|