4.2 KiB
4.2 KiB
Multi-Tenant Same-Key End-to-End Flow Sequences
Date: 2026-02-22
Source sprint: SPRINT_20260222_053_DOCS_multi_tenant_same_api_key_contract_baseline.md
Related ADR: docs/architecture/decisions/ADR-002-multi-tenant-same-api-key-selection.md
1) Sign-in to Tenant Mapping
sequenceDiagram
autonumber
participant User
participant Web as Web Console
participant Auth as Authority
participant Gw as Router/Gateway
participant Svc as Tenant-scoped API
User->>Web: Sign in
Web->>Auth: /connect/authorize + PKCE
Auth-->>Web: auth code
Web->>Auth: /connect/token (client credentials or password grant, tenant=<optional>)
Auth->>Auth: Resolve selected tenant from tenant + tenants metadata
Auth-->>Web: Access token (stellaops:tenant + optional stellaops:allowed_tenants)
Web->>Auth: /console/tenants
Auth-->>Web: { tenants[], selectedTenant }
Web->>Web: Hydrate ConsoleSessionStore + AuthSessionStore + PlatformContext
Web->>Gw: API request + canonical tenant header
Gw->>Svc: Forward resolved tenant context
Svc-->>Web: Tenant-scoped response
Deterministic selection rule:
- If
tenantparameter is present at token request time, it must be in assigned tenant set. - If no parameter and only one assignment/default exists, use that selected tenant.
- If ambiguous (multi-assigned and no default/request), reject.
2) Header Selector Tenant Switch
sequenceDiagram
autonumber
participant User
participant Topbar as Header Tenant Selector
participant Session as ConsoleSessionService
participant Auth as Authority
participant Stores as Session/Context Stores
participant APIs as Platform/Scanner/Graph APIs
User->>Topbar: Select tenant "tenant-bravo"
Topbar->>Session: switchTenant("tenant-bravo")
Session->>Stores: Optimistic selectedTenant update
Session->>Auth: /console/tenants (tenant header=tenant-bravo)
Auth-->>Session: allowed tenants + selectedTenant
Session->>Auth: /console/profile + /console/token/introspect
Auth-->>Session: profile/token introspection for selected tenant
Session->>Stores: Commit tenant to Console/Auth/Platform/TenantActivation stores
Session->>APIs: Trigger context reload for tenant-scoped data
APIs-->>Topbar: Refreshed tenant-scoped responses
Error recovery path:
- On switch failure (
403,tenant_conflict, session expiry), restore previous tenant in all stores. - Attempt context reload for previous tenant.
- Surface deterministic error in tenant panel with retry action.
3) API Request Propagation Through Gateway
sequenceDiagram
autonumber
participant UI as Web API Client
participant I as Tenant/Context Interceptors
participant Gw as Router/Gateway
participant Backend as Platform/Scanner/Graph
UI->>I: Outgoing request
I->>I: Resolve active tenant from canonical runtime state
I-->>UI: Add canonical header X-StellaOps-Tenant (+ compat aliases)
UI->>Gw: Request with tenant headers + token
Gw->>Gw: Strip caller-supplied identity headers, derive tenant from validated claims, rewrite canonical headers
Gw->>Backend: Forward tenant-scoped request
Backend->>Backend: Resolve tenant context + enforce tenant ownership
Backend-->>UI: Deterministic success/failure payload
Cache/store invalidation points after tenant switch:
- Console session context cache.
- Tenant-scoped page stores (Platform/Scanner/Graph read models).
- URL context synchronization where tenant is persisted as global context.
4) Failure Sequences
Missing tenant context
- Expected result: deterministic
400/401/403based on service policy and auth stage. - UI behavior: keep prior selection if available; show recoverable error panel.
Tenant mismatch
- Trigger: claim tenant != header/request tenant.
- Expected result: reject with deterministic conflict error (for example
tenant_conflictortenant_forbidden). - Audit/telemetry: record attempted tenant override + resolved tenant.
Insufficient scope
- Trigger: token lacks required policy scope for requested endpoint.
- Expected result: deterministic
403with scope policy failure context. - UI behavior: no tenant mutation; show access-denied state.