fix: use ip address in forgot password rate limitation (#7832)

* feat: use IP address for rate limiting

* fix: use mail as the last choice

* fix: fallback to ip

* fix: endpoint

* fix: endpoint
pull/9282/head
Nafees Nazik 2023-05-31 00:56:29 +05:30 committed by GitHub
parent 6cf8083b02
commit 951709edef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 25 additions and 9 deletions

View File

@ -6,6 +6,7 @@ import dayjs from "@calcom/dayjs";
import { sendPasswordResetEmail } from "@calcom/emails"; import { sendPasswordResetEmail } from "@calcom/emails";
import { PASSWORD_RESET_EXPIRY_HOURS } from "@calcom/emails/templates/forgot-password-email"; import { PASSWORD_RESET_EXPIRY_HOURS } from "@calcom/emails/templates/forgot-password-email";
import rateLimit from "@calcom/lib/rateLimit"; import rateLimit from "@calcom/lib/rateLimit";
import { defaultHandler } from "@calcom/lib/server";
import { getTranslation } from "@calcom/lib/server/i18n"; import { getTranslation } from "@calcom/lib/server/i18n";
import prisma from "@calcom/prisma"; import prisma from "@calcom/prisma";
@ -13,28 +14,39 @@ const limiter = rateLimit({
intervalInMs: 60 * 1000, // 1 minute intervalInMs: 60 * 1000, // 1 minute
}); });
export default async function handler(req: NextApiRequest, res: NextApiResponse) { async function handler(req: NextApiRequest, res: NextApiResponse) {
const t = await getTranslation(req.body.language ?? "en", "common"); const t = await getTranslation(req.body.language ?? "en", "common");
const email = z const email = z
.string() .string()
.email() .email()
.transform((val) => val.toLowerCase()) .transform((val) => val.toLowerCase())
.parse(req.body?.email); .safeParse(req.body?.email);
const { isRateLimited } = limiter.check(10, email); // 10 requests per minute if (!email.success) {
return res.status(400).json({ message: "email is required" });
if (isRateLimited) {
return res.status(429).json({ message: "Too Many Requests." });
} }
if (req.method !== "POST") { // fallback to email if ip is not present
return res.status(405).end(); let ip = (req.headers["x-real-ip"] as string) ?? email.data;
const forwardedFor = req.headers["x-forwarded-for"] as string;
if (!ip && forwardedFor) {
ip = forwardedFor?.split(",").at(0) ?? email.data;
}
// 10 requests per minute
try {
limiter.check(10, ip);
} catch (e) {
return res.status(429).json({ message: "Too Many Requests." });
} }
try { try {
const maybeUser = await prisma.user.findUnique({ const maybeUser = await prisma.user.findUnique({
where: { where: {
email, email: email.data,
}, },
select: { select: {
name: true, name: true,
@ -97,3 +109,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return res.status(500).json({ message: "Unable to create password reset request" }); return res.status(500).json({ message: "Unable to create password reset request" });
} }
} }
export default defaultHandler({
POST: Promise.resolve({ default: handler }),
});