fix(security): rate-limit public invite and password-reset endpoints
- requestPasswordReset: rate-limited by email (authRateLimiter, 5/15 min) to prevent email bombing - resetPassword: rate-limited by token to add explicit brute-force defence - getInvite + acceptInvite: rate-limited by invite token (authRateLimiter) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import { SystemRole } from "@capakraken/db";
|
||||
import { createTRPCRouter, adminProcedure, publicProcedure } from "../trpc.js";
|
||||
import { getAppBaseUrl } from "../lib/app-base-url.js";
|
||||
import { sendEmail } from "../lib/email.js";
|
||||
import { authRateLimiter } from "../middleware/rate-limit.js";
|
||||
|
||||
const INVITE_TTL_MS = 72 * 60 * 60 * 1000; // 72 hours
|
||||
|
||||
@@ -85,6 +86,14 @@ export const inviteRouter = createTRPCRouter({
|
||||
getInvite: publicProcedure
|
||||
.input(z.object({ token: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const rl = await authRateLimiter(input.token);
|
||||
if (!rl.allowed) {
|
||||
throw new TRPCError({
|
||||
code: "TOO_MANY_REQUESTS",
|
||||
message: "Too many attempts. Please wait before trying again.",
|
||||
});
|
||||
}
|
||||
|
||||
const invite = await ctx.db.inviteToken.findUnique({
|
||||
where: { token: input.token },
|
||||
select: { email: true, role: true, expiresAt: true, usedAt: true },
|
||||
@@ -104,6 +113,14 @@ export const inviteRouter = createTRPCRouter({
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const rl = await authRateLimiter(input.token);
|
||||
if (!rl.allowed) {
|
||||
throw new TRPCError({
|
||||
code: "TOO_MANY_REQUESTS",
|
||||
message: "Too many attempts. Please wait before trying again.",
|
||||
});
|
||||
}
|
||||
|
||||
const invite = await ctx.db.inviteToken.findUnique({
|
||||
where: { token: input.token },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user