Apply strict rate limits to sensitive auth endpoints.

Account deletion, key enrollment, and credential management use a separate 30/15min limiter so they are not left at 300/min while login and sync routes stay independent.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-02 22:52:52 +02:00
parent aee8f4f3db
commit 9bf59280b2
+26 -3
View File
@@ -45,7 +45,7 @@ export function createApp(): express.Express {
app.use(cookieParser())
app.use(express.json({ limit: '50mb' }))
/** Passkey login/register only — not person-pool, profile, etc. */
/** WebAuthn login/register/session — strict per IP; excludes high-volume sync routes. */
const authFlowPaths = new Set([
'/register-options',
'/register-verify',
@@ -57,13 +57,33 @@ export function createApp(): express.Express {
'/session'
])
const authLimiter = rateLimit({
/** Account/key/credential mutations — also strict; separate bucket from login flow. */
const sensitiveAuthExactPaths = new Set([
'/delete-account',
'/enroll-prf',
'/rotate-recovery',
'/add-credential-options',
'/add-credential-verify'
])
function isSensitiveAuthPath(path: string): boolean {
return sensitiveAuthExactPaths.has(path) || path.startsWith('/credentials/')
}
const authFlowLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 60,
standardHeaders: true,
legacyHeaders: false
})
const sensitiveAuthLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 30,
standardHeaders: true,
legacyHeaders: false
})
const apiLimiter = rateLimit({
windowMs: 1 * 60 * 1000,
max: 300,
@@ -80,7 +100,10 @@ export function createApp(): express.Express {
app.use('/api/auth', (req, res, next) => {
if (authFlowPaths.has(req.path)) {
return authLimiter(req, res, next)
return authFlowLimiter(req, res, next)
}
if (isSensitiveAuthPath(req.path)) {
return sensitiveAuthLimiter(req, res, next)
}
return next()
})