feat: Improve error handling in ratelimit (#25223)
* Improve error handling in ratelimit * Update route.ts * Update route.ts * Update route.ts * address feedback
This commit is contained in:
@@ -7,6 +7,9 @@ import { z } from "zod";
|
||||
|
||||
import { validPassword } from "@calcom/features/auth/lib/validPassword";
|
||||
import { hashPassword } from "@calcom/lib/auth/hashPassword";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import getIP from "@calcom/lib/getIP";
|
||||
import { piiHasher } from "@calcom/lib/server/PiiHasher";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { IdentityProvider } from "@calcom/prisma/enums";
|
||||
|
||||
@@ -36,8 +39,14 @@ async function handler(req: NextRequest) {
|
||||
// token verified, delete the cookie / a resubmit on failure requires a new csrf token.
|
||||
cookieStore.delete("calcom.csrf_token");
|
||||
|
||||
// rate-limited there is a low, very low chance that a password request stays valid long enough
|
||||
// to brute force 3.8126967e+40 options.
|
||||
const remoteIp = getIP(req);
|
||||
await checkRateLimitAndThrowError({
|
||||
rateLimitingType: "core",
|
||||
identifier: `api:reset-password:${piiHasher.hash(remoteIp)}`,
|
||||
});
|
||||
|
||||
// Note: There is a low, very low chance that a password request stays valid long enough
|
||||
// to brute force 3.8126967e+40 options, but rate limiting provides additional protection.
|
||||
const maybeRequest = await prisma.resetPasswordRequest.findFirstOrThrow({
|
||||
where: {
|
||||
id: rawRequestId,
|
||||
|
||||
@@ -5,10 +5,12 @@ import { NextResponse, type NextRequest } from "next/server";
|
||||
import calcomSignupHandler from "@calcom/feature-auth/signup/handlers/calcomHandler";
|
||||
import selfHostedSignupHandler from "@calcom/feature-auth/signup/handlers/selfHostedHandler";
|
||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import { IS_PREMIUM_USERNAME_ENABLED } from "@calcom/lib/constants";
|
||||
import getIP from "@calcom/lib/getIP";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { piiHasher } from "@calcom/lib/server/PiiHasher";
|
||||
import { checkCfTurnstileToken } from "@calcom/lib/server/checkCfTurnstileToken";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import { signupSchema } from "@calcom/prisma/zod-utils";
|
||||
@@ -38,6 +40,12 @@ async function handler(req: NextRequest) {
|
||||
const remoteIp = getIP(req);
|
||||
// Use a try catch instead of returning res every time
|
||||
try {
|
||||
// Rate limit: 10 signups per 60 seconds per IP
|
||||
await checkRateLimitAndThrowError({
|
||||
rateLimitingType: "core",
|
||||
identifier: `api:signup:${piiHasher.hash(remoteIp)}`,
|
||||
});
|
||||
|
||||
const body = await parseRequestData(req);
|
||||
await checkCfTurnstileToken({
|
||||
token: req.headers.get("cf-access-token") as string,
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { NextRequest } from "next/server";
|
||||
import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { verifyPassword } from "@calcom/features/auth/lib/verifyPassword";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import { symmetricDecrypt } from "@calcom/lib/crypto";
|
||||
import { totpAuthenticatorCheck } from "@calcom/lib/totp";
|
||||
import prisma from "@calcom/prisma";
|
||||
@@ -27,6 +28,11 @@ async function handler(req: NextRequest) {
|
||||
return NextResponse.json({ error: ErrorCode.InternalServerError }, { status: 500 });
|
||||
}
|
||||
|
||||
await checkRateLimitAndThrowError({
|
||||
rateLimitingType: "core",
|
||||
identifier: `api:totp-disable:${session.user.id}`,
|
||||
});
|
||||
|
||||
const user = await prisma.user.findUnique({ where: { id: session.user.id }, include: { password: true } });
|
||||
|
||||
if (!user) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { NextRequest } from "next/server";
|
||||
|
||||
import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import { symmetricDecrypt } from "@calcom/lib/crypto";
|
||||
import { totpAuthenticatorCheck } from "@calcom/lib/totp";
|
||||
import prisma from "@calcom/prisma";
|
||||
@@ -25,6 +26,11 @@ async function postHandler(req: NextRequest) {
|
||||
return NextResponse.json({ error: ErrorCode.InternalServerError }, { status: 500 });
|
||||
}
|
||||
|
||||
await checkRateLimitAndThrowError({
|
||||
rateLimitingType: "core",
|
||||
identifier: `api:totp-enable:${session.user.id}`,
|
||||
});
|
||||
|
||||
const user = await prisma.user.findUnique({ where: { id: session.user.id } });
|
||||
if (!user) {
|
||||
console.error(`Session references user that no longer exists.`);
|
||||
|
||||
@@ -10,6 +10,7 @@ import qrcode from "qrcode";
|
||||
import { ErrorCode } from "@calcom/features/auth/lib/ErrorCode";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { verifyPassword } from "@calcom/features/auth/lib/verifyPassword";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import { symmetricEncrypt } from "@calcom/lib/crypto";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { IdentityProvider } from "@calcom/prisma/enums";
|
||||
@@ -29,6 +30,11 @@ async function postHandler(req: NextRequest) {
|
||||
return NextResponse.json({ error: ErrorCode.InternalServerError }, { status: 500 });
|
||||
}
|
||||
|
||||
await checkRateLimitAndThrowError({
|
||||
rateLimitingType: "core",
|
||||
identifier: `api:totp-setup:${session.user.id}`,
|
||||
});
|
||||
|
||||
const user = await prisma.user.findUnique({ where: { id: session.user.id }, include: { password: true } });
|
||||
|
||||
if (!user) {
|
||||
|
||||
@@ -5,6 +5,9 @@ import type { NextRequest } from "next/server";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import handleCancelBooking from "@calcom/features/bookings/lib/handleCancelBooking";
|
||||
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
|
||||
import getIP from "@calcom/lib/getIP";
|
||||
import { piiHasher } from "@calcom/lib/server/PiiHasher";
|
||||
import { bookingCancelWithCsrfSchema } from "@calcom/prisma/zod-utils";
|
||||
import { validateCsrfToken } from "@calcom/web/lib/validateCsrfToken";
|
||||
|
||||
@@ -26,6 +29,15 @@ async function handler(req: NextRequest) {
|
||||
|
||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||
|
||||
// Rate limit: 10 booking cancellations per 60 seconds per user (or IP if not authenticated)
|
||||
const identifier = session?.user?.id
|
||||
? `api:cancel-user:${session.user.id}`
|
||||
: `api:cancel-ip:${piiHasher.hash(getIP(req))}`;
|
||||
await checkRateLimitAndThrowError({
|
||||
rateLimitingType: "core",
|
||||
identifier,
|
||||
});
|
||||
|
||||
const result = await handleCancelBooking({
|
||||
bookingData,
|
||||
userId: session?.user?.id || -1,
|
||||
|
||||
Reference in New Issue
Block a user