Email Verification with Supabase
Supabase allows you to intercept authentication events via Auth Hooks. This tutorial creates an Edge Function that calls Mailbeam before Supabase creates a new user, blocking disposable and invalid emails at the identity layer.
Prerequisites
- Supabase project (CLI installed)
- A Mailbeam API key (sign up free)
Create the Edge Function
supabase functions new verify-email// supabase/functions/verify-email/index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
const MAILBEAM_KEY = Deno.env.get("MAILBEAM_KEY")!;
const MIN_SCORE = 60;
serve(async (req) => {
const payload = await req.json();
// Auth Hook payload has user data in payload.user
const email = payload?.user?.email ?? payload?.email;
if (!email) {
return new Response(JSON.stringify({ decision: "reject" }), {
headers: { "Content-Type": "application/json" },
});
}
try {
const response = await fetch("https://api.mailbeam.dev/v1/verify", {
method: "POST",
headers: {
Authorization: `Bearer ${MAILBEAM_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ email }),
signal: AbortSignal.timeout(5000),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const { valid, score, reason } = await response.json();
if (!valid || score < MIN_SCORE) {
const message =
reason === "disposable_domain"
? "Please use a permanent email address."
: "Please provide a valid, reachable email address.";
return new Response(
JSON.stringify({ decision: "reject", message }),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
}
} catch (err) {
// Fail open — don't reject on Mailbeam API errors
console.error("Mailbeam error:", err);
}
return new Response(
JSON.stringify({ decision: "continue" }),
{ status: 200, headers: { "Content-Type": "application/json" } }
);
});Set environment variable
supabase secrets set MAILBEAM_KEY=mb_live_xxxxxxxxxxxxxxxxxxxxDeploy and connect to Auth Hook
supabase functions deploy verify-emailIn the Supabase Dashboard → Authentication → Hooks:
- Enable Before User Creation
- Select your
verify-emailEdge Function
Test locally
supabase start
supabase functions serve verify-email --env-file .env.local
# Test with curl
curl -X POST http://localhost:54321/functions/v1/verify-email \
-H "Content-Type: application/json" \
-d '{"user": {"email": "temp@mailinator.com"}}'
# → {"decision":"reject","message":"Please use a permanent email address."}Best practices
- Always
return { decision: "continue" }in the catch block — fail open - Set
AbortSignal.timeout(5000)to prevent the function from hanging - Log errors to Supabase Function logs for monitoring