Web Server Authentication

This commit is contained in:
amarofarinha 2024-09-08 20:47:37 +01:00
parent a4e56d4b04
commit 1647a4c572
5 changed files with 77 additions and 8 deletions

View file

@ -25,6 +25,9 @@ std::string password = "REPLACE_WITH_YOUR_PASSWORD"; // Minimum of 8 characters
const char* ssidAP = "Battery Emulator"; // Maximum of 63 characters, also used for device name on web interface const char* ssidAP = "Battery Emulator"; // Maximum of 63 characters, also used for device name on web interface
const char* passwordAP = "123456789"; // Minimum of 8 characters; set to NULL if you want the access point to be open const char* passwordAP = "123456789"; // Minimum of 8 characters; set to NULL if you want the access point to be open
const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection const uint8_t wifi_channel = 0; // Set to 0 for automatic channel selection
const char* http_username = "admin"; // username to webserver authentication;
const char* http_password = "admin"; // password to webserver authentication;
// MQTT // MQTT
#ifdef MQTT #ifdef MQTT
const char* mqtt_user = "REDACTED"; // Set NULL for no username const char* mqtt_user = "REDACTED"; // Set NULL for no username

View file

@ -54,6 +54,8 @@
//#define SERIAL_LINK_RECEIVER //Enable this line to receive battery data over RS485 pins from another Lilygo (This LilyGo interfaces with inverter) //#define SERIAL_LINK_RECEIVER //Enable this line to receive battery data over RS485 pins from another Lilygo (This LilyGo interfaces with inverter)
//#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery) //#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery)
#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings. #define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings.
#define WEBSERVER_AUTH_REQUIRED \
false //Enable this line to enable webserver authentication. See USER_SETTINGS.cpp setting the credentials.
#define WIFIAP //Disable this line to permanently disable WIFI AP mode (make sure to hardcode ssid and password of you home wifi network). When enabled WIFI AP can still be disabled by a setting in the future. #define WIFIAP //Disable this line to permanently disable WIFI AP mode (make sure to hardcode ssid and password of you home wifi network). When enabled WIFI AP can still be disabled by a setting in the future.
#define MDNSRESPONDER //Enable this line to enable MDNS, allows battery monitor te be found by .local address. Requires WEBSERVER to be enabled. #define MDNSRESPONDER //Enable this line to enable MDNS, allows battery monitor te be found by .local address. Requires WEBSERVER to be enabled.
#define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot (overrides Wifi/battery settings set below) #define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot (overrides Wifi/battery settings set below)

View file

