# Every API Route Checks Permissions

- **Pattern:** `ab-002249` (`saas-authorization.resource-auth.every-api-checks-perms`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002249
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002249)

## Why it matters

A single unprotected API route or Server Action is a complete authorization bypass for any operation it performs. API routes in Next.js are public endpoints by default — authentication and authorization must be added explicitly. CWE-285 (Improper Authorization) and CWE-284 (Improper Access Control) are both in play when a route performs database writes without verifying who is asking. OWASP A01 (Broken Access Control) is the top web application risk category for this exact reason. NIST 800-53 AC-3 and AC-6 require access enforcement before any privileged operation executes. Server Actions receive the same treatment — they are callable from the browser and must not assume caller identity.

## Severity rationale

Critical because a single unprotected route exposes all of its data operations to unauthenticated callers, with no authentication barrier between the public internet and the database.

## Remediation

Every route handler that touches the database must begin with a session check. Treat every route as a public endpoint and require explicit proof of authorization before acting.

```typescript
// app/api/posts/route.ts
import { auth } from '@/lib/auth';

export async function GET(req: NextRequest) {
  const session = await auth();
  if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  // Now safe to query
  const posts = await db.post.findMany({ where: { userId: session.user.id } });
  return NextResponse.json(posts);
}
```

After fixing, audit completeness by grepping for database operations (`.findMany`, `.create`, `.update`, `.delete`) and confirming every one is preceded by a session check in its call chain.

## Detection

- **ID:** `every-api-checks-perms`
- **Severity:** `critical`
- **What to look for:** Count all relevant instances and enumerate each. Examine every route handler in `app/api/` (Next.js App Router `route.ts` files), `pages/api/` (Pages Router), and Server Actions (`"use server"` files). For each handler that performs any database read or write, check whether the handler retrieves and validates the current user's session before acting. Pay special attention to: POST/PUT/PATCH/DELETE handlers, handlers with dynamic segments (`[id]`, `[slug]`), and Server Actions that mutate data.
- **Pass criteria:** Every route handler that accesses or mutates data begins with a session check. At least 1 implementation must be verified. The handler returns a 401 or 403 before touching the database if the session is missing or insufficient.
- **Fail criteria:** Any route handler performs a database operation (query, insert, update, delete) without first verifying the current user's session and authorization. Even one unprotected route is a fail.
- **Skip (N/A) when:** Project has no API routes and no Server Actions (pure static site with no backend functionality).
- **Detail on fail:** `"Found N API route(s) or Server Action(s) that perform data operations without an auth check. Affected files: [list]."` (Always list the specific routes — this is critical.)
- **Remediation:** Every route handler must be treated as a public endpoint by default, requiring explicit proof of authorization before acting. In Next.js App Router:

  ```typescript
  // app/api/posts/route.ts
  import { auth } from '@/lib/auth';

  export async function GET(req: NextRequest) {
    const session = await auth();
    if (!session) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
    // Now safe to query
    const posts = await db.post.findMany({ where: { userId: session.user.id } });
    return NextResponse.json(posts);
  }
  ```

  For Server Actions, the same applies — they are callable from the browser and must not assume the caller is authorized.

  After fixing, verify by searching for database operations (`.findMany`, `.findUnique`, `.create`, `.update`, `.delete`) and confirming every one is preceded by a session check in the call chain.

## External references

- cwe CWE-284
- cwe CWE-285
- owasp A01
- nist AC-3
- nist AC-6

Taxons: access-control

HTML version: https://auditbuffet.com/patterns/ab-002249
