- proxy.ts: /ideas added to protectedRoutes — unauthenticated users get redirected to /login when navigating to /ideas or /ideas/[id] - existing demo-guard catch-all (\`/api/* + non-GET\`) already blocks POST/PATCH/DELETE /api/ideas* with 403 — confirmed via 3 new tests - server-action endpoints (start-grill / start-make-plan / materialize / promote-to-idea) carry their own \`session.isDemo\` checks inside actions/ideas.ts and actions/todos.ts (defense in depth) Tests: 9/9 in proxy demo-guard suite (added 3 idea cases). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import type { NextRequest } from 'next/server'
|
|
import { unsealData } from 'iron-session'
|
|
import { sessionOptions, type SessionData } from '@/lib/session'
|
|
|
|
const protectedRoutes = ['/dashboard', '/products', '/todos', '/ideas', '/settings', '/solo']
|
|
const authRoutes = ['/login', '/register']
|
|
|
|
const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS'])
|
|
|
|
// Paden die demo MAY aanroepen ook al zijn het non-GET — worden ingevuld na ST-1110.4
|
|
const DEMO_WRITE_ALLOWLIST = [
|
|
'/api/cron/', // machine-auth, irrelevant for demo
|
|
]
|
|
|
|
export async function proxy(request: NextRequest) {
|
|
const { pathname, method } = { pathname: request.nextUrl.pathname, method: request.method }
|
|
|
|
// Demo-guard: block non-GET API writes for demo users (defense in depth)
|
|
if (
|
|
pathname.startsWith('/api/') &&
|
|
!SAFE_METHODS.has(method) &&
|
|
!DEMO_WRITE_ALLOWLIST.some(p => pathname.startsWith(p))
|
|
) {
|
|
const raw = request.cookies.get(sessionOptions.cookieName)?.value
|
|
if (raw) {
|
|
const session = await unsealData<SessionData>(raw, { password: sessionOptions.password as string })
|
|
if (session.isDemo) {
|
|
return NextResponse.json(
|
|
{ error: 'Niet beschikbaar in demo-modus' },
|
|
{ status: 403 }
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Route protection: check cookie existence only — full validation in layout.tsx
|
|
const hasSession = !!request.cookies.get(sessionOptions.cookieName)?.value
|
|
const isProtected = protectedRoutes.some(r => pathname.startsWith(r))
|
|
const isAuthRoute = authRoutes.some(r => pathname.startsWith(r))
|
|
|
|
if (isProtected && !hasSession) {
|
|
return NextResponse.redirect(new URL('/login', request.url))
|
|
}
|
|
|
|
if (isAuthRoute && hasSession) {
|
|
return NextResponse.redirect(new URL('/dashboard', request.url))
|
|
}
|
|
|
|
return NextResponse.next()
|
|
}
|
|
|
|
export const config = {
|
|
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
}
|