Middleware
// veqen.js
export function screenSignup({ field = "email" } = {}) {
return async (req, res, next) => {
try {
const response = await fetch("https://veqen-app.com/v1/signup/check", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.VEQEN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ email: req.body[field], ip: req.ip, phone: req.body.phone }),
signal: AbortSignal.timeout(4000),
});
req.veqen = response.ok ? await response.json() : null;
} catch {
req.veqen = null; // fail open
}
const verdict = req.veqen?.risk.verdict ?? "review";
if (verdict === "refuse") {
return res.status(400).json({ error: "Please use a permanent email address." });
}
req.needsVerification = verdict === "review";
next();
};
}
Wire it up
import express from "express";
import { screenSignup } from "./veqen.js";
const app = express();
app.set("trust proxy", 1); // behind one proxy/load balancer: req.ip is the visitor
app.use(express.json());
app.post("/signup", screenSignup(), async (req, res) => {
const user = await createUser({
email: req.body.email,
canonicalEmail: req.veqen?.email?.canonical,
verified: !req.needsVerification,
});
res.status(201).json({ id: user.id, verifyEmail: req.needsVerification });
});
trust proxy matters: without it, req.ip is your load balancer's address, and every signup looks like it comes from a datacenter.