Unprotected API endpoints that serve user-specific data or perform mutations are the canonical realization of OWASP A01:2021 (broken access control) and CWE-306 (missing authentication for critical function). The specific AI-generated antipattern — reading a userId from the request body rather than the verified session — maps to CWE-639 (authorization bypass through user-controlled key), enabling any authenticated user to read or mutate any other user's data by supplying a different ID. Frontend auth checks are irrelevant: API routes are directly callable via curl.
Critical because any unprotected endpoint exposing user data or mutations is directly exploitable without any authentication, enabling data theft or state corruption at zero friction.
Gate all non-public routes in src/middleware.ts with a route matcher, then derive the user identity exclusively from the verified session inside handlers — never from request body:
// app/api/users/me/route.ts
export async function GET(req: Request) {
const session = await getServerSession(authOptions)
if (!session) return new Response(null, { status: 401 })
// Use session.user.id — never req.body.userId or searchParams.get('userId')
const user = await db.user.findUnique({ where: { id: session.user.id } })
return apiSuccess(serializeUser(user))
}
Any handler that currently accepts a userId in the request body and queries with it is a horizontal privilege escalation vulnerability — replace those immediately with session-derived IDs.
saas-api-design.api-security.auth-required-non-publiccriticalgetServerSession(), auth(), verifyToken()). Also look for: routes that check auth but only return different data rather than returning 401/403 — these are often a design mistake where auth is present but not enforced.GET /api/user/profile accessible without auth. Name the specific unprotected routes (e.g., "GET /api/user/profile and GET /api/billing/invoices accessible without authentication; /api/admin routes have no auth check"). Max 500 chars.src/middleware.ts to all non-public routes. The safest pattern is to use middleware that runs before any route handler — in Next.js, use middleware.ts with a route matcher that gates your /api/ paths. Within handlers, always derive the user identity from the verified session/token (e.g., const { userId } = await getServerSession()) rather than trusting a user-supplied ID in the request body. If a handler receives a userId from the request body and uses it to look up data, replace that with the session-derived ID.