Documentation

Rate limiting & cron auth

Protection contre l'abus — limites par IP/user et auth des routes cron en constant-time.

Deux helpers utilitaires lib/rate-limit.ts et lib/cron-auth.ts.

Rate limit

lib/rate-limit.ts — bucket en mémoire-process, parfait pour single-instance.

import { checkRateLimit, clientIp } from "@/lib/rate-limit";

const ip = clientIp(req.headers);
const rl = checkRateLimit(`waitlist:${ip}`, 5, 60 * 60 * 1000); // 5/h
if (!rl.ok) {
  return NextResponse.json(
    { error: "Trop de requêtes" },
    { status: 429, headers: { "Retry-After": Math.ceil(rl.retryAfterMs / 1000).toString() } },
  );
}

Routes déjà protégées :

RouteLimite
POST /api/waitlist5/h IP
POST /api/auth/register10/h IP
POST /api/auth/register/resend-code5/15min IP
POST /api/auth/forgot-password5/h IP
POST /api/auth/verify-email20/15min IP
POST /api/auth/lock-status10/min IP
POST /api/user/change-email5/h IP
GET /api/admin/users/[id]/export10/h admin

Multi-instance / serverless

Le cache est par instance. Sur Vercel serverless avec autoscaling, la limite réelle = limit × N instances. Pour un cap absolu :

  • Migre vers Upstash Redis (@upstash/ratelimit) — pattern identique
  • Ou un middleware edge avec KV store

Cron auth

lib/cron-auth.ts — vérification constant-time du header Authorization contre process.env.CRON_SECRET.

import { isAuthorizedCron } from "@/lib/cron-auth";

export async function POST(req: Request) {
  if (!isAuthorizedCron(req.headers.get("authorization"))) {
    return NextResponse.json({ error: "Forbidden" }, { status: 403 });
  }
  // ...
}

Compare via timingSafeEqual plutôt que === — évite la fuite du secret par timing.

Routes déjà protégées :

  • /api/cron/coupon-ending
  • /api/cron/purge-deleted-accounts

Côté planificateur

Set le header sur ton scheduler (Vercel Cron, Trigger.dev, etc.) :

Authorization: Bearer ${CRON_SECRET}

Le secret doit être un random ≥ 32 chars (généré une fois, stocké en env). Ne jamais le logger.

On this page