Files
my2/server/middleware/rate-limit.ts
Bastian Masanek f8572c3386 Implement authentication phase with Cidaas OAuth2 integration
- Add authentication middleware to protect routes
- Create API endpoints for login, logout, registration, and user info
- Develop UI components for login and registration forms
- Integrate VeeValidate for form validation
- Update environment configuration for Cidaas settings
- Add i18n support for English and German languages
- Enhance Tailwind CSS for improved styling of auth components
- Document authentication flow and testing procedures
2025-10-31 11:44:48 +01:00

90 lines
2.1 KiB
TypeScript

// server/middleware/rate-limit.ts
/**
* Rate limiting middleware for auth endpoints
*
* Prevents brute force attacks on login/registration
*
* Limits:
* - /api/auth/login: 5 attempts per 15 minutes per IP
* - /api/auth/register: 3 attempts per hour per IP
*/
interface RateLimitEntry {
count: number
resetAt: number
}
// In-memory rate limit store (use Redis in production!)
const rateLimitStore = new Map<string, RateLimitEntry>()
// Clean up expired entries every 5 minutes
setInterval(
() => {
const now = Date.now()
for (const [key, entry] of rateLimitStore.entries()) {
if (entry.resetAt < now) {
rateLimitStore.delete(key)
}
}
},
5 * 60 * 1000
)
export default defineEventHandler((event) => {
const path = event.path
// Only apply to auth endpoints
if (!path.startsWith('/api/auth/')) {
return
}
// Get client IP
const ip = getRequestIP(event, { xForwardedFor: true }) || 'unknown'
// Define rate limits per endpoint
const limits: Record<string, { maxAttempts: number; windowMs: number }> = {
'/api/auth/login': { maxAttempts: 5, windowMs: 15 * 60 * 1000 }, // 5 per 15min
'/api/auth/register': { maxAttempts: 3, windowMs: 60 * 60 * 1000 }, // 3 per hour
}
const limit = limits[path]
if (!limit) {
return // No rate limit for this endpoint
}
// Check rate limit
const key = `${ip}:${path}`
const now = Date.now()
const entry = rateLimitStore.get(key)
if (!entry || entry.resetAt < now) {
// First attempt or window expired - reset counter
rateLimitStore.set(key, {
count: 1,
resetAt: now + limit.windowMs,
})
return
}
// Increment counter
entry.count++
if (entry.count > limit.maxAttempts) {
// Rate limit exceeded
const retryAfter = Math.ceil((entry.resetAt - now) / 1000)
setResponseStatus(event, 429)
setResponseHeader(event, 'Retry-After', retryAfter.toString())
throw createError({
statusCode: 429,
statusMessage: 'Too many requests',
data: {
retryAfter,
message: `Too many attempts. Please try again in ${retryAfter} seconds.`,
},
})
}
})