mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-20 20:26:55 -05:00
fix: separate rate limit request and response handling (#7939)
This commit is contained in:
@@ -12,7 +12,7 @@ import { createRouter } from "better-call";
|
||||
import type { UnionToIntersection } from "../types";
|
||||
import { isAPIError } from "../utils/is-api-error";
|
||||
import { originCheckMiddleware } from "./middlewares";
|
||||
import { onRequestRateLimit } from "./rate-limiter";
|
||||
import { onRequestRateLimit, onResponseRateLimit } from "./rate-limiter";
|
||||
import {
|
||||
accountInfo,
|
||||
callbackOAuth,
|
||||
@@ -302,7 +302,8 @@ export const router = <Option extends BetterAuthOptions>(
|
||||
|
||||
return currentRequest;
|
||||
},
|
||||
async onResponse(res) {
|
||||
async onResponse(res, req) {
|
||||
await onResponseRateLimit(req, ctx);
|
||||
for (const plugin of ctx.options.plugins || []) {
|
||||
if (plugin.onResponse) {
|
||||
const response = await plugin.onResponse(res, ctx);
|
||||
|
||||
@@ -157,17 +157,14 @@ function getRateLimitStorage(
|
||||
return createDatabaseStorageWrapper(ctx);
|
||||
}
|
||||
|
||||
export async function onRequestRateLimit(req: Request, ctx: AuthContext) {
|
||||
if (!ctx.rateLimit.enabled) {
|
||||
return;
|
||||
}
|
||||
async function resolveRateLimitConfig(req: Request, ctx: AuthContext) {
|
||||
const basePath = new URL(ctx.baseURL).pathname;
|
||||
const path = normalizePathname(req.url, basePath);
|
||||
let currentWindow = ctx.rateLimit.window;
|
||||
let currentMax = ctx.rateLimit.max;
|
||||
const ip = getIp(req, ctx.options);
|
||||
if (!ip) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
const key = createRateLimitKey(ip, path);
|
||||
const specialRules = getDefaultSpecialRules();
|
||||
@@ -214,11 +211,45 @@ export async function onRequestRateLimit(req: Request, ctx: AuthContext) {
|
||||
}
|
||||
|
||||
if (resolved === false) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { key, currentWindow, currentMax };
|
||||
}
|
||||
|
||||
export async function onRequestRateLimit(req: Request, ctx: AuthContext) {
|
||||
if (!ctx.rateLimit.enabled) {
|
||||
return;
|
||||
}
|
||||
const config = await resolveRateLimitConfig(req, ctx);
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
const { key, currentWindow, currentMax } = config;
|
||||
|
||||
const storage = getRateLimitStorage(ctx, {
|
||||
window: currentWindow,
|
||||
});
|
||||
const data = await storage.get(key);
|
||||
|
||||
if (data && shouldRateLimit(currentMax, currentWindow, data)) {
|
||||
const retryAfter = getRetryAfter(data.lastRequest, currentWindow);
|
||||
return rateLimitResponse(retryAfter);
|
||||
}
|
||||
}
|
||||
|
||||
export async function onResponseRateLimit(req: Request, ctx: AuthContext) {
|
||||
if (!ctx.rateLimit.enabled) {
|
||||
return;
|
||||
}
|
||||
const config = await resolveRateLimitConfig(req, ctx);
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
const { key, currentWindow } = config;
|
||||
|
||||
const storage = getRateLimitStorage(ctx, {
|
||||
window: currentWindow,
|
||||
});
|
||||
@@ -234,10 +265,7 @@ export async function onRequestRateLimit(req: Request, ctx: AuthContext) {
|
||||
} else {
|
||||
const timeSinceLastRequest = now - data.lastRequest;
|
||||
|
||||
if (shouldRateLimit(currentMax, currentWindow, data)) {
|
||||
const retryAfter = getRetryAfter(data.lastRequest, currentWindow);
|
||||
return rateLimitResponse(retryAfter);
|
||||
} else if (timeSinceLastRequest > currentWindow * 1000) {
|
||||
if (timeSinceLastRequest > currentWindow * 1000) {
|
||||
// Reset the count if the window has passed since the last request
|
||||
await storage.set(
|
||||
key,
|
||||
|
||||
Reference in New Issue
Block a user