import { readUsers, hashPassword, writeUsers } from '../../utils/auth.js' import nodemailer from 'nodemailer' import crypto from 'crypto' import { assertRateLimit, getClientIp, registerRateLimitFailure, registerRateLimitSuccess } from '../../utils/rate-limit.js' import { writeAuditLog } from '../../utils/audit-log.js' export default defineEventHandler(async (event) => { try { const body = await readBody(event) const { email } = body if (!email) { throw createError({ statusCode: 400, message: 'E-Mail-Adresse ist erforderlich' }) } const ip = getClientIp(event) const emailKey = String(email || '').trim().toLowerCase() // Rate Limiting (IP + Account) assertRateLimit(event, { name: 'auth:reset:ip', keyParts: [ip], windowMs: 60 * 60 * 1000, maxAttempts: 20, lockoutMs: 30 * 60 * 1000 }) assertRateLimit(event, { name: 'auth:reset:account', keyParts: [emailKey], windowMs: 60 * 60 * 1000, maxAttempts: 5, lockoutMs: 60 * 60 * 1000 }) // Find user const users = await readUsers() const user = users.find(u => u.email.toLowerCase() === email.toLowerCase()) // Always return success (security: don't reveal if email exists) if (!user) { await registerRateLimitFailure(event, { name: 'auth:reset:ip', keyParts: [ip] }) await registerRateLimitFailure(event, { name: 'auth:reset:account', keyParts: [emailKey] }) await writeAuditLog('auth.reset.request', { ip, email: emailKey, userFound: false }) return { success: true, message: 'Falls ein Konto mit dieser E-Mail existiert, wurde eine E-Mail gesendet.' } } // Generate temporary password const tempPassword = crypto.randomBytes(8).toString('hex') const hashedPassword = await hashPassword(tempPassword) // Update user password user.password = hashedPassword user.passwordResetRequired = true const updatedUsers = users.map(u => u.id === user.id ? user : u) await writeUsers(updatedUsers) registerRateLimitSuccess(event, { name: 'auth:reset:account', keyParts: [emailKey] }) await writeAuditLog('auth.reset.request', { ip, email: emailKey, userFound: true, userId: user.id }) // Send email with temporary password const smtpUser = process.env.SMTP_USER const smtpPass = process.env.SMTP_PASS if (!smtpUser || !smtpPass) { console.warn('SMTP-Credentials fehlen! E-Mail-Versand wird übersprungen.') console.warn(`SMTP_USER=${smtpUser ? 'gesetzt' : 'FEHLT'}, SMTP_PASS=${smtpPass ? 'gesetzt' : 'FEHLT'}`) // Continue without sending email - security: don't reveal if email exists } else { const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST || 'smtp.gmail.com', port: process.env.SMTP_PORT || 587, secure: false, auth: { user: smtpUser, pass: smtpPass } }) const mailOptions = { from: process.env.SMTP_FROM || 'noreply@harheimertc.de', to: user.email, subject: 'Passwort zurücksetzen - Harheimer TC', html: `
Hallo ${user.name},
Sie haben eine Anfrage zum Zurücksetzen Ihres Passworts gestellt.
Ihr temporäres Passwort lautet: ${tempPassword}
Bitte melden Sie sich damit an und ändern Sie Ihr Passwort im Mitgliederbereich.
Falls Sie diese Anfrage nicht gestellt haben, ignorieren Sie diese E-Mail.
Mit sportlichen Grüßen,
Ihr Harheimer TC