103 lines
6.2 KiB
Markdown
103 lines
6.2 KiB
Markdown
# ADR-0002: Session-Scope-Token-Format
|
||
|
||
- **Status:** accepted
|
||
- **Datum:** 2026-10-25
|
||
- **Entscheider:** CTO
|
||
- **Betroffene Dokumente:** `docs/architecture/v1.1.md` Abschnitte 3c, 4.5a, 5.1, 6, 7a.0, 9.1
|
||
|
||
## Kontext und Problemstellung
|
||
|
||
Ein Agent, der eine Datenquelle liest, muss ihr beweisen, dass er für Benutzer Y in einer aktiven Session S liest — nicht nur, dass er irgendein Agent ist. Ohne diesen Nachweis gibt es keine saubere Grundlage für OPA-Regeln wie „Agent-Travel darf nur `project_assignments` seines eigenen Benutzers lesen, nicht fremder". Pocket ID allein liefert nur Agent-Identität (via `client_credentials`), keine Benutzer-Delegation.
|
||
|
||
## Entscheidungsoptionen
|
||
|
||
- **Option A — Vollständiger OAuth 2.0 Token Exchange (RFC 8693) in Pocket ID.** Agent macht Token-Exchange gegen Pocket ID; dieser prüft Session und mintet ein Delegations-Token mit `act`-Claim.
|
||
- **Option B — Eigener Session Scope Token Service (SSTS) in `trust-api`.** Agent präsentiert Pocket-ID-Access-Token + K8s-SA-Token; SSTS prüft doppelt, konsultiert OPA, stellt kurzlebigen JWT mit `act`-Claim aus, signiert mit eigenem Ed25519-Schlüssel. Datenquellen-Adapter validieren per JWKS offline.
|
||
- **Option C — Opaque Bearer Tokens mit zentralem Introspection-Endpunkt.** Datenquellen rufen bei jedem Request `POST /introspect` am trust-api.
|
||
|
||
## Entscheidung
|
||
|
||
**Option B.** Der Session Scope Token Service läuft als Modul in `trust-api` und stellt JWTs im Format gemäß Anhang A dieser ADR aus, signiert mit einem eigenen Ed25519-Schlüssel.
|
||
|
||
### Begründung
|
||
|
||
- **Pocket ID macht keinen RFC-8693-Flow.** Der Check im Pocket-ID-OIDC-Discovery zeigt `client_credentials`, aber keinen `urn:ietf:params:oauth:grant-type:token-exchange`. Option A würde Pocket ID erweitern müssen oder einen zweiten IdP aufbauen — beides nicht gewünscht (siehe `docs/architecture/v1.1.md` 3a).
|
||
- **Offline-Validierung ist wichtig.** Datenquellen-Adapter sollen Tokens ohne Round-Trip zum trust-api prüfen können — JWKS + signierter JWT reichen. Option C erzwingt eine Netzwerkrunde pro Lese-Request und bindet Datenquellen-Verfügbarkeit an trust-api-Verfügbarkeit.
|
||
- **OPA-Entscheidung bleibt zentral.** Der SSTS konsultiert OPA genau einmal beim Ausstellen, nicht pro Query. Rego-Policies müssen nur im trust-api deployed sein.
|
||
|
||
## Entscheidung — Anhang A: Token-Format
|
||
|
||
Header:
|
||
```json
|
||
{ "alg": "EdDSA", "typ": "JWT", "kid": "sst-2026-q4" }
|
||
```
|
||
|
||
Payload (Claims):
|
||
```json
|
||
{
|
||
"iss": "https://trust.basicstack.de",
|
||
"sub": "agent-travel",
|
||
"act": {
|
||
"iss": "https://auth.basicstack.de",
|
||
"sub": "user-12345",
|
||
"session_id": "SES-2026-10-25-abc"
|
||
},
|
||
"aud": "ds:expense-db-app",
|
||
"scope": {
|
||
"data_source": "ds:expense-db-app",
|
||
"resource": "project_assignments",
|
||
"filters": { "employee_id": "EMP-12345" },
|
||
"operation": "read"
|
||
},
|
||
"jti": "01HE2D3V6X7Y8Z9ABCDEFG",
|
||
"iat": 1735120000,
|
||
"exp": 1735120300,
|
||
"nbf": 1735120000,
|
||
"policy_version": "sha256:<rego-bundle-digest>"
|
||
}
|
||
```
|
||
|
||
Semantik:
|
||
- `iss` = feste trust-api-Issuer-URL.
|
||
- `sub` = `agent_id` (OIDC-Client-Name in Pocket ID + Eintrag in Agent Registry).
|
||
- `act` = Delegations-Kontext nach RFC 8693 §4.1 (Semantik, nicht der volle Flow). `act.sub` ist der Benutzer, `act.session_id` die Approval-Service-Session.
|
||
- `aud` = genau *eine* Zieldatenquelle. Mehrere Datenquellen → mehrere Tokens.
|
||
- `scope` = engster erlaubter Scope. Datenquellen-Adapter MUSS `scope` strikt gegen Request matchen (keine erweiterten Filter, keine zusätzlichen Resources).
|
||
- `jti` = ULID oder UUID. Datenquellen-Adapter führen `scope_token_log` mit `UNIQUE (data_source_id, jti)` als optionalen Replay-Schutz zusätzlich zur `exp`-Prüfung.
|
||
- `exp` − `iat` = **≤ 300 Sekunden** (5 Minuten). Begründung: Agenten holen Tokens pro Query, nicht pro Session.
|
||
- `nbf` = `iat`.
|
||
- `policy_version` = SHA-256 des zur Entscheidungszeit aktiven OPA-Rego-Bundles. Wandert später in die Query Attestation für Nachvollziehbarkeit.
|
||
|
||
Signatur: Ed25519. Schlüsselmaterial liegt in OpenBao Transit (`transit/keys/session-scope-token-signer`), rotiert alle 90 Tage. JWKS unter `https://trust.basicstack.de/.well-known/jwks.json`.
|
||
|
||
### Begründung Detailentscheidungen
|
||
|
||
- **EdDSA statt RS256.** Konsistent zum restlichen Signatur-Stack (DSSE / in-toto nutzen Ed25519).
|
||
- **5-Minuten-TTL.** Lang genug für Chat-Interaktionen mit einer Datenquelle; kurz genug, dass ein geleakter Token begrenzten Schaden anrichtet. Agenten holen bei Bedarf einen neuen; `/session-scope-tokens` ist bewusst billig.
|
||
- **`scope.filters` ist Teil des Tokens.** Alternative wäre „offene" Reads mit späterer Filterprüfung — würde aber erlauben, dass ein Agent erst eine breite Query stellt und dann nur bestimmte Zeilen weitergibt. Mit Filter im Token geht das nicht; die Query Attestation beweist zudem die gefilterten Parameter.
|
||
- **`aud = ds:...`.** Explizite Audience bindet den Token an *eine* Datenquelle. Verhindert, dass ein Token bei anderen Datenquellen „probiert" wird.
|
||
|
||
## Konsequenzen
|
||
|
||
### Positive
|
||
|
||
- OPA-Policies werden einmal pro Scope-Request ausgewertet, nicht pro Query-Zeile.
|
||
- Datenquellen können offline (ohne trust-api) authorisieren — bessere Entkopplung, besseres Fehlerverhalten.
|
||
- Delegationskette ist in Attestierungen vollständig nachvollziehbar: `session_scope_token_jti` zeigt zurück auf Token-Ausstellung, die wiederum OPA-Policy-Version trägt.
|
||
|
||
### Negative / Trade-offs
|
||
|
||
- Zusätzliche Signatur-Round-Trip (Agent → `/session-scope-tokens`) pro Lese-Operation. Bei chattigen Workflows mit vielen kleinen Queries ggf. relevant — eine spätere ADR könnte Scope-Token mit mehreren erlaubten Resources diskutieren.
|
||
- Datenquellen-Adapter brauchen JWT-Validierungs-Bibliotheken (für Go: trivial, `github.com/go-jose/go-jose`).
|
||
|
||
### Folgearbeiten / Open Points
|
||
|
||
- Client-Library (Go) für Agenten mit Caching eines Scope-Tokens bis `exp - 30s`.
|
||
- Rego-Testsuite für die drei Policy-Klassen (Transformation, Senke, Datenquelle).
|
||
- Monitoring: Rate von Scope-Token-Ausstellungen pro Agent/Benutzer; Anomalie-Erkennung.
|
||
|
||
## Links
|
||
|
||
- `docs/architecture/v1.1.md` Abschnitte 3c, 4.5a, 5.1, 6, 7a.0, 9.1
|
||
- RFC 8693 (OAuth 2.0 Token Exchange)
|
||
- Pocket ID OIDC Discovery: `https://auth.basicstack.de/.well-known/openid-configuration`
|