c17cad9c66
- #19 : suppression AuthService / UserRepository / Domain\User — dead code incompatible session - #22 : env() et db() centralisés dans src/helpers.php, chargé par config/config.php - #15 : typographieHtml() appliquée après Parsedown dans post_view.php Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
1.9 KiB
PHP
67 lines
1.9 KiB
PHP
<?php
|
|
|
|
// projet : mug.a5l.fr
|
|
// fichier : pages/login/magic.php
|
|
// version : 20251011
|
|
declare(strict_types=1);
|
|
|
|
if (!defined('BASE_PATH')) {
|
|
define('BASE_PATH', dirname(__DIR__, 2));
|
|
}
|
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
|
require_once dirname(__DIR__, 2) . '/config/config.php';
|
|
require_once dirname(__DIR__, 2) . '/bootstrap.php';
|
|
|
|
$token = (string)($_GET['token'] ?? '');
|
|
if ($token === '' || preg_match('/[^A-Za-z0-9\-\_]/', $token)) {
|
|
http_response_code(400);
|
|
exit('Lien invalide.');
|
|
}
|
|
|
|
$pdo = db();
|
|
$pdo->beginTransaction();
|
|
try {
|
|
// récupère lien non consommé et non expiré
|
|
$sql = 'SELECT id, email, token, created_at, expires_at, consumed_at, return_to
|
|
FROM auth_magic_links
|
|
WHERE token = :t
|
|
FOR UPDATE';
|
|
$stmt = $pdo->prepare($sql);
|
|
$stmt->execute([':t' => $token]);
|
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$row) {
|
|
throw new RuntimeException('Lien inconnu.');
|
|
}
|
|
if ($row['consumed_at'] !== null) {
|
|
throw new RuntimeException('Lien déjà utilisé.');
|
|
}
|
|
if (strtotime((string)$row['expires_at']) < time()) {
|
|
throw new RuntimeException('Lien expiré.');
|
|
}
|
|
|
|
// consomme le lien
|
|
$pdo->prepare('UPDATE auth_magic_links SET consumed_at = NOW() WHERE id = :id')->execute([':id' => $row['id']]);
|
|
$pdo->commit();
|
|
|
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
|
session_start();
|
|
}
|
|
session_regenerate_id(true);
|
|
$_SESSION['user_email'] = strtolower(trim((string)$row['email']));
|
|
|
|
$dest = $row['return_to'] ?? '/';
|
|
// sécurité: ne renvoyer que des chemins relatifs
|
|
if (!is_string($dest) || !str_starts_with($dest, '/')) {
|
|
$dest = '/';
|
|
}
|
|
header('Location: ' . $dest, true, 303);
|
|
exit;
|
|
} catch (\Throwable $e) {
|
|
if ($pdo->inTransaction()) {
|
|
$pdo->rollBack();
|
|
}
|
|
http_response_code(400);
|
|
echo htmlspecialchars($e->getMessage(), ENT_QUOTES);
|
|
}
|