fix: detect auth failure by response URL not status code to fix rate limiter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
fuomag9
2026-02-25 18:38:25 +01:00
parent 618982484c
commit cb3c0a1536

View File

@@ -56,17 +56,42 @@ export async function POST(request: NextRequest) {
const response = await handlers.POST(request);
if (response.status >= 200 && response.status < 300) {
resetAttempts(rateLimitKey);
return response;
}
// Determine success/failure by inspecting redirect destination, not status code.
// Auth.js returns 302 (direct form) or 200+JSON (X-Auth-Return-Redirect) on both
// success and failure — the error is signaled by the destination URL containing "error=".
const isFailure = await isAuthFailureResponse(response);
if (response.status === 401) {
if (isFailure) {
const result = registerFailedAttempt(rateLimitKey);
if (result.blocked) {
return buildBlockedResponse(result.retryAfterMs);
}
} else {
resetAttempts(rateLimitKey);
}
return response;
}
async function isAuthFailureResponse(response: Response): Promise<boolean> {
// Redirect case: Auth.js sets Location header
const location = response.headers.get("location");
if (location) {
return location.includes("error=");
}
// JSON case (X-Auth-Return-Redirect: 1): body is {"url": "..."}
const contentType = response.headers.get("content-type") ?? "";
if (response.status === 200 && contentType.includes("application/json")) {
try {
const cloned = response.clone();
const body = await cloned.json() as { url?: string };
if (typeof body.url === "string") {
return body.url.includes("error=");
}
} catch {
// ignore parse errors
}
}
// Any 4xx/5xx is a failure
return response.status >= 400;
}