Modern authentication platforms like Supabase Auth, Clerk, and Auth.js (NextAuth) make it effortless to ship magic links, OAuth, and OTP registration in a single afternoon. Yet the moment you launch a SaaS product with a generous free tier or starter AI credits, you discover a glaring security gap: none of these auth providers stop sophisticated burner domains or Gmail alias trial farming out of the box.
Whether you are granting $5 in OpenAI / Anthropic tokens, 100 free scraper runs, or a 14-day pro trial without requiring a credit card, bad actors automate account creation using fresh disposable domains and sub-addressed webmail aliases. Left unchecked, fake signups drain your cloud compute budget, pollute your PostgreSQL user tables, and trigger hard bounces that get your transactional email provider (Resend, Postmark, or AWS SES) suspended.
In this implementation guide, we compare what Supabase Auth and Clerk block natively versus what slips through, and provide copy-pasteable production code to block 100,000+ disposable email providers and deduplicate Gmail aliases using MailVeri’s real-time verification API.
1. Why Default Auth Provider Settings Fail Against Modern Trial Abuse
Developers often assume that turning on email confirmation links (OTP) or flipping a basic “Block disposable emails” switch in their auth dashboard solves fake signups. In practice, three attack vectors bypass standard auth configurations completely:
- Ephemeral Burner Domains Receive OTP Codes Instantly: Services like Temp-Mail, Guerrilla Mail, Mohmal, and 10MinuteMail provide live, functional web inboxes. When Supabase or Clerk sends a 6-digit verification code, the user copies the OTP from the temporary inbox in 3 seconds and activates the account.
- Custom Domain Rotation (100,000+ Active Burners): Open-source static blocklists and basic auth toggles only track 3,000 to 5,000 legacy domains. Modern disposable mail networks register hundreds of fresh
.xyz,.top,.site, and.cfddomains every week pointed at wildcard MX clusters. - Gmail Dot & Plus-Tag Multi-Account Farming: Even if you block every disposable domain on earth, an abuser with a single
[email protected]inbox can register[email protected],[email protected], and[email protected]. Standard auth providers treat each string as a brand-new user.
| Protection Layer | Supabase Auth (Default) | Clerk (Built-In Toggle) | Auth Hook + MailVeri API |
|---|---|---|---|
| Known Legacy Burners (e.g. Mailinator) | Allowed unless custom hook added | Blocked (Paid plan add-on) | Blocked (100,000+ domains) |
| Rotating MX & Fresh Burner Domains | Not detected | Slips past static blocklist | Blocked via live MX & threat intel |
| Dead / Typo Domains (Bounce Protection) | Sends OTP → Hard Bounce | Sends OTP → Hard Bounce | Blocked before OTP email is sent |
| Gmail/ProtonMail Dot & Plus Normalization | No (Treated as distinct users) | Partial (+ subaddress toggle only) | Full canonical address returned (mail) |
| Cost per Verification | $0 (No protection) | Requires Clerk Pro + Security Add-on | $0.0002 – $0.0009 (Lifetime credits) |
2. Integration #1: Blocking Disposable Emails in Supabase Auth (Before User Created Hook)
The cleanest place to stop disposable and invalid emails in Supabase is the Before User Created Auth Hook. Because this hook executes synchronously before Supabase inserts a record into auth.users or dispatches a confirmation email via Resend/SendGrid, rejecting a burner email here costs zero transactional email quota and leaves zero junk rows in your database.
Step-by-Step Supabase Setup:
- Create a new Supabase Edge Function:
supabase functions new before-user-created. - Set your MailVeri API key in Supabase Vault/Secrets:
supabase secrets set MAILVERI_API_KEY=your_api_key. - Deploy the Edge Function below and register it in your Supabase Dashboard under Authentication → Hooks (Beta) → Before User Created.
1// supabase/functions/before-user-created/index.ts2// Supabase Auth Hook: Block disposable & invalid emails before creating auth.users row3import { serve } from "https://deno.land/[email protected]/http/server.ts";4 5const MAILVERI_API_KEY = Deno.env.get("MAILVERI_API_KEY")!;6 7serve(async (req: Request) => {8 const payload = await req.json();9 const rawEmail: string | undefined = payload?.user?.email;10 11 if (!rawEmail) {12 return new Response(13 JSON.stringify({14 error: { http_code: 400, message: "Email address is required." },15 }),16 { status: 400, headers: { "Content-Type": "application/json" } }17 );18 }19 20 const verifyRes = await fetch("https://api.mailveri.com/v1/verify-mail/", {21 method: "POST",22 headers: {23 "Authorization": `Bearer ${MAILVERI_API_KEY}`,24 "Content-Type": "application/json",25 },26 body: JSON.stringify({ email: rawEmail }),27 });28 29 const result = await verifyRes.json();30 31 // Reject disposable burner domains, invalid addresses, and over-quota inboxes32 if (33 result.status === "DISPOSABLE" ||34 result.status?.startsWith("INVALID") ||35 result.status === "FULL_INBOX"36 ) {37 return new Response(38 JSON.stringify({39 error: {40 http_code: 403,41 message: "Please sign up with an active work or personal email address. Temporary email providers are not allowed.",42 },43 }),44 { status: 403, headers: { "Content-Type": "application/json" } }45 );46 }47 48 // Allow signup to proceed in Supabase Auth49 return new Response(JSON.stringify({}), {50 status: 200,51 headers: { "Content-Type": "application/json" },52 });53});When a user attempts to sign up with [email protected] or a mistyped address like [email protected], MailVeri returns DISPOSABLE or INVALID-MX, and the Supabase Auth Hook immediately returns a clean 403 error message to your frontend signup form before any confirmation email is ever sent.
3. Integration #2: Blocking Burner Emails & Alias Farming in Clerk + Next.js App Router
If your application uses Clerk with Next.js 15 App Router, you can run a pre-signup Server Action (either in a custom Clerk useSignUp() flow or right before provisioning free-trial credits in your onboarding step) that validates the email with MailVeri and checks the canonical address (data.mail) against your database.
1// app/actions/verify-before-clerk-signup.ts2'use server';3 4export interface SignupPrecheckResult {5 allowed: boolean;6 canonicalEmail?: string;7 error?: string;8}9 10export async function verifyEmailBeforeClerkSignup(11 rawEmail: string12): Promise<SignupPrecheckResult> {13 const response = await fetch('https://api.mailveri.com/v1/verify-mail/', {14 method: 'POST',15 headers: {16 'Authorization': `Bearer ${process.env.MAILVERI_API_KEY}`,17 'Content-Type': 'application/json',18 },19 body: JSON.stringify({ email: rawEmail }),20 cache: 'no-store',21 });22 23 const data = await response.json();24 25 if (data.status === 'DISPOSABLE') {26 return {27 allowed: false,28 error: 'Disposable and temporary email addresses are not permitted on free trials.',29 };30 }31 32 if (data.status?.startsWith('INVALID') || data.status === 'FULL_INBOX') {33 return {34 allowed: false,35 error: 'This email address cannot receive messages. Please check for typos.',36 };37 }38 39 // data.mail returns the canonical normalized email (stripping Gmail dots & +tags)40 // Check your database to ensure this canonical identity hasn't already claimed a trial41 return {42 allowed: true,43 canonicalEmail: data.mail || rawEmail.toLowerCase().trim(),44 };45}How Canonical Email Normalization Stops Multi-Account Trial Abuse
Notice the canonicalEmail: data.mail field in the snippet above. Different mailbox providers handle dots (.) and plus tags (+) differently:
- Gmail & ProtonMail: Ignore dots in the local-part completely and strip everything after
+. Thus[email protected]and[email protected]route to the exact same inbox. MailVeri returns"mail": "[email protected]"for both. - Microsoft Outlook / Hotmail & iCloud: Strip
+sub-addressing tags, but treat dots as distinct accounts. MailVeri preserves significant dots while stripping the+suffix so you never falsely block a legitimate Outlook user.
By saving a UNIQUE index on canonical_email in your PostgreSQL users or trial_grants table, you guarantee that one physical inbox can only claim one free trial—permanently closing the Gmail alias loophole described in our SaaS Disposable Email & Free Trial Protection Guide and SaaS Signup Use Case Overview.
4. Fail-Open Resilience & Zero-Waste Credit Economics
Whenever you place an external API check inside an authentication flow, two engineering rules are mandatory:
- Enforce a 3-Second Timeout with Fail-Open Fallback: Wrap your fetch call in an
AbortSignal.timeout(3000). If a transient network hiccup occurs, allow the signup to proceed and queue an asynchronous background check so legitimate users are never locked out. - Never Pay for Inconclusive Checks: If a corporate mail server temporarily defers verification with a 4xx greylisting code, MailVeri returns
UNKNOWNand charges 0 credits. With pay-as-you-go packages starting at $9 for 10,000 lifetime credits ($59 for 100K, down to $0.0002/check), protecting 10,000 signups costs less than a single abused AI trial account.
Ready to secure your authentication pipeline? Explore our AI Startups & SaaS Solutions, review how burner domains compare to departmental aliases in Disposable vs. Role-Based Emails Explained, or grab your free API key with 1,000 credits to test your Supabase or Clerk hook today.
