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
beforeUserCreatedblocking 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-hereIf you don't have Firebase Admin SDK configured yet, you'll also need credentials from Firebase Console → Project Settings → Service Accounts → Generate 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 functionsSelect 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_KEYOr use .env in functions directory:
USER_CLEANING_API_KEY=your-api-key-here4. Deploy
firebase deploy --only functionsIP 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
- Try signing up with a disposable email (e.g.,
test@tempmail.com) - You should see the error message immediately
- Try a legitimate email — signup should proceed normally
- Check logs to confirm validation is working