Architecture: How should we unify authentication, session security, and te...

Documentation for Sessions

🏗️ Architecture: How should we unify authentication, session security, and te...

Date: 2026-03-16
Time: 19:09:35 UTC
Session: gspace1-226.4
Agent: backend
Confidence: 88%
Status: ✅ Documented


📋 Decision Context

Question:

How should we unify authentication, session security, and tenant isolation across all 4 backends (backend-api, backend-ai-api, backend-vitto-api, backend-docs-api)?

Task: Design unified auth, session security, and tenant isolation for all 4 backends


🎯 Decision Made

Chosen:

Full APP_GUARD unification with phased rollout: (1) UnifiedAuthGuard registered as APP_GUARD on all backends (deny-by-default), (2) Dedicated /internal/* routes for service-to-service communication (Option B), (3) tenantId=spaceId formalized across all 8 session schemas, (4) Whistle hardening with proper auth + tenant isolation, (5) WebSocket auth guard, (6) SSE ticket-based auth pattern.

Options Considered:

  1. Add guards to 70 individual controllers one by one (no global guard)
  2. APP_GUARD on ai-api and vitto-api only, keep BackendAuthGuard on backend-api
  3. Full APP_GUARD on all backends with phased per-backend rollout (chosen)
  4. Dual-mode endpoints (same route serves users and services with different auth logic)

💭 Reasoning

Root cause: No backend registers a global guard. 70/150 controllers (47%) are completely unguarded. 5 distinct guard implementations set incompatible request.user shapes (sub vs userId). Whistle endpoints are @Public() with self-asserted caller identity. tenantId exists in schemas but is never populated. Inter-service tokens use inconsistent variable names.

Why full APP_GUARD over alternatives:

Why Option B (internal routes) over Option A (dual-mode endpoints):

Why tenantId=spaceId (not a separate tenant entity):

Critical de-risking: Rollback is a single-line revert of APP_GUARD registration. @Public() annotations are harmless without a global guard. Internal endpoints are additive (new controllers).

Key risks addressed: (1) SSE endpoints can't send headers → ticket-based auth pattern, (2) WhistleOrchestratorService.checkSpaceMembership() lacks auth headers → fixed before APP_GUARD goes live, (3) Mongoose pre-save hooks don't fire on updateMany → service-layer enforcement as primary mechanism, (4) X-Tenant-Id header only honored for service callers to prevent user-side tenant override.


📁 Files Involved


🔗 Related


Auto-generated by MCP Smart Consolidation