Files
familysync/apps/api/src/index.ts
T
Lucas Berger 4b34b16f02 fix(02): dev-auth bypass no longer blocked by oidcAuthMiddleware
- index.ts: compute devBypassActive at startup; skip app.use(oidcAuthMiddleware)
  entirely when active so the OIDC guard never runs in local dev
- routes/me.ts: read c.get('user') first; return dev identity directly when
  devAuthBypass injected it, bypassing getAuth() and the DB upsert
- auth/devBypass.ts: add ContextVariableMap augmentation for 'user' key;
  correct stale comment that claimed getAuth/401 path was still active
2026-06-05 13:48:00 -04:00

66 lines
2.9 KiB
TypeScript

import { serve } from '@hono/node-server'
import { serveStatic } from '@hono/node-server/serve-static'
import { Hono } from 'hono'
import { healthRouter } from './routes/health.js'
import { meRouter } from './routes/me.js'
import { eventsRouter } from './routes/events.js'
import { sseRouter } from './routes/sse.js'
import { oidcAuthMiddleware, processOAuthCallback } from './auth/middleware.js'
import { devAuthBypass } from './auth/devBypass.js'
import { startBrokerPoller } from './broker/poller.js'
export const app = new Hono()
// Compute once at startup: bypass is active only in non-production with explicit opt-in.
// In production NODE_ENV='production' → devBypassActive=false → OIDC is always mounted.
const devBypassActive =
process.env.NODE_ENV !== 'production' && process.env.DEV_AUTH_BYPASS === 'true'
if (devBypassActive) {
console.warn(
'⚠ DEV_AUTH_BYPASS active — OIDC guard DISABLED. Never use in production.',
)
}
// OIDC callback — must be registered BEFORE oidcAuthMiddleware so the
// authorization-code exchange is not itself intercepted by the auth check (T-02-02)
app.get('/callback', (c) => processOAuthCallback(c))
// GET /health — unauthenticated, mounted BEFORE the OIDC guard (T-01-03, T-02-05)
app.route('/health', healthRouter)
// Dev-auth bypass — no-op passthrough unless DEV_AUTH_BYPASS=true AND NODE_ENV!='production'.
// When active, injects DEV_USER into the Hono context and the OIDC guard is NOT mounted.
// Must be mounted BEFORE oidcAuthMiddleware (T-02-01 mitigation; see auth/devBypass.ts).
app.use('/api/*', devAuthBypass())
// Protect all /api/* routes with OIDC session middleware (AUTH-01, T-02-05).
// Skipped entirely when devBypassActive so that local dev works without Authelia.
// In production devBypassActive is always false — OIDC is unconditionally mounted.
// Unauthenticated requests receive a 302 redirect to Authelia's authorize endpoint.
// OIDC_AUTH_EXTERNAL_URL is MANDATORY behind Pangolin to construct the correct
// redirect_uri (Pitfall 1). Set it to https://familysync.<domain>.
if (!devBypassActive) {
app.use('/api/*', oidcAuthMiddleware())
}
// Protected API routes (behind oidcAuthMiddleware)
app.route('/api/me', meRouter)
app.route('/api/events', eventsRouter)
app.route('/api/sse', sseRouter)
// Start the CalDAV broker poller (5-min cron, D-13 ctag change-detection)
// Runs in the background — errors are caught and logged per-credential (T-03-04)
startBrokerPoller()
// Serve React PWA static assets from ./public (Vite build output)
app.use('/assets/*', serveStatic({ root: './public' }))
app.get('*', serveStatic({ path: './public/index.html' }))
// Only start the HTTP server when this module is run directly (not imported in tests)
if (process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/^.*\//, ''))) {
serve({ fetch: app.fetch, port: 3000 }, (info) => {
console.log(`FamilySync API running on http://localhost:${info.port}`)
})
}