Start an OTP login
Send a one-time password to a phone number:Copy
Ask AI
await client.startOTPLogin({
identifier: {
type: "phone_number",
value: "+14155551234",
},
});
Check the OTP code
Verify the code entered by the user:Copy
Ask AI
import { PrldErrors } from "@prelude.so/js-sdk";
try {
await client.checkOTP("123456");
// User is now authenticated
} catch (error) {
if (error instanceof PrldErrors.BadCheckCode) {
// Wrong code entered
} else if (error instanceof PrldErrors.Unauthorized) {
// Verification expired or invalid
} else if (error instanceof PrldErrors.BadRequest) {
// Invalid request
} else if (error instanceof PrldErrors.RateLimited) {
// Too many attempts
}
}
Retry sending the OTP
If the user didn’t receive the code, retry sending it:Copy
Ask AI
await client.retryOTP();
Try it
Try it
Replace
src/App.jsx with:src/App.jsx
Copy
Ask AI
import "@picocss/pico";
import { useState } from "react";
import { PrldSessionClient, PrldErrors, decode } from "@prelude.so/js-sdk";
const client = new PrldSessionClient({ domain: `${import.meta.env.VITE_APP_ID}.session.prelude.dev` });
export default function App() {
const [phone, setPhone] = useState("");
const [code, setCode] = useState("");
const [step, setStep] = useState("phone"); // "phone" | "code" | "done"
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
const handleSendOTP = async (e) => {
e.preventDefault();
setError(null);
try {
await client.startOTPLogin({
identifier: { type: "phone_number", value: phone },
});
setStep("code");
} catch (err) {
if (err instanceof PrldErrors.BadRequest) {
setError("Invalid phone number.");
} else {
setError("Something went wrong. Please try again.");
}
}
};
const handleCheckOTP = async (e) => {
e.preventDefault();
setError(null);
try {
await client.checkOTP(code);
const { user } = await client.refresh();
setUser(user);
setStep("done");
} catch (err) {
if (err instanceof PrldErrors.BadCheckCode) {
setError("Wrong code. Please try again.");
} else if (err instanceof PrldErrors.Unauthorized) {
setError("Verification expired. Please start over.");
} else if (err instanceof PrldErrors.RateLimited) {
setError("Too many attempts. Please try again later.");
} else {
setError("Something went wrong. Please try again.");
}
}
};
const handleRetry = async () => {
setError(null);
try {
await client.retryOTP();
} catch (err) {
setError("Could not resend code. Please try again.");
}
};
return (
<>
<style>{`body, #root { display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; padding: 0; }`}</style>
<main style={{ textAlign: "center", maxWidth: "600px", width: "100%" }}>
<h1>OTP Login</h1>
{step === "done" && user ? (
<article>
<p>Logged in</p>
<pre style={{ textAlign: "left", whiteSpace: "pre-wrap", wordBreak: "break-all" }}>
{JSON.stringify(decode(user.accessToken).claims, null, 2)}
</pre>
</article>
) : step === "code" ? (
<form onSubmit={handleCheckOTP}>
<input type="text" placeholder="Enter code" value={code} onChange={(e) => setCode(e.target.value)} required />
{error && <p role="alert" style={{ color: "var(--pico-color-red-500)" }}>{error}</p>}
<button type="submit">Verify Code</button>
<button type="button" className="secondary" onClick={handleRetry}>Resend Code</button>
</form>
) : (
<form onSubmit={handleSendOTP}>
<input type="tel" placeholder="Phone number (e.g. +14155551234)" value={phone} onChange={(e) => setPhone(e.target.value)} required />
{error && <p role="alert" style={{ color: "var(--pico-color-red-500)" }}>{error}</p>}
<button type="submit">Send Code</button>
</form>
)}
</main>
</>
);
}