"use client"; import { coerceToArrayBuffer, coerceToBase64Url } from "@/helpers/base64"; import { completeFlowOrGetUrl } from "@/lib/client"; import { handleServerActionResponse } from "@/lib/client-utils"; import { addU2F, verifyU2F } from "@/lib/server/u2f"; import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb"; import { RegisterU2FResponse } from "@zitadel/proto/zitadel/user/v2/user_service_pb"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { Alert } from "./alert"; import { AutoSubmitForm } from "./auto-submit-form"; import { BackButton } from "./back-button"; import { Button, ButtonVariants } from "./button"; import { Spinner } from "./spinner"; import { Translated } from "./translated"; type Props = { loginName?: string; sessionId: string; requestId?: string; organization?: string; checkAfter: boolean; loginSettings?: LoginSettings; }; export function RegisterU2f({ loginName, sessionId, organization, requestId, checkAfter, loginSettings }: Props) { const [error, setError] = useState(""); const [samlData, setSamlData] = useState<{ url: string; fields: Record } | null>(null); const [loading, setLoading] = useState(false); const router = useRouter(); async function submitVerify(u2fId: string, passkeyName: string, publicKeyCredential: any, sessionId: string) { setError(""); setLoading(true); const response = await verifyU2F({ u2fId, passkeyName, publicKeyCredential, sessionId, }) .catch(() => { setError("An error on verifying passkey occurred"); return; }) .finally(() => { setLoading(false); }); if (response && "error" in response && response?.error) { setError(response?.error); return; } return response; } async function submitRegisterAndContinue(): Promise { setError(""); setLoading(true); const response = await addU2F({ sessionId, }) .catch(() => { setError("An error on registering passkey"); return; }) .finally(() => { setLoading(false); }); if (response && "error" in response && response?.error) { setError(response?.error); return; } if (!response || !("u2fId" in response)) { setError("An error on registering passkey"); return; } const u2fResponse = response as unknown as RegisterU2FResponse; const u2fId = u2fResponse.u2fId; const options: CredentialCreationOptions = (u2fResponse?.publicKeyCredentialCreationOptions as CredentialCreationOptions) ?? {}; if (options.publicKey) { options.publicKey.challenge = coerceToArrayBuffer(options.publicKey.challenge, "challenge"); options.publicKey.user.id = coerceToArrayBuffer(options.publicKey.user.id, "userid"); if (options.publicKey.excludeCredentials) { options.publicKey.excludeCredentials.map((cred: any) => { cred.id = coerceToArrayBuffer(cred.id as string, "excludeCredentials.id"); return cred; }); } const resp = await navigator.credentials.create(options); if ( !resp || !(resp as any).response.attestationObject || !(resp as any).response.clientDataJSON || !(resp as any).rawId ) { setError("An error on registering passkey"); return; } const attestationObject = (resp as any).response.attestationObject; const clientDataJSON = (resp as any).response.clientDataJSON; const rawId = (resp as any).rawId; const data = { id: resp.id, rawId: coerceToBase64Url(rawId, "rawId"), type: resp.type, response: { attestationObject: coerceToBase64Url(attestationObject, "attestationObject"), clientDataJSON: coerceToBase64Url(clientDataJSON, "clientDataJSON"), }, }; const submitResponse = await submitVerify(u2fId, "", data, sessionId); if (!submitResponse) { setError("An error on verifying passkey"); return; } if (checkAfter) { const paramsToContinue = new URLSearchParams({}); if (sessionId) { paramsToContinue.append("sessionId", sessionId); } if (loginName) { paramsToContinue.append("loginName", loginName); } if (organization) { paramsToContinue.append("organization", organization); } if (requestId) { paramsToContinue.append("requestId", requestId); } return router.push(`/u2f?` + paramsToContinue); } else { if (requestId && sessionId) { const callbackResponse = await completeFlowOrGetUrl( { sessionId: sessionId, requestId: requestId, organization: organization, }, loginSettings?.defaultRedirectUri, ); handleServerActionResponse(callbackResponse, router, setSamlData, setError); } else if (loginName) { const callbackResponse = await completeFlowOrGetUrl( { loginName: loginName, organization: organization, }, loginSettings?.defaultRedirectUri, ); handleServerActionResponse(callbackResponse, router, setSamlData, setError); } } } } return ( <> {samlData && }
{error && (
{error}
)}
); }