@ -19,6 +19,10 @@ To update the software over the air:
- In your webbrowser, go to the url consisting of the IP address, followed by `/update`, for instance `http://192.168.0.224/update`. - In your webbrowser, go to the url consisting of the IP address, followed by `/update`, for instance `http://192.168.0.224/update`.
- In the webbrowser, follow the steps to select the `.bin` file and to upload the file to the board. - In the webbrowser, follow the steps to select the `.bin` file and to upload the file to the board.
Security Concerns
(https://randomnerdtutorials.com/esp32-esp8266-web-server-http-authentication/)
Authentication implemented here is meant to be used in your local network to protect from anyone just typing the ESP IP address and accessing the web server (like unauthorized family member or friend).
## Future work ## Future work
This section lists a number of features that can be implemented as part of the webserver in the future. This section lists a number of features that can be implemented as part of the webserver in the future.

View file

@ -51,25 +51,41 @@ void init_webserver() {
String content = index_html; String content = index_html;
server.on("/logout", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(401); });
// Route for root / web page // Route for root / web page
server.on("/", HTTP_GET, server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
[](AsyncWebServerRequest* request) { request->send_P(200, "text/html", index_html, processor); }); if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
request->send_P(200, "text/html", index_html, processor);
});
// Route for going to settings web page // Route for going to settings web page
server.on("/settings", HTTP_GET, server.on("/settings", HTTP_GET, [](AsyncWebServerRequest* request) {
[](AsyncWebServerRequest* request) { request->send_P(200, "text/html", index_html, settings_processor); }); if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
request->send_P(200, "text/html", index_html, settings_processor);
});
// Route for going to cellmonitor web page // Route for going to cellmonitor web page
server.on("/cellmonitor", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/cellmonitor", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
request->send_P(200, "text/html", index_html, cellmonitor_processor); request->send_P(200, "text/html", index_html, cellmonitor_processor);
}); });
// Route for going to event log web page // Route for going to event log web page
server.on("/events", HTTP_GET, server.on("/events", HTTP_GET, [](AsyncWebServerRequest* request) {
[](AsyncWebServerRequest* request) { request->send_P(200, "text/html", index_html, events_processor); }); if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
request->send_P(200, "text/html", index_html, events_processor);
});
// Route for editing SSID // Route for editing SSID
server.on("/updateSSID", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateSSID", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
if (value.length() <= 63) { // Check if SSID is within the allowable length if (value.length() <= 63) { // Check if SSID is within the allowable length
@ -85,6 +101,8 @@ void init_webserver() {
}); });
// Route for editing Password // Route for editing Password
server.on("/updatePassword", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updatePassword", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
if (value.length() > 8) { // Check if password is within the allowable length if (value.length() > 8) { // Check if password is within the allowable length
@ -101,6 +119,8 @@ void init_webserver() {
// Route for editing Wh // Route for editing Wh
server.on("/updateBatterySize", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateBatterySize", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
datalayer.battery.info.total_capacity_Wh = value.toInt(); datalayer.battery.info.total_capacity_Wh = value.toInt();
@ -113,6 +133,8 @@ void init_webserver() {
// Route for editing USE_SCALED_SOC // Route for editing USE_SCALED_SOC
server.on("/updateUseScaledSOC", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateUseScaledSOC", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
datalayer.battery.settings.soc_scaling_active = value.toInt(); datalayer.battery.settings.soc_scaling_active = value.toInt();
@ -125,6 +147,8 @@ void init_webserver() {
// Route for editing SOCMax // Route for editing SOCMax
server.on("/updateSocMax", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateSocMax", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
datalayer.battery.settings.max_percentage = static_cast<uint16_t>(value.toFloat() * 100); datalayer.battery.settings.max_percentage = static_cast<uint16_t>(value.toFloat() * 100);
@ -137,6 +161,8 @@ void init_webserver() {
// Route for editing SOCMin // Route for editing SOCMin
server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
datalayer.battery.settings.min_percentage = static_cast<uint16_t>(value.toFloat() * 100); datalayer.battery.settings.min_percentage = static_cast<uint16_t>(value.toFloat() * 100);
@ -149,6 +175,8 @@ void init_webserver() {
// Route for editing MaxChargeA // Route for editing MaxChargeA
server.on("/updateMaxChargeA", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateMaxChargeA", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
datalayer.battery.info.max_charge_amp_dA = static_cast<uint16_t>(value.toFloat() * 10); datalayer.battery.info.max_charge_amp_dA = static_cast<uint16_t>(value.toFloat() * 10);
@ -161,6 +189,8 @@ void init_webserver() {
// Route for editing MaxDischargeA // Route for editing MaxDischargeA
server.on("/updateMaxDischargeA", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateMaxDischargeA", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
datalayer.battery.info.max_discharge_amp_dA = static_cast<uint16_t>(value.toFloat() * 10); datalayer.battery.info.max_discharge_amp_dA = static_cast<uint16_t>(value.toFloat() * 10);
@ -174,6 +204,8 @@ void init_webserver() {
#ifdef TEST_FAKE_BATTERY #ifdef TEST_FAKE_BATTERY
// Route for editing FakeBatteryVoltage // Route for editing FakeBatteryVoltage
server.on("/updateFakeBatteryVoltage", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateFakeBatteryVoltage", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (!request->hasParam("value")) { if (!request->hasParam("value")) {
request->send(400, "text/plain", "Bad Request"); request->send(400, "text/plain", "Bad Request");
} }
@ -190,6 +222,8 @@ void init_webserver() {
#if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER #if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
// Route for editing ChargerTargetV // Route for editing ChargerTargetV
server.on("/updateChargeSetpointV", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateChargeSetpointV", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (!request->hasParam("value")) { if (!request->hasParam("value")) {
request->send(400, "text/plain", "Bad Request"); request->send(400, "text/plain", "Bad Request");
} }
@ -212,6 +246,8 @@ void init_webserver() {
// Route for editing ChargerTargetA // Route for editing ChargerTargetA
server.on("/updateChargeSetpointA", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateChargeSetpointA", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (!request->hasParam("value")) { if (!request->hasParam("value")) {
request->send(400, "text/plain", "Bad Request"); request->send(400, "text/plain", "Bad Request");
} }
@ -234,6 +270,8 @@ void init_webserver() {
// Route for editing ChargerEndA // Route for editing ChargerEndA
server.on("/updateChargeEndA", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateChargeEndA", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
charger_setpoint_HV_IDC_END = value.toFloat(); charger_setpoint_HV_IDC_END = value.toFloat();
@ -245,6 +283,8 @@ void init_webserver() {
// Route for enabling/disabling HV charger // Route for enabling/disabling HV charger
server.on("/updateChargerHvEnabled", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateChargerHvEnabled", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
charger_HV_enabled = (bool)value.toInt(); charger_HV_enabled = (bool)value.toInt();
@ -256,6 +296,8 @@ void init_webserver() {
// Route for enabling/disabling aux12v charger // Route for enabling/disabling aux12v charger
server.on("/updateChargerAux12vEnabled", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/updateChargerAux12vEnabled", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
if (request->hasParam("value")) { if (request->hasParam("value")) {
String value = request->getParam("value")->value(); String value = request->getParam("value")->value();
charger_aux12V_enabled = (bool)value.toInt(); charger_aux12V_enabled = (bool)value.toInt();
@ -267,11 +309,16 @@ void init_webserver() {
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER #endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
// Send a GET request to <ESP_IP>/update // Send a GET request to <ESP_IP>/update
server.on("/debug", HTTP_GET, server.on("/debug", HTTP_GET, [](AsyncWebServerRequest* request) {
[](AsyncWebServerRequest* request) { request->send(200, "text/plain", "Debug: all OK."); }); if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
request->send(200, "text/plain", "Debug: all OK.");
});
// Route to handle reboot command // Route to handle reboot command
server.on("/reboot", HTTP_GET, [](AsyncWebServerRequest* request) { server.on("/reboot", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
return request->requestAuthentication();
request->send(200, "text/plain", "Rebooting server..."); request->send(200, "text/plain", "Rebooting server...");
//TODO: Should we handle contactors gracefully? Ifdef CONTACTOR_CONTROL then what? //TODO: Should we handle contactors gracefully? Ifdef CONTACTOR_CONTROL then what?
delay(1000); delay(1000);
@ -789,6 +836,8 @@ String processor(const String& var) {
content += "<button onclick='Events()'>Events</button>"; content += "<button onclick='Events()'>Events</button>";
content += " "; content += " ";
content += "<button onclick='askReboot()'>Reboot Emulator</button>"; content += "<button onclick='askReboot()'>Reboot Emulator</button>";
if (WEBSERVER_AUTH_REQUIRED)
content += "<button onclick='logout()'>Logout</button>";
content += "<script>"; content += "<script>";
content += "function OTA() { window.location.href = '/update'; }"; content += "function OTA() { window.location.href = '/update'; }";
content += "function Cellmon() { window.location.href = '/cellmonitor'; }"; content += "function Cellmon() { window.location.href = '/cellmonitor'; }";
@ -803,6 +852,15 @@ String processor(const String& var) {
content += " xhr.open('GET', '/reboot', true);"; content += " xhr.open('GET', '/reboot', true);";
content += " xhr.send();"; content += " xhr.send();";
content += "}"; content += "}";
if (WEBSERVER_AUTH_REQUIRED) {
content += "function logout() {";
content += " var xhr = new XMLHttpRequest();";
content += " xhr.open('GET', '/logout', true);";
content += " xhr.send();";
content += " setTimeout(function(){ window.open(\"/\",\"_self\"); }, 1000);";
content += "}";
}
content += "</script>"; content += "</script>";
//Script for refreshing page //Script for refreshing page

View file

@ -24,6 +24,8 @@ extern std::string password;
extern const uint8_t wifi_channel; extern const uint8_t wifi_channel;
extern const char* ssidAP; extern const char* ssidAP;
extern const char* passwordAP; extern const char* passwordAP;
extern const char* http_username;
extern const char* http_password;
// Common charger parameters // Common charger parameters
extern float charger_stat_HVcur; extern float charger_stat_HVcur;