false, 'message' => 'Method not allowed'], 405); } $csrf = $_POST['csrf'] ?? ''; if (!Csrf::validate(is_string($csrf) ? $csrf : null)) { Bootstrap::jsonResponse(['ok' => false, 'message' => 'Ungültiges CSRF-Token.'], 419); } if (trim((string) ($_POST['website'] ?? '')) !== '') { Bootstrap::jsonResponse(['ok' => false, 'message' => 'Anfrage blockiert.'], 400); } $email = strtolower(trim((string) ($_POST['email'] ?? ''))); if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) { Bootstrap::jsonResponse(['ok' => false, 'message' => 'Bitte gültige E-Mail eingeben.'], 422); } $autoStartRaw = strtolower(trim((string) ($_POST['auto_start'] ?? '0'))); $autoStart = in_array($autoStartRaw, ['1', 'true', 'yes'], true); $app = Bootstrap::config('app'); $limiter = new RateLimiter(); $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown'; $rateKey = sprintf('otp-request:%s:%s', $ip, $email); if (!$limiter->allow($rateKey, (int) $app['rate_limit']['requests'], (int) $app['rate_limit']['window_seconds'])) { Bootstrap::jsonResponse(['ok' => false, 'message' => 'Zu viele Anfragen. Bitte später erneut versuchen.'], 429); } $formAccess = new FormAccess(); $request = $formAccess->requestOtp($email, $autoStart); if (($request['ok'] ?? false) !== true) { Bootstrap::jsonResponse([ 'ok' => false, 'message' => (string) ($request['message'] ?? 'Code konnte nicht angefordert werden.'), 'retry_after' => (int) ($request['retry_after'] ?? 0), ], (int) ($request['status_code'] ?? 422)); } if (($request['auto_skipped'] ?? false) === true) { Bootstrap::jsonResponse([ 'ok' => true, 'auto_skipped' => true, 'message' => 'Automatische Code-Anfrage in dieser Sitzung bereits erfolgt.', ]); } $otpCode = (string) ($request['code'] ?? ''); $ttlSeconds = (int) ($request['expires_in'] ?? $formAccess->otpTtlSeconds()); $mailer = new Mailer(); if (!$mailer->sendOtpMail($email, $otpCode, $ttlSeconds)) { $formAccess->clearPendingOtp(); Bootstrap::jsonResponse([ 'ok' => false, 'message' => 'Code konnte nicht per E-Mail gesendet werden. Bitte später erneut versuchen.', ], 500); } Bootstrap::jsonResponse([ 'ok' => true, 'message' => 'Sicherheitscode wurde per E-Mail versendet.', 'expires_in' => $ttlSeconds, 'resend_available_in' => (int) ($request['cooldown_seconds'] ?? $formAccess->resendCooldownSeconds()), ]);