wip: doctor/cli/docs/api to vector db consolidation; api hardening for descriptions, tenant, and scopes; migrations and conversions of all DALs to EF v10
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
# ADR-002: Multi-Tenant Selection With Same API Key
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-02-22
|
||||
**Sprint:** `SPRINT_20260222_053_DOCS_multi_tenant_same_api_key_contract_baseline.md`
|
||||
|
||||
## Context
|
||||
|
||||
Stella Ops must support clients that are assigned to more than one tenant while still using the same API key/client registration. Existing behavior assumes a scalar tenant assignment (`tenant`) and cannot safely select among multiple tenant memberships.
|
||||
|
||||
Without a canonical contract, modules diverge on claim names, header behavior, default selection, and mismatch handling. This creates cross-tenant leakage risk and migration churn.
|
||||
|
||||
## Decision
|
||||
|
||||
Use **one selected tenant per access token**, chosen at token issuance time.
|
||||
|
||||
### Selected model (accepted)
|
||||
|
||||
1. Client metadata supports both:
|
||||
- `tenant` (scalar compatibility/default tenant)
|
||||
- `tenants` (space-delimited assignment set; normalized lowercase, unique, sorted)
|
||||
2. Token request may include `tenant=<id>`.
|
||||
3. Authority resolves selected tenant deterministically:
|
||||
- If `tenant` parameter is present: it must exist in assigned tenants.
|
||||
- If no parameter:
|
||||
- use scalar `tenant` when configured, otherwise
|
||||
- use single-entry `tenants`, otherwise
|
||||
- reject as ambiguous.
|
||||
4. Issued tokens carry:
|
||||
- `stellaops:tenant` (selected tenant)
|
||||
- `stellaops:allowed_tenants` (space-delimited assigned set, optional)
|
||||
5. Gateway and services continue operating with one effective tenant per request.
|
||||
|
||||
### Fallback model (rejected for default path)
|
||||
|
||||
Multi-tenant token + per-request header override (`X-StellaOps-Tenant`) as primary selector.
|
||||
|
||||
Reason rejected:
|
||||
- Increases header spoofing and token confusion risk.
|
||||
- Creates inconsistent downstream behavior where services interpret tenant from different sources.
|
||||
- Expands change surface across all modules immediately.
|
||||
|
||||
## Canonical Contract
|
||||
|
||||
### Claims
|
||||
|
||||
- `stellaops:tenant`: selected tenant for this token.
|
||||
- `stellaops:allowed_tenants`: assigned tenant set (space-delimited, sorted).
|
||||
|
||||
### Client metadata
|
||||
|
||||
- `tenant`: scalar assignment / deterministic default.
|
||||
- `tenants`: assigned set (space-delimited).
|
||||
|
||||
### Headers
|
||||
|
||||
- Canonical tenant header: `X-StellaOps-Tenant`.
|
||||
- Legacy compatibility header: `X-Stella-Tenant` (bounded migration use only).
|
||||
|
||||
### Error semantics
|
||||
|
||||
- Requested tenant not assigned: reject `invalid_request`.
|
||||
- Missing tenant for tenant-required scope: reject `invalid_client` / `invalid_request` depending on grant validation stage.
|
||||
- Ambiguous tenant selection (multi-assigned, no default, no request): reject `invalid_request`.
|
||||
- Token tenant not in client assignments during validation: reject `invalid_token`.
|
||||
|
||||
## Threat Model
|
||||
|
||||
### Header spoofing
|
||||
|
||||
Risk: caller supplies tenant headers to escalate into another tenant.
|
||||
Mitigation: gateway strips inbound identity headers and rewrites from validated claims.
|
||||
|
||||
### Token confusion
|
||||
|
||||
Risk: token tenant differs from issuing client assignment or persisted token document.
|
||||
Mitigation: validation enforces principal/document/client assignment consistency.
|
||||
|
||||
### Cross-tenant leakage
|
||||
|
||||
Risk: silent default tenant fallback routes tenant-scoped requests incorrectly.
|
||||
Mitigation: remove authenticated `"default"` tenant fallback and fail ambiguous selections.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Keeps downstream service model stable: one tenant per token/request.
|
||||
- Enables same-key multi-tenant clients without global API contract break.
|
||||
- Supports UI hydration with explicit assigned tenant set.
|
||||
|
||||
### Tradeoff
|
||||
|
||||
- Tenant switching requires requesting a token for the target tenant.
|
||||
- Multi-assigned clients without explicit default must send `tenant` during token issuance.
|
||||
|
||||
## References
|
||||
|
||||
- `docs/implplan/SPRINT_20260222_053_DOCS_multi_tenant_same_api_key_contract_baseline.md`
|
||||
- `docs/implplan/SPRINT_20260222_054_Authority_same_key_multi_tenant_token_selection.md`
|
||||
- `docs/implplan/SPRINT_20260222_055_Router_tenant_header_enforcement_and_selection_flow.md`
|
||||
Reference in New Issue
Block a user