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)) {