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
This commit is contained in:
89
server/middleware/rate-limit.ts
Normal file
89
server/middleware/rate-limit.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
// 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.`,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user