# Logout invalidates the server-side session

- **Pattern:** `ab-002233` (`saas-authentication.auth-flow.logout-invalidates-session`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002233
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002233)

## Why it matters

Clearing a session cookie on the client has no effect on a session token that was already captured — by an XSS payload, a network sniff, or a shared machine where the token was read from memory. CWE-613 (Insufficient Session Expiration) applies when logout does not invalidate the server-side session record. NIST 800-63B §7.1 (Session Bindings) requires that session termination be enforced server-side, not only client-side. OWASP A07 (Identification and Authentication Failures) lists insufficient logout among its named failure modes. The server must forget the session; the cookie clear is only a UX courtesy.

## Severity rationale

High because client-only logout leaves a captured session token valid until its natural expiry, giving attackers continued access even after the legitimate user has explicitly logged out.

## Remediation

Delete the session record on the server in the same response that clears the cookie:

```ts
// src/app/api/auth/logout/route.ts
export async function POST(request: Request) {
  const sessionToken = getSessionTokenFromRequest(request)
  if (sessionToken) {
    await db.session.delete({ where: { token: sessionToken } })
  }
  const response = Response.json({ ok: true })
  response.headers.set('Set-Cookie',
    'session=; Max-Age=0; Path=/; HttpOnly; Secure; SameSite=Lax'
  )
  return response
}
```

For stateless JWT access tokens, add the token to a short-lived Redis blocklist on logout, or keep access token lifetime at 5–15 minutes so the window of post-logout validity is minimal.

## Detection

- **ID:** `logout-invalidates-session`
- **Severity:** `high`
- **What to look for:** Examine the logout handler (typically `POST /api/auth/signout` or similar). Verify it not only clears the client-side cookie but also invalidates the session server-side. For database sessions: check that the session record is deleted from the sessions table. For JWT-only (stateless) sessions: there is no server-side invalidation mechanism — check if a token blocklist/denylist is maintained. Managed auth providers handle this correctly. Count all instances found and enumerate each.
- **Pass criteria:** Logout deletes or invalidates the server-side session record. Client-side cookie is also cleared. For JWT-only systems, either short token lifetime (< 15 minutes) compensates for the inability to revoke, or a blocklist is maintained. At least 1 implementation must be confirmed.
- **Fail criteria:** Logout only clears the client-side cookie without invalidating the server-side session — the session token remains valid and could be reused if the cookie value was captured.
- **Skip (N/A) when:** Fully managed auth provider (NextAuth, Clerk, Supabase Auth) used with default signout — they handle server-side invalidation. Signal: logout calls the provider's signout method directly.
- **Detail on fail:** `"Logout handler at /api/auth/logout clears the session cookie but does not delete the session record from the database — stolen tokens remain valid until expiry"`.
- **Remediation:** Clearing a cookie doesn't prevent someone who already captured the cookie value from using it. The server must forget the session:

  ```ts
  // Database session approach
  export async function POST(request: Request) {
    const sessionToken = getSessionTokenFromRequest(request)
    if (sessionToken) {
      await db.session.delete({ where: { token: sessionToken } })
    }
    const response = Response.json({ ok: true })
    response.headers.set('Set-Cookie',
      'session=; Max-Age=0; Path=/; HttpOnly; Secure; SameSite=Lax'
    )
    return response
  }
  ```

  For JWT-based systems, add tokens to a short-lived blocklist (Redis) on logout if immediate invalidation is needed. Otherwise, keep access token lifetime very short (5-15 minutes).

## External references

- cwe CWE-613
- owasp A07
- nist SP-800-63B §7.1

Taxons: access-control

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