request-otp.php 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. <?php
  2. declare(strict_types=1);
  3. use App\App\Bootstrap;
  4. use App\Mail\Mailer;
  5. use App\Security\Csrf;
  6. use App\Security\FormAccess;
  7. use App\Security\RateLimiter;
  8. require dirname(__DIR__) . '/src/autoload.php';
  9. Bootstrap::init();
  10. if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
  11. Bootstrap::jsonResponse(['ok' => false, 'message' => 'Method not allowed'], 405);
  12. }
  13. $csrf = $_POST['csrf'] ?? '';
  14. if (!Csrf::validate(is_string($csrf) ? $csrf : null)) {
  15. Bootstrap::jsonResponse(['ok' => false, 'message' => 'Ungueltiges CSRF-Token.'], 419);
  16. }
  17. if (trim((string) ($_POST['website'] ?? '')) !== '') {
  18. Bootstrap::jsonResponse(['ok' => false, 'message' => 'Anfrage blockiert.'], 400);
  19. }
  20. $email = strtolower(trim((string) ($_POST['email'] ?? '')));
  21. if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
  22. Bootstrap::jsonResponse(['ok' => false, 'message' => 'Bitte gueltige E-Mail eingeben.'], 422);
  23. }
  24. $autoStartRaw = strtolower(trim((string) ($_POST['auto_start'] ?? '0')));
  25. $autoStart = in_array($autoStartRaw, ['1', 'true', 'yes'], true);
  26. $app = Bootstrap::config('app');
  27. $limiter = new RateLimiter();
  28. $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
  29. $rateKey = sprintf('otp-request:%s:%s', $ip, $email);
  30. if (!$limiter->allow($rateKey, (int) $app['rate_limit']['requests'], (int) $app['rate_limit']['window_seconds'])) {
  31. Bootstrap::jsonResponse(['ok' => false, 'message' => 'Zu viele Anfragen. Bitte spaeter erneut versuchen.'], 429);
  32. }
  33. $formAccess = new FormAccess();
  34. $request = $formAccess->requestOtp($email, $autoStart);
  35. if (($request['ok'] ?? false) !== true) {
  36. Bootstrap::jsonResponse([
  37. 'ok' => false,
  38. 'message' => (string) ($request['message'] ?? 'Code konnte nicht angefordert werden.'),
  39. 'retry_after' => (int) ($request['retry_after'] ?? 0),
  40. ], (int) ($request['status_code'] ?? 422));
  41. }
  42. if (($request['auto_skipped'] ?? false) === true) {
  43. Bootstrap::jsonResponse([
  44. 'ok' => true,
  45. 'auto_skipped' => true,
  46. 'message' => 'Automatische Code-Anfrage in dieser Sitzung bereits erfolgt.',
  47. ]);
  48. }
  49. $otpCode = (string) ($request['code'] ?? '');
  50. $ttlSeconds = (int) ($request['expires_in'] ?? $formAccess->otpTtlSeconds());
  51. $mailer = new Mailer();
  52. if (!$mailer->sendOtpMail($email, $otpCode, $ttlSeconds)) {
  53. $formAccess->clearPendingOtp();
  54. Bootstrap::jsonResponse([
  55. 'ok' => false,
  56. 'message' => 'Code konnte nicht per E-Mail gesendet werden. Bitte spaeter erneut versuchen.',
  57. ], 500);
  58. }
  59. Bootstrap::jsonResponse([
  60. 'ok' => true,
  61. 'message' => 'Sicherheitscode wurde per E-Mail versendet.',
  62. 'expires_in' => $ttlSeconds,
  63. 'resend_available_in' => (int) ($request['cooldown_seconds'] ?? $formAccess->resendCooldownSeconds()),
  64. ]);