# Tenant creation is validated and rate-limited

- **Pattern:** `ab-002351` (`saas-multi-tenancy.tenant-management.tenant-creation-validated`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002351
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002351)

## Why it matters

Tenant creation endpoints with no rate limiting and application-only slug uniqueness checks are vulnerable to two distinct attacks: bulk tenant creation to exhaust system resources or claim desirable slug namespaces (CWE-770), and concurrent-request slug duplication that bypasses the select-then-insert uniqueness check (CWE-20, OWASP A04). In freemium SaaS products, unthrottled tenant creation is also a primary spam and abuse vector that inflates billing, corrupts benchmark data, and degrades performance for legitimate tenants.

## Severity rationale

Low because the impact is resource exhaustion and namespace pollution rather than direct data exposure, but slug duplication via race condition can corrupt routing and break tenant isolation.

## Remediation

Enforce slug uniqueness at the database level and add a per-user rate limit on tenant creation:

```prisma
// schema.prisma — database-enforced unique constraint
model Organization {
  slug String @unique
}
```

```ts
// src/app/api/orgs/route.ts — rate limit tenant creation per user
const ratelimit = new Ratelimit({
  redis,
  limiter: Ratelimit.fixedWindow(3, '1 d')
})
const { success } = await ratelimit.limit(`create-org:${session.user.id}`)
if (!success) return new Response('Too many organizations created today', { status: 429 })
```

The `@unique` constraint eliminates the race condition — the database rejects a duplicate slug with a constraint violation, which you catch and return as a 409 Conflict. Never rely on a prior SELECT check for uniqueness when concurrent inserts are possible.

## Detection

- **ID:** `tenant-creation-validated`
- **Severity:** `low`
- **What to look for:** Examine the tenant creation flow — the endpoint or form where a new organization/team/workspace is created. Check for: (a) input validation on tenant name and slug (length limits, character restrictions, profanity/reserved word checks), (b) uniqueness enforcement on slugs, (c) rate limiting on tenant creation per user account, and (d) any abuse prevention for bulk tenant creation.
- **Pass criteria:** Count all tenant creation paths. Tenant creation validates and sanitizes name and slug inputs with at least 1 validation rule. Slug uniqueness is enforced at the database level (unique index) not just application level. Tenant creation is rate-limited or requires email verification to prevent bulk abuse.
- **Fail criteria:** No validation on tenant name or slug beyond basic presence checks. Slug uniqueness enforced only in application code (not at DB level), creating a race condition. No rate limiting on tenant creation, allowing a single user to create unlimited tenants.
- **Skip (N/A) when:** No self-service tenant creation flow is detected (e.g., organizations are created only by platform admins). Signal: no organization/team creation form or API endpoint accessible to regular users.
- **Detail on fail:** Example: `"POST /api/orgs creates organizations with no rate limiting. Slug uniqueness check in src/app/api/orgs/route.ts uses a select-then-insert pattern without database unique constraint, allowing duplicate slugs under concurrent requests."`
- **Remediation:** Add database-level constraints and rate limiting:

  ```prisma
  // schema.prisma
  model Organization {
    slug String @unique  // database-enforced uniqueness
  }
  ```

  ```ts
  // Rate limit tenant creation per user
  const ratelimit = new Ratelimit({ redis, limiter: Ratelimit.fixedWindow(3, '1 d') })
  const { success } = await ratelimit.limit(`create-org:${session.user.id}`)
  if (!success) return new Response('Too many organizations created', { status: 429 })
  ```

---

## External references

- cwe CWE-770
- cwe CWE-20
- owasp A04

Taxons: injection-and-input-trust, cost-efficiency

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