apps/authproxy/src/auth/rate-limit.ts
import type { Redis } from "ioredis";
import type { FastifyRequest } from "fastify";
import type { RateLimitPluginOptions } from "@fastify/rate-limit";
export const buildRateLimitConfig = (redis: Redis): RateLimitPluginOptions => ({
// Apply the rate limit setting to all routes within the encapsulation scope
global: true,
// The maximum number of 429 responses to return to a single client before returning 403
ban: 3,
// The duration of the time window
timeWindow: "2 minute",
// Renew user limitation when user sends a request to the server when still limited
continueExceeding: true,
// Do not skip errors generated by the storage (e.g. redis not reachable)
skipOnError: false,
// To achieve the maximum speed, this plugin requires the use of ioredis
redis,
// Show all the response headers when rate limit is not reached
addHeadersOnExceeding: {
"x-ratelimit-limit": true,
"x-ratelimit-remaining": true,
"x-ratelimit-reset": true,
},
// Show all the response headers when rate limit is reached
addHeaders: {
"x-ratelimit-limit": true,
"x-ratelimit-remaining": true,
"x-ratelimit-reset": true,
"retry-after": true,
},
// Key Generator should try to use the api key id otherwise fall back to IP
keyGenerator: (request) => request.apiKey?.id || request.ip,
// The maximum number of requests a single client can perform inside a time window.
async max(request: FastifyRequest) {
const rl = request.apiKey?.rateLimitPerWindow || 5;
request.log.debug(`This request is rate-limited to ${rl} requests per 2 minute window`);
// Update the last used date on the api key
if (request.apiKey) {
request.apiKey.lastUsed = new Date();
request.log.debug(`Set api key last used date to ${request.apiKey.lastUsed.toDateString()}`);
await request.apiKey.save();
}
return rl;
},
// Write some nice messages when rate limit is hit
errorResponseBuilder: (request, context) => {
if (context.ban) {
return {
statusCode: 403,
error: "Forbidden",
message: `You can not access this service as you have sent too many requests that exceed your rate limit. Your IP: ${request.ip} and Limit: ${context.max}`,
};
}
return {
statusCode: 429,
error: "Too Many Requests",
message: `You hit the rate limit, please slow down! You can retry in ${context.after}`,
};
},
});
export default buildRateLimitConfig;