AWS Cognito
If you're using AWS Cognito for authentication, you'll appreciate how naturally user.cleaning integrates with your backend signup flow. Rather than letting disposable emails create accounts that clog your user pool, you can validate emails in real-time during the signup flow before users are created in Cognito.
Getting Started
Before integrating user.cleaning with Cognito, you'll need:
- An existing AWS Cognito User Pool with working authentication
- Your user.cleaning API key from the Quickstart guide
There are two integration approaches:
- Backend Signup Handler — Validate emails in your Express/Node backend before calling Cognito Admin APIs
- Serverless Lambda Trigger — Use Cognito's Pre Sign-up trigger for client-side signups (Amplify, Hosted UI)
Backend Integration
This approach validates emails in your backend signup handler before calling Cognito's user creation APIs. Disposable emails are blocked immediately - no fake accounts, no cleanup needed.
IP Forwarding for Rate Limiting
Your backend needs to extract and forward the real client IP address for user.cleaning's IP-based rate limiting and fraud detection to work properly.
Extract the client IP from your request:
function getClientIP(req) {
return req.headers['x-forwarded-for']?.split(',')[0].trim()
|| req.headers['x-real-ip']
|| req.socket.remoteAddress
|| 'unknown';
}Production setup: If you're behind nginx, Cloudflare, or a load balancer, they automatically set the X-Forwarded-For header with the real client IP. If using Docker, you'll need an nginx reverse proxy to preserve client IPs.
Without proper IP forwarding: All requests will appear to come from the same IP (your server's IP), and after 5 disposable email attempts, all registrations will be blocked until the IP is unbanned at API Settings or Color Configuration.
Integration with Your Signup Flow
Add email validation to your existing Cognito signup handler. The validation happens before any Cognito API calls, so disposable emails never reach your user pool.
Here's how to integrate it:
// Extract client IP from request
function getClientIP(req) {
return req.headers['x-forwarded-for']?.split(',')[0].trim()
|| req.headers['x-real-ip']
|| req.socket.remoteAddress
|| 'unknown';
}
// Your existing signup handler with validation added
async function handleSignup(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) {
console.warn('Email validation service unavailable, allowing signup');
// Fail open - allow signup if service is down
} else {
const validation = await response.json();
if (validation.category === 'black') {
return res.status(400).json({
error: 'Disposable email addresses are not allowed.'
});
}
// Optional: also block grey emails (aliases, plus-addressing)
// if (validation.category === 'grey') {
// return res.status(400).json({
// error: 'Please use your primary email address.'
// });
// }
}
// Your existing Cognito signup code continues here
// This could be AdminCreateUser, or signUp from cognito-identity-js, etc.
await yourExistingCognitoSignup(email, password);
res.json({ success: true, message: 'Account created successfully!' });
} catch (error) {
console.error('Signup error:', error);
res.status(500).json({ error: 'Signup failed. Please try again.' });
}
}Environment Configuration
Add your API key to your environment variables:
USER_CLEANING_API_KEY=your-api-key-hereNever commit your API key to version control. Use environment variables or secret management services in production.
Serverless Integration
If you're using Cognito's client-side signup (Amplify, Hosted UI), you can validate emails with a Pre Sign-up Lambda trigger instead of a backend server.
1. Create the Lambda Function
Create a new Lambda function in AWS Console with Node.js 20.x runtime:
// index.mjs
export const handler = async (event) => {
const email = event.request.userAttributes.email;
const clientIP = event.request.clientMetadata?.clientIP || 'unknown';
console.log(`Pre-signup validation for: ${email} from IP: ${clientIP}`);
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 Error('Too many attempts. Please try again later.');
}
if (response.ok) {
const result = await response.json();
if (result.category === 'black') {
throw new Error('Disposable email addresses are not allowed.');
}
}
// If API is down, fail open (allow signup)
} catch (error) {
if (error.message.includes('Disposable') || error.message.includes('Too many')) {
throw error; // This rejects the signup
}
// Network errors - fail open
console.warn('Validation service error, allowing signup:', error.message);
}
return event; // Allow signup to proceed
};2. Configure the Lambda
In AWS Lambda Console:
- Environment variables: Add
USER_CLEANING_API_KEYwith your API key - Timeout: Set to 10 seconds
- Memory: 128 MB is sufficient
3. Attach to Cognito User Pool
Via AWS Console:
- Go to Cognito → User Pools → Your Pool
- Click User pool properties tab
- Scroll to Lambda triggers → Click Add Lambda trigger
- Select Sign-up → Pre sign-up trigger
- Choose your Lambda function → Save
Via AWS CLI:
aws cognito-idp update-user-pool \
--user-pool-id YOUR_POOL_ID \
--lambda-config PreSignUp=arn:aws:lambda:REGION:ACCOUNT:function:FUNCTION_NAME4. Pass Client IP from Frontend
Lambda triggers don't have access to the original HTTP request headers. Pass the client IP via clientMetadata when calling signUp:
// Amplify v6
import { signUp } from 'aws-amplify/auth';
// Get client IP first
const ipResponse = await fetch('https://api.ipify.org?format=json');
const { ip } = await ipResponse.json();
// Signup with IP in metadata
await signUp({
username: email,
password: password,
options: {
userAttributes: { email },
clientMetadata: { clientIP: ip }
}
});Note: This uses a third-party service (ipify) to fetch the client's public IP. Alternatively, you can create your own IP endpoint with API Gateway + Lambda, or skip IP forwarding entirely (rate limiting will be less precise but validation still works).
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) or email forwarding services. By default these are allowed, but you can uncomment the grey validation block in the code above to block them or require additional verification.
White (Allow): Legitimate email addresses that pass validation. These are from trusted domains like gmail.com, outlook.com, etc.
Grey Email Handling
By default, the code allows grey emails (aliases like user+tag@gmail.com or forwarding services). If you want to require additional verification for these, or block them entirely, uncomment the grey email check in the code:
// Block grey emails entirely:
if (validation.category === 'grey') {
return res.status(400).json({
error: 'Please use your primary email address.'
});
}Error Messages
Customize the error messages to match your application's tone and user experience:
if (validation.category === 'black') {
return res.status(400).json({
error: 'Please use a permanent email address to create an account.'
});
}
if (validation.category === 'grey') {
return res.status(400).json({
error: 'Email aliases are not supported. Please use your primary email.'
});
}Error Handling
The code above implements graceful error handling:
- 403 (Rate Limited): Returns user-friendly message about too many attempts
- Service Unavailable: Logs warning and allows signup to proceed (fail open)
- Validation Failed: Returns appropriate error message to user
For more details on email categories and what each means, see the Categories Reference.