From aa0bcf35fe8bc437c957ad7160083cd15ce6cad1 Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 17 Jun 2026 23:06:46 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20remove=20auth=20middleware=20=E2=80=94?= =?UTF-8?q?=20single-user=20pedal=20doesn't=20need=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auth was blocking all POST/PUT operations with 401. Disabled per user request. PIN generation kept (harmless). Frontend reverted to no-auth API helpers. --- frontend-react/src/App.jsx | 16 +++------- src/web/server.py | 65 ++------------------------------------ 2 files changed, 8 insertions(+), 73 deletions(-) diff --git a/frontend-react/src/App.jsx b/frontend-react/src/App.jsx index 20f2b2d..46f9cf4 100644 --- a/frontend-react/src/App.jsx +++ b/frontend-react/src/App.jsx @@ -37,12 +37,6 @@ function _ch(url) { } // ── API helpers ──────────────────────────────────────────────── -let _authPin = ''; - -function _authHeaders() { - return _authPin ? { 'X-Pedal-Auth': _authPin } : {}; -} - async function apiGet(path) { const r = await fetch(`${API}${_ch(path)}`); if (!r.ok) throw new Error(`${path} => ${r.status}`); @@ -51,7 +45,7 @@ async function apiGet(path) { async function apiPost(path, data) { const r = await fetch(`${API}${_ch(path)}`, { - method: 'POST', headers: { 'Content-Type': 'application/json', ..._authHeaders() }, + method: 'POST', headers: { 'Content-Type': 'application/json' }, body: data != null ? JSON.stringify(data) : undefined, }); if (!r.ok) throw new Error(`${path} => ${r.status}`); @@ -60,7 +54,7 @@ async function apiPost(path, data) { async function apiPut(path, data) { const r = await fetch(`${API}${_ch(path)}`, { - method: 'PUT', headers: { 'Content-Type': 'application/json', ..._authHeaders() }, + method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); if (!r.ok) throw new Error(`${path} => ${r.status}`); @@ -68,7 +62,7 @@ async function apiPut(path, data) { } async function apiDelete(path) { - const r = await fetch(`${API}${_ch(path)}`, { method: 'DELETE', headers: _authHeaders() }); + const r = await fetch(`${API}${_ch(path)}`, { method: 'DELETE' }); if (!r.ok) throw new Error(`${path} => ${r.status}`); return r.json(); } @@ -76,7 +70,7 @@ async function apiDelete(path) { async function apiUpload(path, file) { const fd = new FormData(); fd.append('file', file); - const r = await fetch(`${API}${_ch(path)}`, { method: 'POST', body: fd, headers: _authHeaders() }); + const r = await fetch(`${API}${_ch(path)}`, { method: 'POST', body: fd }); if (!r.ok) throw new Error(`${path} => ${r.status}`); return r.json(); } @@ -90,7 +84,7 @@ function usePedalState() { useEffect(() => { // Fetch initial state - apiGet('/api/state').then(s => { setState(s); setConnected(true); _authPin = s.auth_pin || ''; }).catch(() => { setConnected(false); }); + apiGet('/api/state').then(s => { setState(s); setConnected(true); }).catch(() => { setConnected(false); }); // WebSocket for real-time updates let reconnectTimer; diff --git a/src/web/server.py b/src/web/server.py index 8a8dc11..62d34e7 100644 --- a/src/web/server.py +++ b/src/web/server.py @@ -444,67 +444,9 @@ class WebServer: request.method, request.url.path, exc, exc_info=True) return JSONResponse(status_code=500, content={"detail": "Internal server error"}) - # ── Auth middleware ────────────────────────────────────────────── - # All /api/ endpoints require X-Pedal-Auth: header, EXCEPT - # read-only GET endpoints that the UI needs on first load. - # The PIN is set at first boot in config.yaml under web.auth_pin. - - AUTH_SAFE_PREFIXES = ( - # Read-only endpoints the UI needs for basic operation - # (none of these expose WiFi/BT credentials or settings) - "/api/state", - "/api/presets", - "/api/snapshots", - "/api/models", - "/api/irs", - "/api/audio/profile", - "/api/audio/profiles", - "/api/audio/devices", - "/api/block-params", - "/api/levels", - "/api/tuner/pitch", - "/api/channel-output", - "/api/channel-mode", - "/api/routing", - "/api/system", - "/api/network", - "/api/backing-source", - "/api/midi/mappings", - "/api/nam/engine", - "/api/tonedownload/status", - "/api/models/tonedownload/search", - "/api/irs/tonedownload/search", - "/api/fx/params", - "/api/bluetooth/midi-status", - "/api/bluetooth/midi-devices", - ) - - @app.middleware("http") - async def auth_middleware(request: Request, call_next): - path = request.url.path - - # Static files and the main UI are always public - if path == "/" or path.startswith("/static/"): - return await call_next(request) - - # Legacy redirect routes are public - if path in ("/presets", "/models", "/irs", "/settings"): - return await call_next(request) - - # Enforce auth on all /api/ endpoints except safe GET prefixes - if path.startswith("/api/"): - # Safe GET endpoints — read-only, no sensitive data - if request.method == "GET" and any(path.startswith(p) for p in AUTH_SAFE_PREFIXES): - return await call_next(request) - - pin = self._get_auth_pin() - if pin is not None: - auth = request.headers.get("X-Pedal-Auth", "") - if auth != pin: - logger.warning("Unauthorized request to %s %s", request.method, path) - return JSONResponse(status_code=401, content={"detail": "Unauthorized"}) - - return await call_next(request) + # ── Auth — disabled per user request (single-user stage pedal) ── + # Auth middleware removed. PIN generation still runs (harmless). + # Remove the X-Pedal-Auth wiring from frontend-react/src/App.jsx too. # Mount static files app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static") @@ -2910,7 +2852,6 @@ iface eth0 inet static "bluetooth": self._gather_bt_state(), "current_snapshot": getattr(pm, '_current_snapshot', 0) if pm else 0, "needs_reboot": self._needs_reboot, - "auth_pin": self._get_auth_pin() or "", } def _gather_state(self, channel: Optional[str] = None) -> dict[str, Any]: