Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions src/app/api/auth/[...nextauth]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,65 @@ import { NextRequest, NextResponse } from "next/server";
const handler = NextAuth(authOptions);

const rateLimitMap = new Map<string, { count: number; resetAt: number }>();

const RATE_LIMIT_MAX = 10;
const RATE_LIMIT_WINDOW_MS = 60 * 1000;

type AuthRouteContext = {
params: Promise<{ nextauth: string[] }>;
};

function getClientIP(req: NextRequest): string {
return (
req.headers.get("x-forwarded-for")?.split(",")[0].trim() ??
req.headers.get("x-real-ip") ??
req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
req.headers.get("x-real-ip") ||
"unknown"
);
}

function isRateLimited(ip: string): boolean {
const now = Date.now();

const entry = rateLimitMap.get(ip);

if (!entry || now > entry.resetAt) {
rateLimitMap.set(ip, { count: 1, resetAt: now + RATE_LIMIT_WINDOW_MS });
rateLimitMap.set(ip, {
count: 1,
resetAt: now + RATE_LIMIT_WINDOW_MS,
});

return false;
}
if (entry.count >= RATE_LIMIT_MAX) return true;
entry.count++;

if (entry.count >= RATE_LIMIT_MAX) {
return true;
}

entry.count += 1;
return false;
}

export async function GET(req: NextRequest, ctx: { params: Promise<{ nextauth: string[] }> }) {
export async function GET(req: NextRequest, ctx: AuthRouteContext) {
const params = await ctx.params;
return handler(req, { params });
}

export async function POST(req: NextRequest, ctx: { params: Promise<{ nextauth: string[] }> }) {
const ip = getClientIP(req);
if (isRateLimited(ip)) {
return NextResponse.redirect(
new URL("/auth/signin?error=RateLimitError", req.url)
);
}
export async function POST(req: NextRequest, ctx: AuthRouteContext) {
const params = await ctx.params;
const action = params.nextauth?.[0];

const shouldRateLimit = action === "signin" || action === "callback";

if (shouldRateLimit) {
const ip = getClientIP(req);

if (isRateLimited(ip)) {
return NextResponse.redirect(
new URL("/auth/signin?error=RateLimitError", req.url),
{ status: 303 }
);
}
}

return handler(req, { params });
}
}
Loading