"use client"; import { useState } from "react"; import { Button, ButtonVariants } from "./Button"; import { TextInput } from "./Input"; import { useForm } from "react-hook-form"; import { useRouter } from "next/navigation"; import { Spinner } from "./Spinner"; import Alert from "./Alert"; import { LoginSettings, AuthFactor, Checks } from "@zitadel/server"; type Inputs = { password: string; }; type Props = { loginSettings: LoginSettings | undefined; loginName?: string; organization?: string; authRequestId?: string; isAlternative?: boolean; // whether password was requested as alternative auth method promptPasswordless?: boolean; }; export default function PasswordForm({ loginSettings, loginName, organization, authRequestId, promptPasswordless, isAlternative, }: Props) { const { register, handleSubmit, formState } = useForm({ mode: "onBlur", }); const [error, setError] = useState(""); const [loading, setLoading] = useState(false); const router = useRouter(); async function submitPassword(values: Inputs) { setError(""); setLoading(true); const res = await fetch("/api/session", { method: "PUT", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ loginName, organization, checks: { password: { password: values.password }, } as Checks, authRequestId, }), }); const response = await res.json(); setLoading(false); if (!res.ok) { console.log(response.details.details); setError(response.details?.details ?? "Could not verify password"); return Promise.reject(response.details); } return response; } function submitPasswordAndContinue(value: Inputs): Promise { return submitPassword(value).then((resp) => { // if user has mfa -> /otp/[method] or /u2f // if mfa is forced and user has no mfa -> /mfa/set // if no passwordless -> /passkey/add if (resp.authFactors?.length == 1) { const params = new URLSearchParams({ loginName: resp.factors.user.loginName, }); if (authRequestId) { params.append("authRequestId", authRequestId); } if (organization) { params.append("organization", organization); } let method; const factor = (resp.authFactors as AuthFactor[])[0]; if (factor.otp) { method = "time-based"; return router.push(`/otp/${method}?` + params); } else if (factor.otpSms) { method = "sms"; return router.push(`/otp/${method}?` + params); } else if (factor.otpEmail) { method = "email"; return router.push(`/otp/${method}?` + params); } else if (factor.u2f) { method = "u2f"; return router.push(`/u2f?` + params); } } else if (resp.authFactors?.length >= 1) { const params = new URLSearchParams({ loginName: resp.factors.user.loginName, }); if (authRequestId) { params.append("authRequest", authRequestId); } if (organization) { params.append("organization", organization); } return router.push(`/mfa?` + params); } else if (loginSettings?.forceMfa && !resp.authFactors?.length) { const params = new URLSearchParams({ loginName: resp.factors.user.loginName, }); if (authRequestId) { params.append("authRequest", authRequestId); } if (organization) { params.append("organization", organization); } return router.push(`/mfa/set?` + params); } else if ( resp.factors && !resp.factors.passwordless && // if session was not verified with a passkey promptPasswordless && // if explicitly prompted due policy !isAlternative // escaped if password was used as an alternative method ) { const params = new URLSearchParams({ loginName: resp.factors.user.loginName, promptPasswordless: "true", }); if (authRequestId) { params.append("authRequestId", authRequestId); } if (organization) { params.append("organization", organization); } return router.push(`/passkey/add?` + params); } else if (authRequestId && resp && resp.sessionId) { const params = new URLSearchParams({ sessionId: resp.sessionId, authRequest: authRequestId, }); if (organization) { params.append("organization", organization); } return router.push(`/login?` + params); } else { // without OIDC flow const params = new URLSearchParams( authRequestId ? { loginName: resp.factors.user.loginName, authRequestId, } : { loginName: resp.factors.user.loginName, } ); if (organization) { params.append("organization", organization); } return router.push(`/signedin?` + params); } }); } const { errors } = formState; return (
{loginName && ( )}
{error && (
{error}
)}
{/* */}
); }