# No hardcoded user IDs in handler logic

- **Pattern:** `ab-000262` (`ai-slop-half-finished.hardcoded-test-data.hardcoded-user-id-in-handlers`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000262
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000262)

## Why it matters

Hardcoded user IDs in handlers mean the application is operating as a single-user system dressed up as multi-user software. Every POST, PATCH, or DELETE runs against the demo account's data regardless of which user is authenticated — or whether anyone is authenticated at all. This violates OWASP A01:2021 (Broken Access Control) and CWE-284 (Improper Access Control): the authorization decision has been reduced to a constant. Real users create data they cannot see, and the demo user accumulates all writes silently.

## Severity rationale

Medium because exploitation requires understanding the application's data model, but any authenticated user can corrupt another user's data or read the demo account's accumulated records.

## Remediation

Pull `userId` from the authenticated session, not a constant. In every API handler that touches user-scoped data, replace the hardcoded ID with a session lookup:

```ts
// Remove this pattern:
const userId = 'demo-user-id'

// Replace with:
import { auth } from '@/lib/auth'

export async function POST(req: Request) {
  const session = await auth()
  if (!session?.user?.id) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }
  const userId = session.user.id
  // proceed with real userId
}
```

Search `src/app/api/` for string literals matching UUID patterns starting with `0000`, `1111`, or the literal strings `demo-user-id`, `test-user-1`, `user-1`, and replace every occurrence.

## Detection

- **ID:** `hardcoded-user-id-in-handlers`
- **Severity:** `medium`
- **What to look for:** Walk all API handler files for hardcoded user identifier constants used in data operations. Count all string literals that match these patterns AND are used as arguments to database `where`, `findUnique`, `findFirst`, `update`, `delete`, or `connect` clauses: `"demo-user-id"`, `"test-user-1"`, `"user-1"`, `"admin-user"`, `"admin-id"`, `"00000000-0000-0000-0000-000000000000"` (nil UUID), `"user_demo"`, UUIDs starting with `"11111111-"` or `"00000000-"`. Also count assignments like `const userId = "demo-user-id"` in handlers.
- **Pass criteria:** 0 hardcoded user ID constants are used in handler data operations. Report: "Scanned X API handlers, 0 hardcoded user IDs in data queries."
- **Fail criteria:** At least 1 API handler uses a hardcoded user ID string in a database query or data operation.
- **Do NOT pass when:** The hardcoded ID is only used as a fallback when session lookup fails (still a backdoor into another user's data).
- **Skip (N/A) when:** Project has 0 API handler files.
- **Detail on fail:** `"1 hardcoded user ID: 'const userId = \"demo-user-id\"' in app/api/posts/route.ts line 8, used in prisma.post.create({ data: { authorId: userId } })"`
- **Remediation:** Hardcoded user IDs mean every user's data gets created under the demo account. Pull the user ID from the actual session:

  ```ts
  // Bad: all posts created under demo user
  export async function POST(req: Request) {
    const userId = "demo-user-id"
    const post = await prisma.post.create({ data: { ...body, authorId: userId } })
  }

  // Good: use the authenticated session
  import { auth } from '@/lib/auth'
  export async function POST(req: Request) {
    const session = await auth()
    if (!session?.user) {
      return Response.json({ error: 'Unauthorized' }, { status: 401 })
    }
    const post = await prisma.post.create({ data: { ...body, authorId: session.user.id } })
  }
  ```

## External references

- cwe CWE-284
- owasp A01

Taxons: placeholder-hygiene, access-control

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