Public Access
feat(auth): harden sessions + HA Ingress support
- backend: settings SESSION_COOKIE_SECURE + TRUSTED_NETWORK_AUTO_AUTH, require_session uses secrets.compare_digest and respects trusted-network opt-in, main.py adds require_family_session middleware gating all /api/ routes except auth/admin/email-vote-token paths - docker-compose: pass SESSION_COOKIE_SECURE + TRUSTED_NETWORK_AUTO_AUTH through to backend + scheduler (fixes env-file changes not reaching runtime) - frontend: Ingress path-prefix support (APP_BASE_PATH, BrowserRouter basename, vite base './'), Login redirect honors APP_BASE_PATH - nginx: no-cache headers on root + /assets/ - docs: Home Assistant Ingress install/troubleshooting + plan file - tests: test_auth expects 401 on no-session GET Defaults: SESSION_COOKIE_SECURE=false, TRUSTED_NETWORK_AUTO_AUTH=true (HA is the auth boundary; MealPlanner must not be port-forwarded directly).
This commit is contained in:
@@ -6,6 +6,7 @@ import { OnboardingTour, useOnboarding } from './components/OnboardingTour'
|
||||
import { showApiError } from './lib/toast'
|
||||
import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'
|
||||
import { requestFocusSearch } from './hooks/useFocusSearch'
|
||||
import { APP_BASE_PATH } from './api'
|
||||
import Dashboard from './pages/Dashboard'
|
||||
import MealDetail from './pages/MealDetail'
|
||||
import Pantry from './pages/Pantry'
|
||||
@@ -81,7 +82,7 @@ function App() {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<BrowserRouter>
|
||||
<BrowserRouter basename={APP_BASE_PATH || undefined}>
|
||||
<GlobalShortcuts />
|
||||
<div className="min-h-screen bg-surface-50">
|
||||
<Navigation />
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_URL || '/api'
|
||||
export function getIngressBasePath() {
|
||||
const parts = window.location.pathname.split('/').filter(Boolean)
|
||||
if (parts[0] === 'api' && parts[1] === 'hassio_ingress' && parts[2]) {
|
||||
return `/${parts.slice(0, 3).join('/')}`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export const APP_BASE_PATH = getIngressBasePath()
|
||||
const API_BASE = import.meta.env.VITE_API_URL || `${APP_BASE_PATH}/api`
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: API_BASE,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useState } from 'react'
|
||||
import { Lock, ArrowRight } from 'lucide-react'
|
||||
import { mealPlannerApi } from '../api'
|
||||
import { APP_BASE_PATH, mealPlannerApi } from '../api'
|
||||
import { Button } from '../components/ui/Button'
|
||||
import { Card, CardBody } from '../components/ui/Card'
|
||||
import { Input } from '../components/ui/Input'
|
||||
@@ -16,7 +16,7 @@ export default function Login() {
|
||||
setLoading(true)
|
||||
try {
|
||||
await mealPlannerApi.auth.login(password)
|
||||
window.location.href = '/'
|
||||
window.location.href = `${APP_BASE_PATH}/`
|
||||
} catch {
|
||||
setError('Incorrect password. Try again.')
|
||||
} finally {
|
||||
|
||||
@@ -3,6 +3,7 @@ import react from '@vitejs/plugin-react'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
base: './',
|
||||
server: {
|
||||
port: 3000,
|
||||
proxy: {
|
||||
|
||||
Reference in New Issue
Block a user