feat(web): optional web login and API tokens, off by default (stacked on #674) (#683)

Optional web login, off by default: a device that sets no password behaves
exactly as before. Set under General > Security; then every page and API
route needs a session login or an API token (Authorization: Bearer).
Loopback, the Wi-Fi setup flow in AP mode, static files, captive-portal
probes and a reduced /api/v3/health stay open. Secrets live in the web_auth
section of config_secrets.json and no API returns them.
scripts/reset_web_password.py turns login off. Stacked on #674.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-30 09:09:40 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent ba38a83c2c
commit e3c85cece6
23 changed files with 2310 additions and 26 deletions
+17 -2
View File
@@ -61,8 +61,23 @@ Out of scope (please report upstream):
LEDMatrix is designed for trusted local networks. Several limitations
are intentional rather than vulnerabilities:
- **No web UI authentication.** The web interface assumes the network
it's running on is trusted. Don't expose port 5000 to the internet.
- **Web UI authentication is optional and off by default.** Out of the
box the web interface assumes the network it's running on is trusted.
Setting a password under **General > Security** makes every page and
API route require a login or an API token (`Authorization: Bearer`),
with wrong passwords rate-limited per address
(`web_interface/auth.py`). Deliberately left open even then: requests
from the Pi itself (loopback without proxy headers; a reverse proxy on
the Pi must add `X-Forwarded-For`, or every request it relays counts as
local), the Wi-Fi setup flow while the Pi is in access-point mode,
static files, and a status-only `/api/v3/health`. The password is a
werkzeug hash and tokens are stored as SHA-256, in
`config/config_secrets.json`, which no API returns. There is no TLS:
over plain HTTP the password and tokens cross the LAN in the clear, so
still don't expose port 5000 to the internet; put a TLS reverse proxy
or a VPN in front for remote access. Anyone with shell access to the Pi
can turn login off (`scripts/reset_web_password.py`), which is the
documented recovery path.
"Trusted network" does not mean "trusted websites", though: any page
a LAN user opens could make their browser POST to the Pi. So the
interface refuses a `POST`/`PUT`/`PATCH`/`DELETE` whose `Origin` (or