Problem 1: Email Uniqueness
A global UNIQUE(email) constraint breaks when Alice uses the same email at two tenants. The correct constraint is UNIQUE(app_id, email) — email unique within a tenant, not globally.
CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), app_id VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, password_hash TEXT, UNIQUE(app_id, email) -- email unique WITHIN a tenant );
Problem 2: Tenant Context Resolution
Three sources with different trust levels: JWT claim (authoritative, cryptographically bound), request header (can be forged), subdomain (reliable but not proof of access). Priority chain: JWT claim > Header > Subdomain. When both JWT and header are present, optionally validate they match.
Problem 3: The requireTenant Middleware
Some endpoints must have tenant context. A GET /api/users without tenant scope would return users from every tenant — a catastrophic data leak. The requireTenant middleware rejects requests where tenant resolution failed and validates tenant match for authenticated users.
Problem 4: Cross-Tenant Data Leaks in SQL
The most dangerous bug: a missing WHERE clause. Mitigated with query helper functions that enforce app_id scoping, plus PostgreSQL Row-Level Security as a defense-in-depth backup.
Problem 5: Tenant-Scoped Rate Limiting
Rate limit keys include the tenant: userId:tenantId. A tenant with 10,000 active users does not eat into the rate limit headroom of a tenant with 10 users. Every rate limit key — SMS OTP, API, auth — includes the tenant context.
Problem 6: The Fused Middleware Approach
Five separate middleware functions (requestId, authenticate, tenantContext, requireTenant, rateLimiter) replaced with a single native Rust call via auth-shield. JWT verification, tenant resolution, tenant matching, rate limiting, and request ID generation all happen in 4.36 microseconds. Deployed behind a USE_RUST_AUTH=true feature flag.
Problem 7: Session Isolation
A JWT issued for Tenant X must not be valid for Tenant Y, even if the user has accounts in both. tenant_id is embedded in JWT claims at token creation. Refresh tokens are tenant-bound. Cross-tenant token reuse is rejected at the middleware layer.
Architecture Summary
| Component | Isolation Mechanism |
|---|---|
| User storage | UNIQUE(app_id, email) composite index |
| SQL queries | Mandatory app_id in WHERE + RLS backup |
| Rate limiting | userId:tenantId composite keys in DashMap |
| Sessions (JWT) | tenant_id embedded in JWT claims |
| Tenant resolution | Priority chain: JWT > Header > Subdomain |
| Middleware | Fused Rust call resolves tenant + auth in one step |