Verifica delle email con Clerk
Clerk supporta i webhook per gli eventi del ciclo di vita dell'utente, compreso user.created. Clerk non ha un blocco nativo precedente alla creazione, ma puoi combinare un webhook con i metadati utente di Clerk per marcare le registrazioni non valide e cancellarle subito, oppure usare il modello della redirezione precedente alla registrazione.
Questo tutorial usa il modello consigliato: verificare l'email in un endpoint sul server prima di chiamare signUp.create di Clerk.
Prerequisiti
- Un account Clerk e un progetto Next.js con
@clerk/nextjs - Una chiave API di Mailbeam (iscriviti gratis)
Preparazione
npm install svix # svix è la libreria dei webhook di ClerkEndpoint di verifica sul server
// app/api/verify-email/route.ts
import { NextResponse } from "next/server";
// Non ci sono ancora SDK: questo è il wrapper di 12 righe di /docs/quickstart.
import { verifyEmail } from "./lib/mailbeam";
export async function POST(req: Request) {
const { email } = await req.json();
if (!email) {
return NextResponse.json({ error: "email mancante" }, { status: 400 });
}
try {
const { valid, score, reason } = await verifyEmail(email);
return NextResponse.json({ valid, score, reason });
} catch {
// Lascia passare
return NextResponse.json({ valid: true, score: 50, reason: null });
}
}Modulo di registrazione tuo
// app/signup/CustomSignupForm.tsx
"use client";
import { useSignUp } from "@clerk/nextjs";
import { useState } from "react";
export function CustomSignupForm() {
const { signUp, setActive } = useSignUp();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
setError(null);
// Passo 1: verifica l'email sul server prima di chiamare Clerk
const verifyRes = await fetch("/api/verify-email", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});
const { valid, score, reason } = await verifyRes.json();
if (!valid || score < 60) {
setError(
reason === "disposable_domain"
? "Usa un indirizzo email permanente."
: "Inserisci un indirizzo email valido."
);
setLoading(false);
return;
}
// Passo 2: prosegui con la registrazione in Clerk
try {
const result = await signUp!.create({ emailAddress: email, password });
if (result.status === "complete") {
await setActive!({ session: result.createdSessionId });
window.location.href = "/dashboard";
}
} catch (err: unknown) {
setError((err as { errors?: { message: string }[] })?.errors?.[0]?.message ?? "La registrazione non è riuscita.");
} finally {
setLoading(false);
}
}
return (
<form onSubmit={handleSubmit} className="space-y-4 max-w-sm">
{error && (
<p role="alert" className="rounded-lg bg-destructive/10 p-3 text-sm text-destructive">
{error}
</p>
)}
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Indirizzo email"
className="w-full rounded-lg border border-border px-3 py-2 text-sm bg-background"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
className="w-full rounded-lg border border-border px-3 py-2 text-sm bg-background"
required
/>
<button
type="submit"
disabled={loading}
className="w-full rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground disabled:opacity-60"
>
{loading ? "Creazione dell'account…" : "Crea account"}
</button>
</form>
);
}Buone pratiche
- Verifica prima di chiamare Clerk, non dopo: eviti utenti orfani non verificati
- Tieni leggero l'endpoint di verifica: deve solo chiamare Mailbeam e restituire
- Usa
reasonper dare messaggi di errore concreti