api.
Real-time VerificationAuth providers

Firebase

If you're using Firebase for authentication, you'll appreciate how naturally user.cleaning integrates with your signup flow. Validate emails before accounts are created, keeping your user base clean from day one.

Getting Started

Before integrating user.cleaning with Firebase, you'll need:

  • A Firebase project with Authentication enabled
  • Your user.cleaning API key from the Quickstart guide

There are two integration approaches:

  • Backend Integration — Validate emails in your Express/Node backend using Firebase Admin SDK
  • Serverless Integration — Use Firebase Cloud Functions beforeUserCreated blocking trigger

Backend Integration

This approach uses Firebase Admin SDK to create users server-side after validating emails. Best for apps that already have a backend.

Environment Setup

Add your user.cleaning API key to your environment:

USER_CLEANING_API_KEY=your-api-key-here

If you don't have Firebase Admin SDK configured yet, you'll also need credentials from Firebase ConsoleProject SettingsService AccountsGenerate new private key:

FIREBASE_PROJECT_ID=your-project-id
FIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxxxx@your-project-id.iam.gserviceaccount.com
FIREBASE_PRIVATE_KEY="-----YOUR PRIVATE KEY-----"

Integration Code

Assuming you have Firebase Admin SDK initialized, add a helper function to extract the real client IP from incoming requests:

function getClientIP(req) {
  return req.headers['x-forwarded-for']?.split(',')[0].trim()
    || req.headers['x-real-ip']
    || req.socket.remoteAddress
    || 'unknown';
}

Now add email validation to your signup endpoint. The key is to validate the email before calling admin.auth().createUser():

app.post('/signup', async (req, res) => {
  const { email, password } = req.body;
  const clientIP = getClientIP(req);

  console.log(`[${clientIP}] Signup attempt: ${email}`);

  try {
    // Validate email with user.cleaning
    const response = await fetch(
      `https://api.user.cleaning/v1/external-api-requests/check-email?email=${encodeURIComponent(email)}`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': process.env.USER_CLEANING_API_KEY,
          'X-Forwarded-For': clientIP,
          'X-Real-IP': clientIP,
        },
      }
    );

    if (response.status === 403) {
      return res.status(400).json({
        error: 'Too many disposable email attempts. Please try again later.',
      });
    }

    if (response.ok) {
      const result = await response.json();
      
      if (result.category === 'black') {
        return res.status(400).json({
          error: 'Disposable email addresses are not allowed.',
        });
      }

      // Optional: block grey emails (aliases, plus-addressing)
      // if (result.category === 'grey') {
      //   return res.status(400).json({
      //     error: 'Please use your primary email address.',
      //   });
      // }
    } else {
      console.warn('Email validation service returned', response.status);
      // Fail open - allow signup if service is down
    }

    // Only create user if validation passed
    const userRecord = await admin.auth().createUser({
      email,
      password,
      emailVerified: false,
    });

    res.status(201).json({
      success: true,
      user: { uid: userRecord.uid, email: userRecord.email },
    });

  } catch (error) {
    console.error('Signup error:', error);

    if (error.code === 'auth/email-already-exists') {
      return res.status(400).json({ error: 'Email already registered' });
    }

    res.status(500).json({ error: 'Signup failed. Please try again.' });
  }
});

IP Forwarding

For user.cleaning's rate limiting to work correctly, your application must forward real client IPs via X-Forwarded-For headers.

Production: If you're behind nginx, Cloudflare, or a load balancer, this is already configured automatically.

Development/testing: Without a reverse proxy forwarding X-Forwarded-For, the integration will see your server's IP instead of actual client IPs. This means all requests appear to come from the same IP, and after 5 disposable email attempts, all registrations will be blocked until the IP is unbanned.

If you see container IPs in your logs or experience unexpected registration blocks, verify your reverse proxy is forwarding client IPs correctly. You can unban blocked IPs at API Settings and configure number of registration attempts before block or disable it completely at Color Configuration.

Serverless Integration

If you're using Firebase Client SDK for authentication (no backend), you can validate emails with a beforeUserCreated Cloud Function blocking trigger.

Requirements:

  • Firebase Blaze plan (pay-as-you-go) — blocking triggers require Blaze
  • Firebase CLI installed: npm install -g firebase-tools

1. Initialize Cloud Functions

firebase login
firebase init functions

Select JavaScript or TypeScript, install dependencies.

2. Create the Blocking Function

// functions/index.js
import { beforeUserCreated } from 'firebase-functions/v2/identity';
import { HttpsError } from 'firebase-functions/v2/https';

export const validateSignup = beforeUserCreated(async (event) => {
  const email = event.data.email;
  const clientIP = event.ipAddress || 'unknown';

  console.log(`[${clientIP}] Validating email: ${email}`);

  try {
    const response = await fetch(
      `https://api.user.cleaning/v1/external-api-requests/check-email?email=${encodeURIComponent(email)}`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': process.env.USER_CLEANING_API_KEY,
          'X-Forwarded-For': clientIP,
          'X-Real-IP': clientIP,
        },
      }
    );

    if (response.status === 403) {
      throw new HttpsError(
        'resource-exhausted',
        'Too many disposable email attempts. Please try again later.'
      );
    }

    if (response.ok) {
      const result = await response.json();
      console.log('Validation result:', result);

      if (result.category === 'black') {
        throw new HttpsError(
          'invalid-argument',
          'Disposable email addresses are not allowed.'
        );
      }

      // Optional: block grey emails
      // if (result.category === 'grey') {
      //   throw new HttpsError(
      //     'invalid-argument',
      //     'Please use your primary email address.'
      //   );
      // }
    }
    // Service unavailable - fail open, allow signup
  } catch (error) {
    if (error instanceof HttpsError) throw error;
    console.warn('Email validation skipped:', error.message);
  }
});

3. Configure Environment

Set your API key in Firebase:

firebase functions:secrets:set USER_CLEANING_API_KEY

Or use .env in functions directory:

USER_CLEANING_API_KEY=your-api-key-here

4. Deploy

firebase deploy --only functions

IP Limitations in Serverless

Firebase's beforeUserCreated trigger has limited access to client IP. The event.ipAddress field may not always contain the real client IP.

For more accurate IP-based rate limiting, consider:

  • Using the backend integration approach
  • Passing IP via custom claims from your frontend (requires additional setup)

Understanding Validation Results

The API categorizes emails into three groups. See the Categories Reference for detailed explanations.

Black (Block): Disposable email domains that should never be allowed. These are temporary email services like tempmail.com, guerrillamail.com, etc.

Grey (Caution): Suspicious patterns like aliases or plus-addressing (user+tag@gmail.com). By default allowed, but you can uncomment the grey validation block to block them.

White (Allow): Legitimate email addresses that pass validation.

Error Handling

Both integration approaches use a "fail open" strategy — if user.cleaning is unavailable, signups proceed normally. This ensures your authentication flow isn't blocked by external service issues.

  • 403 (Rate Limited): Returns user-friendly message about too many attempts
  • Service Unavailable: Logs warning and allows signup to proceed
  • Validation Failed: Returns appropriate error message to user

Testing Your Integration

  1. Try signing up with a disposable email (e.g., test@tempmail.com)
  2. You should see the error message immediately
  3. Try a legitimate email — signup should proceed normally
  4. Check logs to confirm validation is working

On this page