# Session-getter failures return early without executing protected logic

- **Pattern:** `ab-000285` (`ai-slop-security-theater.unbound-auth.session-getter-failures-return-early`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000285
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000285)

## Why it matters

Calling `const session = await auth()` and then immediately proceeding to render data or execute logic without checking `if (!session)` means the code path runs identically whether the user is authenticated or not. CWE-284 (Improper Access Control) and OWASP A01 apply: a null session is not an error, it's the expected return value when no user is logged in. The absence of a guard after the session call is functionally equivalent to having no auth check at all — the protected logic executes unconditionally.

## Severity rationale

High because an unguarded session call allows the protected logic to execute when the session is null, bypassing authentication on any route where this pattern appears.

## Remediation

Add a null-check guard within the first few lines after the session call. The guard must redirect or return before any business logic runs.

```ts
// Bad: session called but result ignored
const session = await auth()
const users = await prisma.user.findMany()  // executes even when session is null

// Good: guard immediately
const session = await auth()
if (!session?.user) {
  redirect('/login')
  // or for API routes:
  // return Response.json({ error: 'Unauthorized' }, { status: 401 })
}
const users = await prisma.user.findMany()
```

The pattern `if (!session?.user) redirect('/login')` is idiomatic for Next.js App Router server components. For API routes, use an early `return Response.json(...)` with status 401.

## Detection

- **ID:** `session-getter-failures-return-early`
- **Severity:** `high`
- **What to look for:** For each file that calls a session getter, find the call and check the next 5 lines of source code below the call. Count all session-getter call sites and verify at least 1 of these guard patterns appears within those 5 lines: `if (!session) return`, `if (!session?.user) return`, `if (!user) return`, `if (!session) throw`, `if (!user) throw`, `if (!session) redirect(`, `if (!user) redirect(`, `if (!session) notFound()`, `if (session === null) ...`. Count all files where the session getter is called but no guard follows it.
- **Pass criteria:** 100% of files calling a session getter have a null-check guard within 5 lines after the call. Report: "X session-getter call sites, Y guarded immediately, 0 unguarded."
- **Fail criteria:** At least 1 session-getter call has no guard pattern within 5 lines below it — the protected logic runs even when the session is null.
- **Skip (N/A) when:** No file in the project calls a session getter function.
- **Detail on fail:** `"1 unguarded session call: app/admin/page.tsx line 12 calls await auth() but never checks the result before rendering admin data on line 15"`
- **Remediation:** Call the session getter and immediately check the result. Don't trust that the session "must be present":

  ```ts
  // Bad: session call without guard
  const session = await auth()
  const users = await prisma.user.findMany()  // runs even if session is null

  // Good: guard immediately after the call
  const session = await auth()
  if (!session?.user) {
    redirect('/login')  // or return Response.json({...}, { status: 401 })
  }
  const users = await prisma.user.findMany()
  ```

## External references

- cwe CWE-284
- cwe CWE-754
- owasp A01
- owasp A07

Taxons: access-control

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