diff --git a/public/docs.html b/public/docs.html index e46817e..341e149 100644 --- a/public/docs.html +++ b/public/docs.html @@ -83,8 +83,10 @@

Authentication

-

All conversion and template endpoints require an API key. Pass it in the Authorization header:

+

All conversion and template endpoints require an API key. Pass it using either method:

Authorization: Bearer df_free_your_api_key_here
+

Or use the X-API-Key header:

+
X-API-Key: df_free_your_api_key_here

Get a free API key instantly — no credit card required:

curl -X POST https://docfast.dev/v1/signup/free \
   -H "Content-Type: application/json" \
diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts
index 72f5b20..3748b1b 100644
--- a/src/middleware/auth.ts
+++ b/src/middleware/auth.ts
@@ -7,11 +7,19 @@ export function authMiddleware(
   next: NextFunction
 ): void {
   const header = req.headers.authorization;
-  if (!header?.startsWith("Bearer ")) {
-    res.status(401).json({ error: "Missing API key. Use: Authorization: Bearer " });
+  const xApiKey = req.headers["x-api-key"] as string | undefined;
+  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  or X-API-Key: " });
     return;
   }
-  const key = header.slice(7);
   if (!isValidKey(key)) {
     res.status(403).json({ error: "Invalid API key" });
     return;
diff --git a/src/routes/signup.ts b/src/routes/signup.ts
index 50f9f39..1e03f67 100644
--- a/src/routes/signup.ts
+++ b/src/routes/signup.ts
@@ -22,8 +22,21 @@ const verifyLimiter = rateLimit({
   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
-router.post("/free", signupLimiter, async (req: Request, res: Response) => {
+router.post("/free", rejectDuplicateEmail, signupLimiter, async (req: Request, res: Response) => {
   const { email } = req.body || {};
 
   if (!email || typeof email !== "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {