107 lines
4.2 KiB
Markdown
107 lines
4.2 KiB
Markdown
# 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
|
|
|
|
```mermaid
|
|
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 `tenant` parameter 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
|
|
|
|
```mermaid
|
|
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
|
|
|
|
```mermaid
|
|
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`/`403` based 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_conflict` or `tenant_forbidden`).
|
|
- Audit/telemetry: record attempted tenant override + resolved tenant.
|
|
|
|
### Insufficient scope
|
|
- Trigger: token lacks required policy scope for requested endpoint.
|
|
- Expected result: deterministic `403` with scope policy failure context.
|
|
- UI behavior: no tenant mutation; show access-denied state.
|
|
|