Problem
The HTTP server (port 80, web_handlers.c) currently requires no authentication. Anyone with LAN access can, unauthenticated:
- Flash arbitrary firmware via
POST /update (full device takeover)
- Change WiFi credentials (
/provision) and MQTT broker/creds (/mqtt_config)
- Reboot the device (each config POST calls
esp_restart())
- Inject arbitrary frames onto the pool bus via
POST /api/test_decode
All POST endpoints are also CSRF-able (no token, no Origin/Referer check), so a malicious page the owner visits could drive these against the device's known IP.
This issue tracks adding auth to the HTTP control plane. (TCP bridge on 7373 is tracked separately — it needs its own scheme and depends on this landing first.)
Authentication mechanisms available
ESP-IDF's esp_http_server has no built-in auth — it's enforced in handlers (typically a wrapper run before the real handler).
| Mechanism |
How it works |
Notes |
| HTTP Basic |
Authorization: Basic base64(user:pass); decode + compare, 401 + WWW-Authenticate if missing |
Smallest change (~40 lines), native browser dialog, zero client code. Credentials are base64 (not encrypted) → needs HTTPS or accept LAN-only exposure. |
| HTTP Digest |
Challenge/response, password not sent in clear |
MD5-based, no IDF helper, fiddly nonce state — poor effort/value. Skip. |
| Form + session cookie |
Login page → random token (esp_random()), Set-Cookie: HttpOnly, validate per request from in-RAM session table |
Nicer UX (logout/expiry), more code + RAM state. Still wants HTTPS. |
| API token / Bearer |
Pre-shared token in a header |
Good for automation/scripts, not browser-friendly. Could complement Basic. |
| TLS client certs |
Mutual TLS |
Overkill / unusable for a household web UI. |
Transit protection (any of the above) means moving from httpd to esp_https_server (same API, TLS via mbedTLS, server cert in NVS/flash).
Suggested default: HTTP Basic now (gates every mutating endpoint + /update), HTTPS as a follow-up.
Setting the initial password (bootstrap)
Principle: never ship a fixed default password (same mistake as the hardcoded poolsetup SoftAP password).
Recommended: set the admin password during WiFi provisioning, in the existing SoftAP captive portal.
- During first-run the device is only on its own WPA2 SoftAP — that AP is the trusted bootstrap channel.
- Add an "Admin password" field to the
/wifi provisioning form.
- On save, store a salted hash in NVS (same pattern as
wifi_credentials_save / mqtt_save_config), reboot into station mode.
- Once on the real LAN, require auth on HTTP endpoints. The only no-password window is while physically connected to the device's own AP.
Alternatives considered: per-device random password (awkward for a board with no sticker/screen), trust-on-first-use (loses to whoever connects first), known-default + forced change (still has a guessable window).
Storage & verification
- Store a salted hash (mbedTLS PBKDF2 or salted SHA-256), never plaintext.
- Constant-time comparison.
- Ideally enable flash encryption so the NVS blob isn't readable off-chip (ties in with the Secure Boot / signed-OTA work).
Reset path
Forgotten-password recovery must be physical: add a factory-reset trigger (long-press boot/GPIO button, or N rapid power cycles) that clears the admin hash + WiFi creds and drops back into provisioning. Also the "bought it secondhand" story.
Key decisions to make
- Mechanism: Basic vs session-cookie? (recommend Basic to start)
- Transport: plain HTTP on LAN, or move to
esp_https_server (HTTPS)?
- Initial password: set during provisioning (recommended) vs per-device random vs other?
- Hashing: PBKDF2 vs salted SHA-256; iteration count.
- Flash encryption: enable now or defer?
- Factory reset: which physical trigger (button long-press vs power-cycle count)?
- CSRF: rely on auth +
Origin/Referer checks, or add tokens to POST forms?
- Scope: auth all endpoints, or leave read-only
/status open?
Scope / dependencies
- This is a prerequisite for the TCP bridge (7373) hardening — the bridge token must be displayed on an authenticated config page.
- Does not replace signed OTA (signing ensures only your images run; auth/anti-rollback stops unauthorized triggering and downgrade).
Problem
The HTTP server (port 80,
web_handlers.c) currently requires no authentication. Anyone with LAN access can, unauthenticated:POST /update(full device takeover)/provision) and MQTT broker/creds (/mqtt_config)esp_restart())POST /api/test_decodeAll POST endpoints are also CSRF-able (no token, no
Origin/Referercheck), so a malicious page the owner visits could drive these against the device's known IP.This issue tracks adding auth to the HTTP control plane. (TCP bridge on 7373 is tracked separately — it needs its own scheme and depends on this landing first.)
Authentication mechanisms available
ESP-IDF's
esp_http_serverhas no built-in auth — it's enforced in handlers (typically a wrapper run before the real handler).Authorization: Basic base64(user:pass); decode + compare,401 + WWW-Authenticateif missingesp_random()),Set-Cookie: HttpOnly, validate per request from in-RAM session tableTransit protection (any of the above) means moving from
httpdtoesp_https_server(same API, TLS via mbedTLS, server cert in NVS/flash).Suggested default: HTTP Basic now (gates every mutating endpoint +
/update), HTTPS as a follow-up.Setting the initial password (bootstrap)
Principle: never ship a fixed default password (same mistake as the hardcoded
poolsetupSoftAP password).Recommended: set the admin password during WiFi provisioning, in the existing SoftAP captive portal.
/wifiprovisioning form.wifi_credentials_save/mqtt_save_config), reboot into station mode.Alternatives considered: per-device random password (awkward for a board with no sticker/screen), trust-on-first-use (loses to whoever connects first), known-default + forced change (still has a guessable window).
Storage & verification
Reset path
Forgotten-password recovery must be physical: add a factory-reset trigger (long-press boot/GPIO button, or N rapid power cycles) that clears the admin hash + WiFi creds and drops back into provisioning. Also the "bought it secondhand" story.
Key decisions to make
esp_https_server(HTTPS)?Origin/Refererchecks, or add tokens to POST forms?/statusopen?Scope / dependencies