# Timeout configurable per org, min 15min

- **Pattern:** `ab-001463` (`finserv-session-security.session-lifecycle.timeout-configurable`)
- **Severity:** info
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001463
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001463)

## Why it matters

Different organizations within a multi-tenant financial platform have different risk tolerances and regulatory environments. A corporate treasury customer operating under SOC 2 Type II may be required to enforce shorter timeouts than a retail consumer. Hard-coding a global timeout at the application layer removes admin control and forces a binary choice: apply the strictest timeout everywhere and hurt UX, or apply a lenient one and fail to meet specific org-level compliance requirements. CWE-613 and PCI-DSS Req 8.2.8 do not preclude stricter controls — per-org configuration lets each tenant set a timeout at or below the maximum, enforcing the floor at the database level.

## Severity rationale

Info severity because a hard-coded global timeout is not inherently insecure if it meets the 15-minute minimum, but the absence of per-org configuration prevents compliance-driven customers from enforcing stricter controls required by their own policies.

## Remediation

Add an `org_settings` table with a database-enforced minimum constraint in `db/schema/` or `prisma/schema.prisma`:

```sql
CREATE TABLE org_settings (
  org_id UUID PRIMARY KEY REFERENCES organizations(id),
  session_timeout_minutes INT NOT NULL
    CHECK (session_timeout_minutes >= 15),  -- enforce floor
  updated_at TIMESTAMPTZ DEFAULT now()
);
```

Then read the org-specific value in session middleware before applying the timeout:

```js
async function getSessionTimeout(orgId: string): Promise<number> {
  const settings = await db.orgSettings.findUnique({ where: { org_id: orgId } });
  return (settings?.session_timeout_minutes ?? 30) * 60 * 1000; // ms
}
```

The CHECK constraint prevents any org admin from setting a timeout below 15 minutes, even via direct DB access.

## Detection

- **ID:** `timeout-configurable`
- **Severity:** `info`
- **What to look for:** Count all configuration mechanisms for session timeout (admin settings, database schema, environment variables). Look for a minimum constraint of at least 15 minutes. Quote the actual configuration table or settings structure found. Enumerate per-org vs. global settings.
- **Pass criteria:** At least 1 per-org configuration mechanism exists for session timeout, with a minimum enforced value of at least 15 minutes. Report the count even on pass (e.g., "1 org_settings table with session_timeout_minutes column, CHECK constraint >= 15").
- **Fail criteria:** Timeout is globally hard-coded with 0 per-org configuration options, or minimum allowed timeout is less than 15 minutes.
- **Skip (N/A) when:** The application is single-tenant (not multi-org) — cite the actual tenant model found.
- **Detail on fail:** `"Session timeout hard-coded to 30 minutes — 0 per-org configuration mechanisms found"` or `"Org config allows timeouts as low as 5 minutes — below 15-minute minimum"`
- **Remediation:** Add org-level timeout configuration (in `db/schema/` or `prisma/schema.prisma`):
  ```js
  // database schema
  CREATE TABLE org_settings (
    org_id UUID PRIMARY KEY,
    session_timeout_minutes INT CHECK (session_timeout_minutes >= 15),
    updated_at TIMESTAMP
  );

  // In session middleware
  async function getSessionTimeout(orgId: string) {
    const settings = await db.orgSettings.findUnique({ where: { org_id: orgId } });
    return settings?.session_timeout_minutes || 30; // default 30, min 15
  }
  ```

## External references

- cwe CWE-613
- pci-dss Req 8.2.8

Taxons: access-control

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