getParsedBody() ?? []); $email = is_string($body['email'] ?? null) ? mb_strtolower(trim($body['email'])) : ''; if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL) || strlen($email) > self::EMAIL_MAX) { throw new ValidationException(['email' => ['Enter a valid email address.']]); } $user = $this->users->findByEmail($email); if ($user === null && $this->allowRegistration && $this->emailAllowlist->permits($email)) { $user = $this->users->findOrCreateByEmail($email); } if ($user !== null && !$this->recentlyEmailed($user)) { try { $this->verifier->sendLoginLink($user); } catch (MailException $e) { error_log('Login link failed for user ' . $user['id'] . ': ' . $e->getMessage()); } } return $this->json($response, [ 'message' => 'Check your email for a link to sign in.', ], 202); } /** * GET /api/me (requires AuthMiddleware) */ public function me(Request $request, Response $response): Response { return $this->json($response, ['user' => $this->session->present($this->user($request))]); } /** * @param array{verification_email_sent_at?: string|null} $user */ private function recentlyEmailed(array $user): bool { $lastSent = $user['verification_email_sent_at'] ?? null; return $lastSent !== null && (time() - (int) strtotime($lastSent)) < $this->verifier->resendIntervalSeconds; } }