mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-05 05:42:16 +00:00
feat(login): add support for disabling automatic email verification (#11515)
# Which Problems Are Solved Enterprise email link scanners (used by organizations for security) pre-fetch URLs in emails to check for malicious content. In Login V2, email verification codes are automatically redeemed when the verification page loads. This causes link scanners to inadvertently consume one-time verification codes before users can click them, resulting in "code already used" errors. # How the Problems Are Solved - Changed the default behaviour so that verification codes are **not** auto-submitted on page load — users must click a Submit button to redeem the code - Added a new environment variable `NEXT_PUBLIC_AUTO_SUBMIT_CODE` that can be set to `true` to re-enable auto-submission for instances where link scanners are not a concern - The env var is named generically so it can be extended to other auto-submit flows (e.g. Email OTP) in the future # Additional Changes None # Additional Context Closes #11406
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { VerifyForm } from "./verify-form";
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
useRouter: () => ({ push: vi.fn() }),
|
||||
}));
|
||||
|
||||
vi.mock("next-intl", () => ({
|
||||
useTranslations: () => (key: string) => key,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/server/verify", () => ({
|
||||
sendVerification: vi.fn(),
|
||||
resendVerification: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("VerifyForm", () => {
|
||||
let mockSendVerification: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(async () => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
const { sendVerification } = await import("@/lib/server/verify");
|
||||
mockSendVerification = vi.mocked(sendVerification);
|
||||
mockSendVerification.mockResolvedValue({ redirect: "/success" });
|
||||
});
|
||||
|
||||
afterEach(cleanup);
|
||||
|
||||
describe("Auto-submit Behavior", () => {
|
||||
test("should call sendVerification automatically when submit=true", async () => {
|
||||
render(<VerifyForm userId="user-1" code="123456" isInvite={false} submit={true} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSendVerification).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
code: "123456",
|
||||
userId: "user-1",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("should prefill code but not auto-submit when submit=false", () => {
|
||||
render(<VerifyForm userId="user-1" code="123456" isInvite={false} submit={false} />);
|
||||
|
||||
const input = screen.getByTestId("code-text-input");
|
||||
expect(input).toHaveValue("123456");
|
||||
|
||||
const submitButton = screen.getByTestId("submit-button");
|
||||
expect(submitButton).toBeInTheDocument();
|
||||
|
||||
expect(mockSendVerification).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -26,9 +26,10 @@ type Props = {
|
||||
code?: string;
|
||||
isInvite: boolean;
|
||||
requestId?: string;
|
||||
submit: boolean;
|
||||
};
|
||||
|
||||
export function VerifyForm({ userId, loginName, organization, requestId, code, isInvite }: Props) {
|
||||
export function VerifyForm({ userId, loginName, organization, requestId, code, isInvite, submit }: Props) {
|
||||
const router = useRouter();
|
||||
|
||||
const { register, handleSubmit, formState } = useForm<Inputs>({
|
||||
@@ -104,11 +105,11 @@ export function VerifyForm({ userId, loginName, organization, requestId, code, i
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (code && code !== processedCode.current) {
|
||||
if (submit && code && code !== processedCode.current) {
|
||||
processedCode.current = code;
|
||||
fcn({ code });
|
||||
}
|
||||
}, [code, fcn]);
|
||||
}, [submit, code, fcn]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user