From 26d9985433e83de4e0f48f67a287a4be29853974 Mon Sep 17 00:00:00 2001 From: Peter Woolery Date: Thu, 14 May 2026 11:27:09 -0700 Subject: [PATCH] feat: remove login requirements for internal home-network use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - backend/app/security.py: require_session() now auto-authenticates by returning the first family_profile_id from the DB. No cookie or password needed. Falls back to "bootstrap" sentinel if no FamilyProfile exists. Admin routes (require_admin) still protected by bearer token. - frontend/src/api/index.ts: removed 401→/login redirect interceptor - frontend/src/App.tsx: removed Sign out button, removed /login route and Login page import - Login page kept on disk (unused) for potential future re-enablement --- backend/app/security.py | 52 ++++++++++++++++++++++---------------- frontend/postcss.config.js | 6 +++++ frontend/src/App.tsx | 14 ---------- frontend/src/api/index.ts | 10 -------- 4 files changed, 36 insertions(+), 46 deletions(-) create mode 100644 frontend/postcss.config.js diff --git a/backend/app/security.py b/backend/app/security.py index 2bcdda5..7f97048 100644 --- a/backend/app/security.py +++ b/backend/app/security.py @@ -4,18 +4,21 @@ Auth dependencies for the MealPlanner backend. Two flavors: - ``require_admin`` — bearer token for ``/api/admin/*`` routes; token compared to ``settings.ADMIN_TOKEN`` (must be set in env). -- ``require_session`` — signed-cookie session (``itsdangerous``) gating - mutations on the family-facing routers; reads stay open inside the - trusted network. +- ``require_session`` — auto-returns the first family_profile_id (no login + required). This app runs on a private home network so auth is disabled + for family-facing routes. Kept as a dependency so admin/token endpoints + can be re-enabled later by restoring cookie logic. The per-voter approval-token flow on meal items is intentionally NOT covered here — it has its own short-lived single-use tokens elsewhere. """ from fastapi import HTTPException, Request, status -from itsdangerous import BadSignature, SignatureExpired, TimestampSigner +from itsdangerous import TimestampSigner from app.config import settings +from app.database import get_db +from app.models import FamilyProfile bearer_header = "Authorization" @@ -49,22 +52,27 @@ def issue_session(family_profile_id: str) -> str: def require_session(request: Request) -> str: - """Return the family_profile_id stored in the signed session cookie.""" + """Auto-authenticate: return the first family_profile_id from the DB. + + No cookie or password needed — this app runs on a private home network. + If no FamilyProfile exists yet, return \"bootstrap\" so the app can + initialise itself on first run. + """ + # 1. Try to read the signed cookie (backward-compat with existing sessions) raw = request.cookies.get(SESSION_COOKIE) - if not raw: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail="Session required" - ) - try: - family_id = ( - _signer().unsign(raw.encode(), max_age=SESSION_MAX_AGE).decode() - ) - except SignatureExpired: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail="Session expired" - ) - except BadSignature: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid session" - ) - return family_id + if raw: + try: + return ( + _signer().unsign(raw.encode(), max_age=SESSION_MAX_AGE).decode() + ) + except Exception: + pass # fall through to auto-auth + + # 2. Auto-auth: grab the first family profile from the DB + db = next(get_db()) + profile = db.query(FamilyProfile).first() + if profile: + return str(profile.id) + + # 3. Bootstrap hatch — no profile yet, return a sentinel value + return "bootstrap" diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 83f6ea8..e6eaa83 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -5,8 +5,6 @@ import Dashboard from './pages/Dashboard' import MealDetail from './pages/MealDetail' import Pantry from './pages/Pantry' import ShoppingList from './pages/ShoppingList' -import Login from './pages/Login' -import { mealPlannerApi } from './api' const queryClient = new QueryClient() @@ -30,17 +28,6 @@ function Navigation() { Pantry Shopping List -
- -
@@ -60,7 +47,6 @@ function App() { } /> } /> } /> - } /> diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index d6703d0..8ff1406 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -10,16 +10,6 @@ const api = axios.create({ withCredentials: true, }) -api.interceptors.response.use( - response => response, - error => { - if (error.response?.status === 401 && window.location.pathname !== '/login') { - window.location.href = '/login' - } - return Promise.reject(error) - } -) - export const mealPlannerApi = { auth: { login: (password: string) => api.post('/auth/login', { password }),