Compare commits

...

1 Commits

  1. 47
      README.md
  2. 62
      data/settings.html
  3. 107
      src/main.cpp
  4. 12
      src/server.cpp

47
README.md

@ -7,6 +7,7 @@ Squelette d'application web pour ESP32 avec interface de gestion via dashboard w @@ -7,6 +7,7 @@ Squelette d'application web pour ESP32 avec interface de gestion via dashboard w
- **Dashboard web responsive** - Interface utilisateur moderne et adaptative
- **Authentification sécurisée** - Système de login avec sessions et hash SHA-256
- **Configuration WiFi** - Mode Station et Point d'Accès automatique
- **WPA2-Enterprise PEAP** - Connexion PEAP (sans certificat) avec utilisateur/mot de passe (MSCHAPv2)
- **API REST** - Endpoints pour configuration, statistiques et changement de mot de passe
- **Gestion des mots de passe** - Changement sécurisé avec validation de l'ancien mot de passe
- **Mise à jour OTA** - Mise à jour du firmware et système de fichiers via web avec modales de confirmation
@ -69,7 +70,9 @@ platformio device monitor --environment esp32 @@ -69,7 +70,9 @@ platformio device monitor --environment esp32
1. Accédez à la page **Paramètres**
2. Section **Configuration WiFi**
3. Entrez le SSID et le mot de passe de votre réseau
3. Choisissez le type de sécurité :
- **WPA2-PSK** (classique) : SSID + mot de passe WiFi
- **WPA2-Enterprise PEAP** : SSID + utilisateur + mot de passe (sans certificat)
4. Cliquez sur **Enregistrer WiFi**
5. L'ESP32 redémarre et se connecte à votre réseau
@ -176,7 +179,10 @@ Tous les assets CSS, JS et HTML sont compressés automatiquement en gzip avant l @@ -176,7 +179,10 @@ Tous les assets CSS, JS et HTML sont compressés automatiquement en gzip avant l
},
"wifi": {
"ssid": "Nom_du_réseau",
"security": "wpa2",
"password": "mot_de_passe_wifi",
"eap_username": "utilisateur_peap",
"eap_password": "mot_de_passe_peap",
"ap_password": "webapp123"
},
"system": {
@ -189,6 +195,44 @@ Tous les assets CSS, JS et HTML sont compressés automatiquement en gzip avant l @@ -189,6 +195,44 @@ Tous les assets CSS, JS et HTML sont compressés automatiquement en gzip avant l
}
```
### Exemple sécurisé prêt à copier (WPA2-Enterprise PEAP)
```json
{
"auth": {
"username": "admin",
"password_hash": "hash_sha256_du_mot_de_passe"
},
"wifi": {
"ssid": "Mon-SSID-Entreprise",
"security": "wpa2_peap",
"password": "",
"eap_username": "prenom.nom",
"eap_password": "",
"ap_password": "webapp123"
},
"system": {
"hostname": "ESP32-Webapp",
"session_timeout": 60
},
"params": {
"app_name": "ESP32 Webapp"
}
}
```
Notes:
- Aucun certificat n'est requis pour ce mode PEAP.
- Le mode PEAP est configuré en auto avec authentification interne MSCHAPv2.
- Remplacez `eap_username` par votre identifiant entreprise.
- Laissez `eap_password` vide dans le fichier, puis saisissez-le uniquement dans l'interface web.
Procédure recommandée (sans secret en clair dans les fichiers):
1. Téléversez cette configuration avec `eap_password` vide.
2. Ouvrez la page Paramètres de l'ESP32.
3. Sélectionnez `WPA2-Enterprise PEAP`, saisissez l'utilisateur et le mot de passe PEAP.
4. Enregistrez : l'ESP32 redémarre et se connecte.
## 🛠 Dépendances
- **ESPAsyncWebServer** - Serveur web asynchrone
@ -204,6 +248,7 @@ Tous les assets CSS, JS et HTML sont compressés automatiquement en gzip avant l @@ -204,6 +248,7 @@ Tous les assets CSS, JS et HTML sont compressés automatiquement en gzip avant l
### L'ESP32 ne se connecte pas au WiFi
1. Vérifiez le SSID et le mot de passe dans la configuration
2. En mode WPA2-PEAP, vérifiez l'utilisateur/mot de passe PEAP
2. Assurez-vous que le réseau WiFi est en 2.4 GHz (l'ESP32 ne supporte pas le 5 GHz)
3. L'ESP32 passera automatiquement en mode AP si la connexion échoue

62
data/settings.html

@ -23,15 +23,33 @@ @@ -23,15 +23,33 @@
<div class="card">
<h2>Configuration WiFi</h2>
<form id="wifiForm">
<div class="form-group">
<label for="wifi_security">Type de sécurité</label>
<select id="wifi_security" name="wifi_security">
<option value="wpa2">WPA2-PSK (classique)</option>
<option value="wpa2_peap">WPA2-Enterprise PEAP (MSCHAPv2)</option>
</select>
</div>
<div class="form-group">
<label for="wifi_ssid">SSID du réseau</label>
<input type="text" id="wifi_ssid" name="wifi_ssid">
</div>
<div class="form-group">
<div class="form-group" id="wifi_password_group">
<label for="wifi_password">Mot de passe WiFi</label>
<input type="password" id="wifi_password" name="wifi_password" placeholder="Laisser vide pour ne pas changer">
</div>
<div class="form-group" id="eap_username_group" style="display: none;">
<label for="eap_username">Utilisateur PEAP</label>
<input type="text" id="eap_username" name="eap_username" placeholder="Nom d'utilisateur" autocomplete="username">
</div>
<div class="form-group" id="eap_password_group" style="display: none;">
<label for="eap_password">Mot de passe PEAP</label>
<input type="password" id="eap_password" name="eap_password" placeholder="Laisser vide pour ne pas changer" autocomplete="current-password">
</div>
<div class="form-group">
<label for="ap_password">Mot de passe du Point d'Accès</label>
@ -88,13 +106,24 @@ @@ -88,13 +106,24 @@
<script src="/js/crypto-js.min.js"></script>
<script>
let config = {};
function toggleWifiSecurityFields() {
const security = document.getElementById('wifi_security').value;
const isPeap = security === 'wpa2_peap';
document.getElementById('wifi_password_group').style.display = isPeap ? 'none' : 'block';
document.getElementById('eap_username_group').style.display = isPeap ? 'block' : 'none';
document.getElementById('eap_password_group').style.display = isPeap ? 'block' : 'none';
}
async function loadConfig() {
try {
config = await api.getConfig();
// WiFi
document.getElementById('wifi_security').value = config.wifi.security || 'wpa2';
document.getElementById('wifi_ssid').value = config.wifi.ssid || '';
document.getElementById('eap_username').value = config.wifi.eap_username || '';
toggleWifiSecurityFields();
// Système
document.getElementById('hostname').value = config.system.hostname || '';
@ -117,11 +146,32 @@ @@ -117,11 +146,32 @@
document.getElementById('wifiForm').addEventListener('submit', async (e) => {
e.preventDefault();
config.wifi.security = document.getElementById('wifi_security').value;
config.wifi.ssid = document.getElementById('wifi_ssid').value;
const wifiPassword = document.getElementById('wifi_password').value;
if (wifiPassword) {
config.wifi.password = wifiPassword;
if (config.wifi.security === 'wpa2_peap') {
const eapUsername = document.getElementById('eap_username').value.trim();
if (!eapUsername) {
showMessage('Le nom d\'utilisateur PEAP est requis', 'error');
return;
}
config.wifi.eap_username = eapUsername;
const eapPassword = document.getElementById('eap_password').value;
if (eapPassword) {
config.wifi.eap_password = eapPassword;
}
config.wifi.password = '';
} else {
const wifiPassword = document.getElementById('wifi_password').value;
if (wifiPassword) {
config.wifi.password = wifiPassword;
}
config.wifi.eap_username = '';
config.wifi.eap_password = '';
}
const apPassword = document.getElementById('ap_password').value;
@ -136,6 +186,8 @@ @@ -136,6 +186,8 @@
showMessage('Erreur d\'enregistrement', 'error');
}
});
document.getElementById('wifi_security').addEventListener('change', toggleWifiSecurityFields);
document.getElementById('systemForm').addEventListener('submit', async (e) => {
e.preventDefault();

107
src/main.cpp

@ -3,6 +3,13 @@ @@ -3,6 +3,13 @@
#include <LittleFS.h>
#include <ArduinoJson.h>
#include <ArduinoOTA.h>
#if __has_include(<esp_wpa2.h>)
#include <esp_wpa2.h>
#include <esp_wifi.h>
#define HAS_LEGACY_WPA2_ENTERPRISE 1
#else
#define HAS_LEGACY_WPA2_ENTERPRISE 0
#endif
#include "auth.h"
#include "server.h"
#include "utils.h"
@ -11,6 +18,9 @@ @@ -11,6 +18,9 @@
String hostname = "ESP32-Webapp";
String wifiSSID = "";
String wifiPassword = "";
String wifiSecurity = "wpa2";
String wifiEapUsername = "";
String wifiEapPassword = "";
String apPassword = "webapp123";
void createDefaultConfig() {
@ -25,6 +35,9 @@ void createDefaultConfig() { @@ -25,6 +35,9 @@ void createDefaultConfig() {
// Configuration WiFi
doc["wifi"]["ssid"] = "";
doc["wifi"]["password"] = "";
doc["wifi"]["security"] = "wpa2";
doc["wifi"]["eap_username"] = "";
doc["wifi"]["eap_password"] = "";
doc["wifi"]["ap_password"] = apPassword;
// Configuration système
@ -89,6 +102,18 @@ void loadConfig() { @@ -89,6 +102,18 @@ void loadConfig() {
if (doc["wifi"]["password"]) {
wifiPassword = doc["wifi"]["password"].as<String>();
}
if (doc["wifi"]["security"]) {
wifiSecurity = doc["wifi"]["security"].as<String>();
}
if (doc["wifi"]["eap_username"]) {
wifiEapUsername = doc["wifi"]["eap_username"].as<String>();
}
if (doc["wifi"]["eap_password"]) {
wifiEapPassword = doc["wifi"]["eap_password"].as<String>();
}
if (doc["wifi"]["ap_password"]) {
apPassword = doc["wifi"]["ap_password"].as<String>();
@ -97,26 +122,82 @@ void loadConfig() { @@ -97,26 +122,82 @@ void loadConfig() {
logMessage(LOG_INFO, "Configuration chargée");
}
bool waitForWiFiConnection() {
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
logMessage(LOG_INFO, "WiFi connecté");
logMessage(LOG_INFO, "Adresse IP: " + WiFi.localIP().toString());
return true;
}
return false;
}
bool connectWPA2PEAP() {
if (wifiEapUsername.isEmpty() || wifiEapPassword.isEmpty()) {
logMessage(LOG_WARN, "WPA2-PEAP: identifiant ou mot de passe manquant");
return false;
}
logMessage(LOG_INFO, "Connexion WPA2-PEAP au WiFi: " + wifiSSID);
#if defined(WPA2_AUTH_PEAP)
WiFi.begin(
wifiSSID.c_str(),
WPA2_AUTH_PEAP,
wifiEapUsername.c_str(),
wifiEapUsername.c_str(),
wifiEapPassword.c_str(),
nullptr
);
return waitForWiFiConnection();
#elif HAS_LEGACY_WPA2_ENTERPRISE
WiFi.begin(wifiSSID.c_str());
esp_wifi_sta_wpa2_ent_set_identity((uint8_t*)wifiEapUsername.c_str(), wifiEapUsername.length());
esp_wifi_sta_wpa2_ent_set_username((uint8_t*)wifiEapUsername.c_str(), wifiEapUsername.length());
esp_wifi_sta_wpa2_ent_set_password((uint8_t*)wifiEapPassword.c_str(), wifiEapPassword.length());
#ifdef ESP_EAP_TTLS_PHASE2_MSCHAPV2
esp_wifi_sta_wpa2_ent_set_ttls_phase2_method(ESP_EAP_TTLS_PHASE2_MSCHAPV2);
#endif
esp_wifi_sta_wpa2_ent_enable();
return waitForWiFiConnection();
#else
logMessage(LOG_ERROR, "WPA2-PEAP non supporté par cette version du framework");
return false;
#endif
}
bool connectWPA2PSK() {
logMessage(LOG_INFO, "Connexion WPA2-PSK au WiFi: " + wifiSSID);
WiFi.begin(wifiSSID.c_str(), wifiPassword.c_str());
return waitForWiFiConnection();
}
void setupWiFi() {
if (wifiSSID.length() > 0) {
logMessage(LOG_INFO, "Connexion au WiFi: " + wifiSSID);
WiFi.mode(WIFI_STA);
WiFi.setHostname(hostname.c_str());
WiFi.begin(wifiSSID.c_str(), wifiPassword.c_str());
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
bool connected = false;
if (wifiSecurity == "wpa2_peap") {
connected = connectWPA2PEAP();
} else {
connected = connectWPA2PSK();
}
if (WiFi.status() == WL_CONNECTED) {
logMessage(LOG_INFO, "WiFi connecté");
logMessage(LOG_INFO, "Adresse IP: " + WiFi.localIP().toString());
if (connected) {
return;
}
logMessage(LOG_WARN, "Échec de connexion WiFi");
}

12
src/server.cpp

@ -183,6 +183,9 @@ void WebServerManager::handleGetConfig(AsyncWebServerRequest* request) { @@ -183,6 +183,9 @@ void WebServerManager::handleGetConfig(AsyncWebServerRequest* request) {
if (doc["wifi"]["password"]) {
doc["wifi"]["password"] = "***";
}
if (doc["wifi"]["eap_password"]) {
doc["wifi"]["eap_password"] = "***";
}
if (doc["wifi"]["ap_password"]) {
doc["wifi"]["ap_password"] = "***";
}
@ -232,6 +235,9 @@ void WebServerManager::handleSetConfig(AsyncWebServerRequest* request) { @@ -232,6 +235,9 @@ void WebServerManager::handleSetConfig(AsyncWebServerRequest* request) {
if (newDoc["wifi"]["password"] && newDoc["wifi"]["password"] != "***") {
existingDoc["wifi"]["password"] = newDoc["wifi"]["password"];
}
if (!newDoc["wifi"]["eap_password"].isNull() && newDoc["wifi"]["eap_password"] != "***") {
existingDoc["wifi"]["eap_password"] = newDoc["wifi"]["eap_password"];
}
if (newDoc["wifi"]["ap_password"] && newDoc["wifi"]["ap_password"] != "***") {
existingDoc["wifi"]["ap_password"] = newDoc["wifi"]["ap_password"];
}
@ -246,6 +252,12 @@ void WebServerManager::handleSetConfig(AsyncWebServerRequest* request) { @@ -246,6 +252,12 @@ void WebServerManager::handleSetConfig(AsyncWebServerRequest* request) {
if (newDoc["wifi"]["ssid"]) {
existingDoc["wifi"]["ssid"] = newDoc["wifi"]["ssid"];
}
if (!newDoc["wifi"]["security"].isNull()) {
existingDoc["wifi"]["security"] = newDoc["wifi"]["security"];
}
if (!newDoc["wifi"]["eap_username"].isNull()) {
existingDoc["wifi"]["eap_username"] = newDoc["wifi"]["eap_username"];
}
if (newDoc["system"]["hostname"]) {
existingDoc["system"]["hostname"] = newDoc["system"]["hostname"];
}

Loading…
Cancel
Save