A dashboard or admin route that lacks an auth check renders fine when the user is logged in and renders fine when the user is logged out — and when it's logged out, it's serving your authenticated UI to the open internet. Direct navigation to /dashboard/settings or /admin/users returns the page HTML; any data-fetching inside runs against the anon session and leaks whatever the API returns when there's no user. AI coding tools tend to scaffold protected page files based on a route name prompt without wiring them to a middleware.ts matcher or adding a layout-level session check, because the visual rendering works in dev where the developer is already signed in. The usual failure shape is a new page added months after the initial auth plumbing, skipped by both the middleware matcher and the nearest protected layout.
Critical because an unguarded protected route is directly navigable by any unauthenticated user and serves authenticated UI plus any server-side data the page fetches.
Add a layout-level guard:
// app/dashboard/layout.tsx
import { redirect } from 'next/navigation'
import { getUser } from '@/lib/auth'
export default async function Layout({ children }) {
const user = await getUser()
if (!user) redirect('/auth/login')
return <>{children}</>
}
Deeper remediation guidance and cross-reference coverage for this check lives in the saas-authentication Pro audit — run that after applying this fix for a more exhaustive pass on the same topic.
project-snapshot.auth-access.protected-routes-have-guardscriticalapp/(app)/, app/(authenticated)/, app/dashboard/*, app/admin/*, app/account/*, pages/dashboard/*. For each, check whether the file or its layout calls a session getter and redirects/throws when unauthenticated. Also check middleware.ts for matcher patterns covering these routes.middleware.ts's matcher with auth logic."No app/(app), app/dashboard, app/admin, or equivalent protected-style routes detected."getUser() helper but doesn't conditionally redirect — fetching the user without acting on null is not a guard."Found N protected-style route files; all N covered by auth guards (middleware/layout/page-level).""app/dashboard/settings/page.tsx renders without checking session; not covered by middleware matcher".// app/dashboard/layout.tsx
import { redirect } from 'next/navigation'
import { getUser } from '@/lib/auth'
export default async function Layout({ children }) {
const user = await getUser()
if (!user) redirect('/auth/login')
return <>{children}</>
}