Procházet zdrojové kódy

added rate limit for auth pages (#11)

Bernt Christian Egeland před 7 měsíci
rodič
revize
eca17e518c

+ 5 - 1
src/app/(public)/auth/sign-in/sign-in-form.tsx

@@ -25,7 +25,11 @@ function SignInFormInner({ registrationDisabled }: { registrationDisabled: boole
     try {
       const result = await signIn.email({ email, password })
       if (result.error) {
-        setError(result.error.message || 'Invalid email or password')
+        if (result.error.status === 429) {
+          setError('Too many attempts, try again later.')
+        } else {
+          setError(result.error.message || 'Invalid email or password')
+        }
       } else {
         const redirect = searchParams.get('redirect') || '/'
         router.push(redirect)

+ 26 - 1
src/app/api/auth/[...all]/route.ts

@@ -1,4 +1,29 @@
 import { auth } from "@/lib/auth";
+import { rateLimit } from "@/lib/rate-limit";
 import { toNextJsHandler } from "better-auth/next-js";
 
-export const { POST, GET } = toNextJsHandler(auth);
+const { POST: authPOST, GET } = toNextJsHandler(auth);
+
+// Path prefixes that need stricter rate limits.
+// Better-auth registers sub-paths like /sign-in/email, /sign-up/email,
+// /two-factor/verify-totp, etc., so we match by prefix.
+const strictPrefixes: { prefix: string; limit: number; windowMs: number }[] = [
+  { prefix: "/api/auth/sign-in", limit: 10, windowMs: 60_000 },
+  { prefix: "/api/auth/two-factor/verify", limit: 10, windowMs: 60_000 },
+  { prefix: "/api/auth/sign-up", limit: 5, windowMs: 60_000 },
+  { prefix: "/api/auth/request-password-reset", limit: 5, windowMs: 60_000 },
+  { prefix: "/api/auth/reset-password", limit: 5, windowMs: 60_000 },
+];
+
+const defaultConfig = { limit: 30, windowMs: 60_000 };
+
+async function POST(request: Request) {
+  const { pathname } = new URL(request.url);
+  const config =
+    strictPrefixes.find((p) => pathname.startsWith(p.prefix)) ?? defaultConfig;
+  const limited = rateLimit(request, config);
+  if (limited) return limited;
+  return authPOST(request);
+}
+
+export { GET, POST };