diff --git a/config/database.example.php b/config/database.example.php index a953536..ffc1d3f 100644 --- a/config/database.example.php +++ b/config/database.example.php @@ -4,8 +4,9 @@ class Database { private $host = "127.0.0.1"; private $db_name = "VOTRE_BDD"; - private $username = "VOTRE_USER"; + private $username = "VOTRE_USER"; // N'utilisez par "root" private $password = "VOTRE_MDP"; + public $jwt_secret = "VOTRE_CLE_SECRETE_POUR_JWT"; // Une passphrase est préférable public $conn; // Récupérer la connexion diff --git a/controllers/UserController.php b/controllers/UserController.php index 9087c42..f6f4e66 100644 --- a/controllers/UserController.php +++ b/controllers/UserController.php @@ -6,10 +6,12 @@ require_once __DIR__ . '/../models/User.php'; class UserController { private $db; private $userModel; + private $jwt_secret; - public function __construct($db) { + public function __construct($db, $jwt_secret = null) { $this->db = $db; $this->userModel = new User($this->db); + $this->jwt_secret = $jwt_secret; } // --- INTERCEPTE L'INSCRIPTION --- @@ -44,13 +46,22 @@ class UserController { $result = $this->userModel->login($data['email'], $data['password']); if ($result['success']) { - http_response_code(200); // 200 OK - // TODO: Plus tard, on générera un token (JWT) ici pour sécuriser les sessions de l'API - } else { - http_response_code(401); // 401 Unauthorized - } + require_once __DIR__ . '/../utils/JWT.php'; + + // On prépare les infos à enfermer dans le token + $token_payload = ["user_id" => $result['user']['id']]; + $token = JWT::generate($token_payload, $this->jwt_secret); - echo json_encode($result); + http_response_code(200); + echo json_encode([ + "success" => true, + "token" => $token, // Le client stockera ce token + "user" => $result['user'] + ]); + } else { + http_response_code(401); + echo json_encode($result); + } } // --- INTERCEPTE LA LIAISON DE COMPTES --- diff --git a/index.php b/index.php index cac3549..caedf38 100644 --- a/index.php +++ b/index.php @@ -16,11 +16,14 @@ if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { // --- CONFIGURATION --- require_once __DIR__ . '/config/database.php'; require_once __DIR__ . '/controllers/UserController.php'; +require_once __DIR__ . '/utils/JWT.php'; // Initialisation de la BDD et du contrôleur $database = new Database(); $db = $database->getConnection(); -$userController = new UserController($db); +$jwt_secret = $database->jwt_secret; + +$userController = new UserController($db, $jwt_secret); // On récupère la route épurée (ex: si on tape /register, $route vaudra 'register') $request_uri = explode('?', $_SERVER['REQUEST_URI'], 2)[0]; @@ -30,52 +33,63 @@ $method = $_SERVER['REQUEST_METHOD']; // Récupération des données JSON reçues (ex: les credentials d'inscription) $data = json_decode(file_get_contents('php://input'), true) ?? []; +// --- FONCTION DE SÉCURISATION DES ROUTES --- +function getAuthenticatedUserId($secret) { + $headers = apache_request_headers(); + // On gère les différentes casses possibles pour le header Authorization + $authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? null; + + if (!$authHeader || !preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) { + http_response_code(401); + echo json_encode(["error" => "Accès refusé. Token manquant."]); + exit(); + } + + $token = $matches[1]; + $decoded = JWT::validate($token, $secret); + + if (!$decoded) { + http_response_code(401); + echo json_encode(["error" => "Session expirée ou token invalide."]); + exit(); + } + + return $decoded['user_id']; // Retourne le vrai user_id extrait du token +} + // --- ROUTEUR --- switch ($route) { case 'register': - if ($method === 'POST') { - $userController->register($data); - } else { - http_response_code(405); - echo json_encode(["error" => "Méthode non autorisée (POST requis)"]); - } + if ($method === 'POST') $userController->register($data); + else http_response_code(405); break; case 'login': - if ($method === 'POST') { - $userController->login($data); - } else { - http_response_code(405); - echo json_encode(["error" => "Méthode non autorisée (POST requis)"]); - } + if ($method === 'POST') $userController->login($data); + else http_response_code(405); break; + // --- TOUTES LES ROUTES CI-DESSOUS DEVIENNENT SÉCURISÉES --- case 'link-partner': if ($method === 'POST') { + $data['user_id'] = getAuthenticatedUserId($jwt_secret); // Plus besoin d'envoyer user_id en clair, le token le donne ! $userController->link($data); - } else { - http_response_code(405); - echo json_encode(["error" => "Méthode non autorisée (POST requis)"]); - } + } else { http_response_code(405); } break; - + case 'pairing-status': - if ($method === 'POST') { // On utilise POST temporairement pour lire le user_id dans le JSON + if ($method === 'POST') { + $data['user_id'] = getAuthenticatedUserId($jwt_secret); $userController->pairingStatus($data); - } else { - http_response_code(405); - echo json_encode(["error" => "Méthode non autorisée (POST requis)"]); - } + } else { http_response_code(405); } break; case 'unlink-partner': if ($method === 'POST') { + $data['user_id'] = getAuthenticatedUserId($jwt_secret); $userController->unlink($data); - } else { - http_response_code(405); - echo json_encode(["error" => "Méthode non autorisée (POST requis)"]); - } + } else { http_response_code(405); } break; default: diff --git a/utils/JWT.php b/utils/JWT.php new file mode 100644 index 0000000..1de4651 --- /dev/null +++ b/utils/JWT.php @@ -0,0 +1,55 @@ + 'HS256', 'typ' => 'JWT']); + + // On ajoute une date d'expiration (ex: valide 30 jours) + $payload['exp'] = time() + (30 * 24 * 60 * 60); + $payload_json = json_encode($payload); + + $base64UrlHeader = self::base64UrlEncode($header); + $base64UrlPayload = self::base64UrlEncode($payload_json); + + // Signature HMAC SHA256 + $signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $secret, true); + $base64UrlSignature = self::base64UrlEncode($signature); + + return $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature; + } + + // 2. VÉRIFIER LE TOKEN + public static function validate($token, $secret) { + $part = explode('.', $token); + if (count($part) !== 3) return false; + + list($header, $payload, $signature) = $part; + + // On refait la signature pour vérifier si elle correspond + $valid_signature = hash_hmac('sha256', $header . "." . $payload, $secret, true); + $valid_base64UrlSignature = self::base64UrlEncode($valid_signature); + + if ($signature !== $valid_base64UrlSignature) return false; + + $payload_data = json_encode(json_decode(self::base64UrlDecode($payload), true)); + $data = json_decode($payload_data, true); + + // Vérification de la date d'expiration + if (isset($data['exp']) && $data['exp'] < time()) return false; + + return $data; // Renvoie les données (user_id) si tout est OK + } +} \ No newline at end of file