CheckDisposable Emailcheckdisposable.email
← All guidesNextAuth.js (Auth.js) guide · TypeScript

Block disposable email signups with Auth.js / NextAuth

NextAuth's `signIn` callback runs before Auth.js writes the account. Return `false` (or a redirect URL) and the signup is refused.

The code

// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import Google from 'next-auth/providers/google';

async function isDisposable(email: string) {
  try {
    const r = await fetch(
      `https://api.checkdisposable.email/v1/check?email=${encodeURIComponent(email)}`,
      { headers: { Authorization: `Bearer ${process.env.CDE_KEY!}` } }
    );
    if (!r.ok) return false;
    return (await r.json()).is_disposable === true;
  } catch {
    return false;
  }
}

const handler = NextAuth({
  providers: [Google({ clientId: process.env.GOOGLE_ID!, clientSecret: process.env.GOOGLE_SECRET! })],
  callbacks: {
    async signIn({ user }) {
      if (user.email && (await isDisposable(user.email))) {
        // Return a URL to redirect there with an error param,
        // or just `return false` to bounce with a generic message.
        return '/signin?error=DisposableEmail';
      }
      return true;
    },
  },
});

export { handler as GET, handler as POST };

Notes

Where the error surfaces
Returning false sends the user to the default NextAuth error page. Returning a URL gives you control over the message — read the `error` query param on your signin page and render it.
Email provider gotcha
If you use the Email (magic link) provider, signIn fires AFTER the verification email is sent. To block before sending, also call isDisposable in `sendVerificationRequest`.
Account linking
If a user with the same email already exists, NextAuth links the new provider to the existing account. signIn still fires, so disposable check still runs — good.

Get a free API key

500 checks/month, no credit card. No credit card. 30 seconds.

Sign up free →