Sécurisation d'un secret tiers
Compréhension de la frontière client / serveur : ce qui est envoyé au navigateur est public. La clé reste dans un fichier de configuration exclu du dépôt, jamais dans le code livré.
Application météo en temps réel avec villes favorites. Le front, statique, ne connaît aucune clé d'API : il interroge une API PHP qui fait proxy vers OpenWeatherMap et persiste les favoris en MySQL, sans aucun système de compte.
Brancher une application front sur une API tierce pose toujours la même question : où vit la clé d'accès ? Appelée directement depuis le navigateur, elle est lisible par n'importe quel visiteur dans l'onglet réseau et donc réutilisable par un tiers, sur le quota du propriétaire du compte.
Ce projet traite le problème par une couche serveur intermédiaire : une petite API PHP détient la clé, interroge OpenWeatherMap, et ne renvoie au front que les champs strictement nécessaires. La même couche persiste les villes favorites en MySQL, rattachées à un identifiant de visiteur anonyme ce qui permet de retrouver ses favoris sans jamais créer de compte ni collecter de donnée personnelle.
Une contrainte de plateforme structure toute l'architecture : le portfolio est déployé sur Vercel, qui n'exécute pas de PHP. Le front et l'API vivent donc sur deux hébergements distincts, ce qui impose de traiter explicitement le partage entre origines (CORS).
Quatre briques : la recherche côté client, le proxy PHP qui protège la clé, la persistance MySQL des favoris, et la gestion du déploiement réparti.
Le front n'appelle jamais OpenWeatherMap. Il interroge une
seule adresse, définie par la constante API_BASE,
et bascule automatiquement entre l'API locale et l'API en
ligne selon le domaine servi. Les erreurs (ville introuvable,
service injoignable, délai dépassé) sont traduites en messages
lisibles plutôt qu'en échec silencieux.
weather.php lit la clé dans un fichier de
configuration exclu du dépôt, appelle OpenWeatherMap en cURL,
puis renvoie une réponse normalisée : ville, pays,
température, ressenti, humidité, vent, description en
français. Le contrat de données est ainsi maîtrisé et
indépendant du format du fournisseur.
À la première visite, un identifiant anonyme est généré via
crypto.randomUUID() et conservé en
localStorage. Toutes les opérations sur les
favoris sont scopées sur cet identifiant côté MySQL, avec une
contrainte d'unicité sur le couple visiteur/ville qui rend les
ajouts idempotents. Aucun e-mail, aucun mot de passe.
Vercel n'exécutant pas de PHP, l'API est hébergée séparément. Les deux origines communiquent via une liste blanche explicite de domaines jamais un joker avec gestion de la requête préliminaire. Les accès en base passent tous par des requêtes préparées PDO.
Saisissez une ville pour obtenir sa météo en direct, puis ajoutez vos villes favorites elles vous seront proposées à la prochaine visite.
Compréhension de la frontière client / serveur : ce qui est envoyé au navigateur est public. La clé reste dans un fichier de configuration exclu du dépôt, jamais dans le code livré.
Identification en amont d'une limite d'hébergement Vercel n'exécute pas de PHP et conception d'une architecture répartie qui en tient compte, plutôt que découverte du problème au déploiement.
Conception d'un schéma MySQL avec contrainte d'unicité, CRUD complet scopé par visiteur, et accès exclusivement via requêtes préparées PDO.
Un identifiant anonyme suffit à offrir une expérience personnalisée : ni inscription, ni mot de passe, ni collecte de donnée personnelle. Le parcours reste ouvert dès la première seconde.
// Proxy OpenWeatherMap la clé ne quitte jamais le serveur (api/weather.php) $config = require __DIR__ . '/config.php'; // fichier gitignoré $url = 'https://api.openweathermap.org/data/2.5/weather?' . http_build_query([ 'q' => $city, 'appid' => $config['owm_api_key'], 'units' => 'metric', 'lang' => 'fr', ]); // Le front ne reçoit qu'un contrat de données maîtrisé, pas la réponse brute echo json_encode([ 'city' => $data['name'], 'temp' => round($data['main']['temp']), 'icon' => $data['weather'][0]['icon'] ?? '01d', ]); // Favoris toujours scopés sur le visiteur, en requête préparée (api/favorites.php) $stmt = $pdo->prepare('SELECT city, country FROM favorites WHERE visitor_id = ?'); $stmt->execute([$visitor]);