fix: BUG-022 check duplicate email before rate limit, BUG-024 support X-API-Key header

This commit is contained in:
OpenClaw 2026-02-14 18:41:46 +00:00
parent f59b99203e
commit a67c16cd0f
3 changed files with 28 additions and 5 deletions

View file

@ -83,8 +83,10 @@
<section id="auth"> <section id="auth">
<h2>Authentication</h2> <h2>Authentication</h2>
<p>All conversion and template endpoints require an API key. Pass it in the <code>Authorization</code> header:</p> <p>All conversion and template endpoints require an API key. Pass it using either method:</p>
<pre>Authorization: Bearer df_free_your_api_key_here</pre> <pre>Authorization: Bearer df_free_your_api_key_here</pre>
<p>Or use the <code>X-API-Key</code> header:</p>
<pre>X-API-Key: df_free_your_api_key_here</pre>
<p style="margin-top:0.75rem">Get a free API key instantly — no credit card required:</p> <p style="margin-top:0.75rem">Get a free API key instantly — no credit card required:</p>
<pre>curl -X POST https://docfast.dev/v1/signup/free \ <pre>curl -X POST https://docfast.dev/v1/signup/free \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \

View file

@ -7,11 +7,19 @@ export function authMiddleware(
next: NextFunction next: NextFunction
): void { ): void {
const header = req.headers.authorization; const header = req.headers.authorization;
if (!header?.startsWith("Bearer ")) { const xApiKey = req.headers["x-api-key"] as string | undefined;
res.status(401).json({ error: "Missing API key. Use: Authorization: Bearer <key>" }); let key: string | undefined;
if (header?.startsWith("Bearer ")) {
key = header.slice(7);
} else if (xApiKey) {
key = xApiKey;
}
if (!key) {
res.status(401).json({ error: "Missing API key. Use: Authorization: Bearer <key> or X-API-Key: <key>" });
return; return;
} }
const key = header.slice(7);
if (!isValidKey(key)) { if (!isValidKey(key)) {
res.status(403).json({ error: "Invalid API key" }); res.status(403).json({ error: "Invalid API key" });
return; return;

View file

@ -22,8 +22,21 @@ const verifyLimiter = rateLimit({
legacyHeaders: false, legacyHeaders: false,
}); });
// Pre-check: reject already-registered emails BEFORE rate limiting (BUG-022)
function rejectDuplicateEmail(req: Request, res: Response, next: Function) {
const { email } = req.body || {};
if (email && typeof email === "string") {
const cleanEmail = email.trim().toLowerCase();
if (isEmailVerified(cleanEmail)) {
res.status(409).json({ error: "Email already registered" });
return;
}
}
next();
}
// Step 1: Request signup — generates 6-digit code // Step 1: Request signup — generates 6-digit code
router.post("/free", signupLimiter, async (req: Request, res: Response) => { router.post("/free", rejectDuplicateEmail, signupLimiter, async (req: Request, res: Response) => {
const { email } = req.body || {}; const { email } = req.body || {};
if (!email || typeof email !== "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) { if (!email || typeof email !== "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {