@chilly-king-10285 this is my code for login with phone and otp can you add new button for login with passkey with functionality , please update the code : "use client";
import { useState, useEffect, useCallback } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import ory from "../../../pkg/sdk";
import { enqueueSnackbar } from "notistack";
export default function Login() {
const [email, setEmail] = useState("");
const [otp, setOtp] = useState("");
const [flow, setFlow] = useState(null);
const [passkeyData, setPasskeyData] = useState(null);
const [step, setStep] = useState("email");
const router = useRouter();
const params = useSearchParams();
const initializeFlow = async () => {
try {
const { data } = await ory.createBrowserLoginFlow({
refresh: false,
aal: "aal1",
returnTo: "/",
});
setFlow(data);
} catch (error) {
console.error("Error initializing login flow:", error);
}
};
useEffect(() => {
const flowId = params.get("flow");
if (flowId) {
ory
.getLoginFlow({ id: flowId })
.then(({ data }) => {
setFlow(data);
})
.catch((err) => {
console.error(err);
initializeFlow();
});
} else {
initializeFlow();
}
}, [router, params]);
const handleEmailSubmit = async (e) => {
e.preventDefault();
if (!flow) return;
try {
const { data } = await ory.updateLoginFlow({
flow: flow.id,
updateLoginFlowBody: {
method: "code",
identifier: email,
csrf_token: flow.ui.nodes.find(
(node) => node.attributes.name === "csrf_token",
)?.attributes.value,
},
});
setFlow(data);
setStep("otp");
} catch (error) {
if (error?.response?.data?.state === "sent_email") {
enqueueSnackbar("OTP sent to your email", { variant: "success" });
setStep("otp");
} else {
enqueueSnackbar("Something went wrong", { variant: "error" });
}
}
};
const handleOtpSubmit = async (e) => {
e.preventDefault();
if (!flow) return;
try {
const { data } = await ory.updateLoginFlow({
flow: flow.id,
updateLoginFlowBody: {
method: "code",
code: otp,
identifier: email,
csrf_token: flow.ui.nodes.find(
(node) => node.attributes.name === "csrf_token",
)?.attributes.value,
},
});
if (data.session) {
router.push("/auth");
} else if (data.continue_with) {
setStep("passkey");
} else {
console.error("OTP verification failed:", data);
}
} catch (error) {
console.error("Error verifying OTP:", error);
}
};
const handlePasskeyAuthentication = useCallback(async () => {
if (!flow || !passkeyData) return;
try {
const credential = await navigator.credentials.get({
publicKey: passkeyData.publicKey,
});
const { data } = await ory.updateLoginFlow({
flow: flow.id,
updateLoginFlowBody: {
method: "passkey",
identifier: email,
passkey: JSON.stringify(credential),
csrf_token: flow.ui.nodes.find(
(node) => node.attributes.name === "csrf_token",
)?.attributes.value,
},
});
if (data.session) {
enqueueSnackbar("Login successful", { variant: "success" });
router.push("/auth");
} else {
enqueueSnackbar("Passkey authentication failed", { variant: "error" });
}
} catch (error) {
console.error("Error during passkey authentication:", error);
enqueueSnackbar("Passkey authentication failed", { variant: "error" });
}
}, [flow, passkeyData, email, router]);
useEffect(() => {
if (step === "passkey" && flow?.continue_with) {
const passkeyChallenge = flow.continue_with.find(
(item) => item.action === "show_passkey",
);
if (passkeyChallenge) {
setPasskeyData(passkeyChallenge.passkey);
}
}
}, [step, flow]);
if (!flow) {
return <div>Loading...</div>;
}
return (
<div
style={{
maxWidth: "500px",
margin: "0 auto",
padding: "20px",
border: "1px solid #ccc",
borderRadius: "8px",
boxShadow: "0 0 10px rgba(0,0,0,0.1)",
}}
>
{step === "email" && (
<form
onSubmit={handleEmailSubmit}
style={{ display: "flex", flexDirection: "column", gap: "10px" }}
>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter your email"
required
style={{
padding: "10px",
borderRadius: "4px",
border: "1px solid #ccc",
}}
/>
<input
type="hidden"
name="csrf_token"
value={
flow?.ui.nodes.find(
(node) => node.attributes.name === "csrf_token",
)?.attributes.value
}
/>
<button
type="submit"
style={{
padding: "10px",
borderRadius: "4px",
border: "none",
backgroundColor: "#007BFF",
color: "#fff",
cursor: "pointer",
}}
>
Send OTP
</button>
</form>
)}
{step === "otp" && (
<form
onSubmit={handleOtpSubmit}
style={{ display: "flex", flexDirection: "column", gap: "10px" }}
>
<input
type="text"
value={otp}
onChange={(e) => setOtp(e.target.value)}
placeholder="Enter OTP"
required
style={{
padding: "10px",
borderRadius: "4px",
border: "1px solid #ccc",
}}
/>
<input
type="hidden"
name="csrf_token"
value={
flow?.ui.nodes.find(
(node) => node.attributes.name === "csrf_token",
)?.attributes.value
}
/>
<button
type="submit"
style={{
padding: "10px",
borderRadius: "4px",
border: "none",
backgroundColor: "#007BFF",
color: "#fff",
cursor: "pointer",
}}
>
Verify OTP
</button>
</form>
)}
{step === "passkey" && (
<div style={{ textAlign: "center" }}>
<h2>Passkey Authentication</h2>
<p>Please use your passkey to complete the login process.</p>
<button
onClick={handlePasskeyAuthentication}
style={{
padding: "10px",
borderRadius: "4px",
border: "none",
backgroundColor: "#007BFF",
color: "#fff",
cursor: "pointer",
}}
>
Continue with Passkey
</button>
</div>
)}
</div>
);
}