beginTransaction(); try { // purge expirés / consommés $pdo->prepare('DELETE FROM auth_magic_links WHERE email = :e AND (expires_at < NOW() OR consumed_at IS NOT NULL)') ->execute([':e' => $email]); // 1) cooldown: refuser si un envoi récent < coolMin $sql = sprintf( "SELECT 1 FROM auth_magic_links WHERE email = :e AND created_at >= NOW() - INTERVAL '%d minutes' LIMIT 1", max(0, $coolMin) ); $stmt = $pdo->prepare($sql); $stmt->execute([':e' => $email]); if ($stmt->fetchColumn()) { throw new RuntimeException(sprintf('Un lien vient d’être envoyé. Réessayez dans %d min. Si vous ne recevez toujours rien, envisagez d\'utiliser un fournisseur de messagerie respectueux de la vie privée, comme Proton Mail, Tuta, Posteo, Mailfence ou Infomaniak, qui garantissent un hébergement européen et ne revendent pas vos données. -- Cédrix, le 11/10/2025', $coolMin)); } // 2) plafond: maxPerWin liens sur winHours $sql = sprintf( "SELECT COUNT(*) FROM auth_magic_links WHERE email = :e AND created_at >= NOW() - INTERVAL '%d hours'", max(0, $winHours) ); $stmt = $pdo->prepare($sql); $stmt->execute([':e' => $email]); if ((int)$stmt->fetchColumn() >= $maxPerWin) { throw new RuntimeException('Quota atteint. Réessayez plus tard.'); } // Génère et enregistre le lien avec TTL ttlMin $raw = random_bytes(32); $token = rtrim(strtr(base64_encode($raw), '+/', '-_'), '='); $sql = sprintf( "INSERT INTO auth_magic_links (id,email,token,created_at,expires_at,ip,user_agent,return_to) VALUES (gen_random_uuid(), :email, :token, NOW(), NOW() + INTERVAL '%d minutes', :ip, :ua, :rt) RETURNING token", max(1, $ttlMin) ); $stmt = $pdo->prepare($sql); $stmt->execute([ ':email' => $email, ':token' => $token, ':ip' => $ip, ':ua' => substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 512), ':rt' => ($returnTo !== '/' ? $returnTo : null), ]); $pdo->commit(); // construit l’URL et ENVOIE le mail ici... $magicUrl = url('/login/magic.php') . '?token=' . urlencode($token); /* envoyer_mail_smtp(...) ou mail(...) */ // message utilisateur $okMsg = "Un lien vient d'être envoyé. Vérifiez votre boîte de réception et le dossier spam/indésirables."; } catch (\Throwable $ex) { if ($pdo->inTransaction()) { $pdo->rollBack(); } $errors[] = $ex->getMessage(); } } } } $csrf = Csrf::token(); ?>
Vous n’êtes pas connecté. Accédez auxfonctionnalités en vous identifiant.
OIDC_ISSUER et OIDC_CLIENT_ID dans .env.