Skip to Content

Token Exchange (RFC 8693)

Das Problem: Ein Token passt nicht für alle

In einer monolithischen Anwendung reicht ein einzelnes Zugriffstoken aus: Der Benutzer authentifiziert sich, erhält ein Token und verwendet es für jeden API-Aufruf. Aber moderne Architekturen sind nicht monolithisch. Eine typische Anfrage könnte mehrere Dienste durchlaufen:

Benutzer → Frontend → API-Gateway → Bestelldienst → Zahlungsdienst → Benachrichtigungsdienst

Jeder Dienst in dieser Kette hat unterschiedliche Vertrauensniveaus, unterschiedliche Scopes und unterschiedliche Zielgruppen. Das gleiche Token überall zu verwenden, schafft Probleme:

ProblemBeschreibung
Überprivilegierte TokensDas Frontend-Token hat read:users write:orders manage:payments, aber der Zahlungsdienst braucht nur process:payments. Wenn der Zahlungsdienst kompromittiert wird, hat der Angreifer Zugriff auf alle Scopes.
Falsche ZielgruppeEin für frontend-app ausgestelltes Token sollte nicht von payment-service akzeptiert werden. Ohne Zielgruppen-Einschränkung ist jeder Dienst, der das Token akzeptiert, ein gültiges Ziel.
ImpersonierungEin Admin muss das Konto eines Benutzers debuggen, indem er als dieser Benutzer agiert. Es gibt keine Standardmethode, einen anderen Benutzer zu „werden”, ohne seine Anmeldedaten zu kennen.
DelegationDienst A muss Dienst B im Namen des Benutzers aufrufen, aber Dienst B muss sowohl den ursprünglichen Benutzer als auch die Identität des aufrufenden Dienstes kennen.

OAuth 2.0 Token Exchange (RFC 8693) bietet ein standardisiertes Protokoll zum Austausch eines Sicherheitstokens gegen ein anderes, mit präziser Kontrolle über das Subjekt, die Zielgruppe und den Scope des resultierenden Tokens.

Was Token Exchange tut

Token Exchange definiert einen neuen Grant-Typ am Token-Endpunkt des Autorisierungsservers. Ein Client sendet ein vorhandenes Token (den subject_token) und optional einen actor_token und erhält dafür ein neues Token mit anderen Eigenschaften zurück.

Die Schlüsseloperationen, die es ermöglicht:

  1. Impersonierung: Admin erhält ein Token mit sub auf einen anderen Benutzer gesetzt
  2. Delegation: Dienst erhält ein Token, das die ursprüngliche Benutzeridentität beibehält, aber den Dienst als Akteur aufzeichnet
  3. Zielgruppen-Einschränkung: Dienst erhält ein Token, das auf eine bestimmte nachgelagerte API beschränkt ist
  4. Scope-Reduzierung: Dienst erhält ein Token mit weniger Berechtigungen als das Original

Funktionsweise von Token Exchange

Die Token-Exchange-Anfrage

Eine Token-Exchange-Anfrage ist ein POST an den Standard-Token-Endpunkt mit grant_type=urn:ietf:params:oauth:grant-type:token-exchange:

POST /api/auth/token HTTP/1.1 Host: auth.example.com Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange &subject_token=eyJhbGciOiJSUzI1NiJ9... &subject_token_type=urn:ietf:params:oauth:token-type:access_token &requested_token_type=urn:ietf:params:oauth:token-type:access_token &audience=payment-service &scope=process:payments &actor_token=eyJhbGciOiJSUzI1NiJ9... &actor_token_type=urn:ietf:params:oauth:token-type:access_token

Anfrageparameter

ParameterErforderlichBeschreibung
grant_typeJaImmer urn:ietf:params:oauth:grant-type:token-exchange
subject_tokenJaDas auszutauschende Token — repräsentiert die Identität der Partei, in deren Namen die Anfrage gestellt wird
subject_token_typeJaDer Typ des Subjekt-Tokens (siehe Token-Typen unten)
requested_token_typeOptionalDer gewünschte Typ für das Ausgabe-Token. Standard: access_token.
audienceOptionalDer Zieldienst oder die API, die das neue Token verwenden wird
scopeOptionalDie angeforderten Scopes für das neue Token. Muss eine Teilmenge der Scopes des Subjekt-Tokens sein.
actor_tokenOptionalDas Token der Partei, die den Austausch durchführt (der „Akteur”)
actor_token_typeBedingtErforderlich, wenn actor_token vorhanden ist

Token-Typen

RFC 8693 definiert standardisierte URNs für Token-Typen:

Token-Typ-URNBeschreibung
urn:ietf:params:oauth:token-type:access_tokenOAuth 2.0-Zugriffstoken
urn:ietf:params:oauth:token-type:refresh_tokenOAuth 2.0-Refresh-Token
urn:ietf:params:oauth:token-type:id_tokenOpenID Connect-ID-Token
urn:ietf:params:oauth:token-type:jwtGenerisches JWT

Die Antwort

Ein erfolgreicher Token-Exchange gibt ein neues Token zurück:

{ "access_token": "eyJhbGciOiJSUzI1NiJ9...", "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", "token_type": "Bearer", "expires_in": 1800, "scope": "process:payments" }

Der issued_token_type bestätigt, welche Art von Token tatsächlich ausgestellt wurde (es kann sich von requested_token_type unterscheiden, wenn der Server Richtlinien angewendet hat).

Impersonierung vs Delegation

Token Exchange unterstützt zwei grundlegend unterschiedliche Muster: Impersonierung und Delegation. Den Unterschied zu verstehen ist entscheidend für den Aufbau sicherer Systeme.

Impersonierung

Bei der Impersonierung wird der sub-Claim des ausgetauschten Tokens auf den Zielbenutzer gesetzt. Der nachgelagerte Dienst sieht Anfragen „von” dem impers onierten Benutzer. Der ursprüngliche Akteur wird im act-Claim für Prüfzwecke aufgezeichnet, aber der Dienst behandelt die Anfrage so, als käme sie vom Zielbenutzer.

Admin (sub: admin-456) tauscht Token für Benutzer (sub: user-123) → Neues Token: sub=user-123, act.sub=admin-456 → Zahlungsdienst sieht: „Anfrage von user-123 (impersoniert durch admin-456)"

Anwendungsfall: Ein Admin muss einen Fehler reproduzieren, der nur das Konto eines bestimmten Benutzers betrifft. Er impersoniert den Benutzer, um dieselben Daten und dasselbe Verhalten zu sehen.

POST /api/auth/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange &subject_token=<user-123-token> &subject_token_type=urn:ietf:params:oauth:token-type:access_token &actor_token=<admin-456-token> &actor_token_type=urn:ietf:params:oauth:token-type:access_token &requested_token_type=urn:ietf:params:oauth:token-type:access_token

Das resultierende JWT:

{ "sub": "user-123", "iss": "https://auth.example.com", "aud": "frontend-app", "exp": 1739882700, "act": { "sub": "admin-456" } }

Delegation

Bei der Delegation bleibt der sub-Claim des ausgetauschten Tokens der ursprüngliche Benutzer. Der Akteur wird im act-Claim aufgezeichnet, aber das Token repräsentiert immer noch die Anfrage des ursprünglichen Benutzers, jetzt vermittelt durch einen Dienst.

Benutzer (sub: user-123) authentifiziert sich → Frontend ruft Bestelldienst auf → Bestelldienst tauscht Token, um Zahlungsdienst aufzurufen → Neues Token: sub=user-123, act.sub=order-service → Zahlungsdienst sieht: „Anfrage von user-123, delegiert über order-service"

Anwendungsfall: Dienst-zu-Dienst-Aufrufe in einer Microservice-Architektur, bei der jeder Dienst nachgelagerte Dienste im Namen des Benutzers aufrufen muss, wobei der nachgelagerte Dienst sowohl wissen muss, wer der Benutzer ist, als auch welcher Dienst aufruft.

Das resultierende JWT:

{ "sub": "user-123", "iss": "https://auth.example.com", "aud": "payment-service", "exp": 1739882700, "scope": "process:payments", "act": { "sub": "order-service" } }

Wesentliche Unterschiede

DimensionImpersonierungDelegation
sub im neuen TokenZielbenutzerUrsprünglicher Benutzer (unverändert)
act-ClaimUrsprünglicher Admin/AkteurZwischendienst
Nachgelagerter Dienst sieht„Anfrage vom Zielbenutzer”„Anfrage vom ursprünglichen Benutzer via Dienst”
Erforderliche Berechtigungimpersonate:usersdelegate:tokens
RisikoniveauHoch (vollständige Identitätsannahme)Mittel (Scope kann reduziert werden)
Prüfpfadact zeichnet auf, wer impersoniert hatact zeichnet auf, welcher Dienst delegiert hat
Typischer AkteurAdmin-BenutzerBackend-Dienst

Impersonierung ist eine privilegierte Operation. In Auris können nur Clients mit der impersonate:users-Berechtigung Impersonierungs-Token-Exchanges durchführen. Diese Berechtigung sollte auf Admin-Anwendungen beschränkt und niemals benutzerorientiertern Clients gewährt werden.

Der act-Claim: Delegationsketten

Der act-(Akteur-)Claim ist ein JSON-Objekt, das im JWT eingebettet ist und aufzeichnet, wer den Token-Exchange durchgeführt hat. Akteur-Claims können verschachtelt sein, um eine Kette von Delegationen darzustellen:

{ "sub": "user-123", "iss": "https://auth.example.com", "aud": "notification-service", "act": { "sub": "payment-service", "act": { "sub": "order-service", "act": { "sub": "api-gateway" } } } }

Dieses Token erzählt eine Geschichte: user-123 hat eine Anfrage über api-gateway gestellt, das an order-service delegiert hat, das an payment-service delegiert hat, das jetzt notification-service aufruft.

Die Kette lesen

Der äußerste act.sub ist der unmittelbare Aufrufer. Jedes verschachtelte act repräsentiert den vorherigen Hop in der Delegationskette. Das sub auf der obersten Ebene ist immer der ursprüngliche Benutzer.

notification-service empfängt das Token und sieht: - Wer ist der Benutzer? → sub: user-123 - Wer ruft mich auf? → act.sub: payment-service - Wer hat payment-service aufgerufen? → act.act.sub: order-service - Wer hat order-service aufgerufen? → act.act.act.sub: api-gateway

Kettentiefenbegrenzungen

Auris begrenzt die Delegationskettentiefe, um Confused-Deputy-Angriffe und unbegrenztes Token-Wachstum zu verhindern. Die Standard-Maximalkettentiefe beträgt 5. Wenn ein Token-Exchange diese Tiefe überschreiten würde, wird die Anfrage abgelehnt mit:

{ "error": "invalid_request", "error_description": "Maximum delegation chain depth exceeded" }

Sicherheitseigenschaften

Token Exchange ist ein leistungsstarker Mechanismus, der sorgfältige Sicherheitskontrollen erfordert.

Berechtigungsprüfungen

Auris erzwingt explizite Berechtigungsprüfungen bei jedem Token-Exchange:

OperationErforderliche BerechtigungWer hat sie
Impersonierungimpersonate:usersNur Admin-Anwendungen
Delegationdelegate:tokensBackend-Dienste
Zielgruppen-Einschränkungdelegate:tokensBackend-Dienste
Scope-ReduzierungKeine spezielle BerechtigungJeder Client kann seinen eigenen Scope reduzieren

Scope-Reduzierung (Niemals Erweiterung)

Das ausgetauschte Token kann nicht mehr Berechtigungen haben als das Original. Wenn das Subjekt-Token Scopes read:users write:orders hat, kann das ausgetauschte Token read:users (Teilmenge) anfordern, aber nicht read:users delete:users (Obermenge). Der Autorisierungsserver erzwingt dies durch Schnittmengenbildung des angeforderten Scopes mit dem Scope des Subjekt-Tokens.

Prüfpfad

Jeder Token-Exchange wird protokolliert mit:

  • Der Identität des Subjekt-Tokens (sub)
  • Der Identität des Akteur-Tokens (falls vorhanden)
  • Der angeforderten Zielgruppe und dem Scope
  • Ob der Exchange eine Impersonierung oder Delegation war
  • IP-Adresse und Zeitstempel

Dieser Prüfpfad ist in der Auris-Konsole unter Logs sichtbar und kann über die API abgefragt werden.

Token-Lebensdauerreduzierung

Ausgetauschte Tokens haben immer eine kürzere Lebensdauer als das Original. Wenn das Subjekt-Token in 1 Stunde abläuft, könnte das ausgetauschte Token in 30 Minuten ablaufen. Dies begrenzt das Expositionsfenster, wenn das ausgetauschte Token kompromittiert wird.

Die spezifische Reduzierung ist pro Anwendung konfigurierbar, mit einem Standard von 50% der verbleibenden Lebensdauer des Subjekt-Tokens.

Sicherheitsüberlegungen

Impersonierungsrisiken

Impersonierung ermöglicht eine vollständige Identitätsannahme. Eine Anwendung mit impersonate:users kann als jeder Benutzer im System agieren. Gegenmaßnahmen:

  1. Berechtigung einschränken: impersonate:users nur Admin-Anwendungen gewähren, niemals benutzerorientierenden Apps
  2. MFA verlangen: Admin muss in der aktuellen Sitzung MFA abgeschlossen haben, um Impersonierung durchzuführen
  3. Protokollieren und warnen: Alle Impersonierungsereignisse lösen Audit-Log-Einträge und (optional) Benachrichtigungen aus
  4. Zeitbegrenzung: Impersonierte Tokens haben kurze Lebensdauern (Standard 15 Minuten)

Delegationsketten-Angriffe

Eine Delegationskette A → B → C → D bedeutet, dass jeder Dienst in der Kette sukzessiv eingeschränktere Tokens hat. Ein kompromittierter Dienst in der Mitte der Kette kann jedoch:

  • Die Token-Claims lesen (einschließlich der act-Kette)
  • Sein delegiertes Token verwenden, um nachgelagerte Dienste aufzurufen
  • Er kann nicht Dienste außerhalb der audience des Tokens aufrufen
  • Er kann nicht den Scope über das hinaus erweitern, was ihm delegiert wurde

Token Exchange vs Alternativen

AnsatzStandardisiertIdentität erhaltenPrüfpfadScope-Kontrolle
Token Exchange (RFC 8693)JaJa (act-Claim)Vollständige KettePro Exchange
API-Gateway-Token-TransformationNeinVariiertNur GatewayGateway-Ebene
Benutzerdefinierte Middleware-HeaderNeinOft verlorenManuellAd-hoc
Gemeinsames DienstkontoNeinNein (Benutzeridentität verloren)MinimalKeine

Token Exchange ist der standardisierte Ansatz. Es bewahrt die vollständige Identitätskette, bietet granulare Scope-Kontrolle und erstellt einen Prüfpfad am Autorisierungsserver.

Auris-Implementierungsdetails

Prisma-Modell

model TokenExchange { id String @id @default(cuid()) tenantId String applicationId String subjectUserId String actorUserId String? requestedAudience String? requestedScope String? grantedScope String? exchangeType TokenExchangeType createdAt DateTime @default(now()) } enum TokenExchangeType { IMPERSONATION DELEGATION }

Konfiguration

Token Exchange wird pro Anwendung in der Auris-Konsole unter Anwendungen > (Anwendung auswählen) > Einstellungen aktiviert:

EinstellungStandardBeschreibung
Token Exchange aktivierenfalseHaupt-Toggle
Impersonierung erlaubenfalseOb diese Anwendung Impersonierungs-Exchanges durchführen kann
Delegation erlaubenfalseOb diese Anwendung Delegations-Exchanges durchführen kann
Maximale Kettentiefe5Maximale Verschachtelungstiefe der act-Claims
Ausgetauschte Token-Lebensdauer50%Lebensdauer des ausgetauschten Tokens als Prozentsatz der verbleibenden Lebensdauer des Subjekt-Tokens

API-Endpunkt

Token Exchange verwendet den Standard-Token-Endpunkt:

EndpunktMethodeGrant-Typ
/api/auth/tokenPOSTurn:ietf:params:oauth:grant-type:token-exchange

Codebeispiel: Microservice-Delegationskette

Das folgende Beispiel demonstriert eine Drei-Dienste-Delegationskette:

// api-gateway empfängt das Zugriffstoken des Benutzers und muss order-service aufrufen async function callOrderService(userAccessToken: string) { // Token des Benutzers gegen eines für order-service austauschen const exchangeResponse = await fetch('https://auth.example.com/api/auth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange', subject_token: userAccessToken, subject_token_type: 'urn:ietf:params:oauth:token-type:access_token', audience: 'order-service', scope: 'read:orders write:orders', }), }) const { access_token: orderServiceToken } = await exchangeResponse.json() // order-service mit dem delegierten Token aufrufen const orders = await fetch('https://order-service.internal/api/orders', { headers: { Authorization: `Bearer ${orderServiceToken}` }, }) return orders.json() } // order-service empfängt das delegierte Token und muss payment-service aufrufen async function processPayment(delegatedToken: string, orderId: string) { // Weiterer Exchange: Scope auf payment-service reduzieren const exchangeResponse = await fetch('https://auth.example.com/api/auth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange', subject_token: delegatedToken, subject_token_type: 'urn:ietf:params:oauth:token-type:access_token', audience: 'payment-service', scope: 'process:payments', }), }) const { access_token: paymentToken } = await exchangeResponse.json() // Das payment-service-Token hat jetzt: // sub: original-benutzer-id // aud: payment-service // scope: process:payments // act: { sub: order-service, act: { sub: api-gateway } } return fetch('https://payment-service.internal/api/charge', { method: 'POST', headers: { Authorization: `Bearer ${paymentToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ orderId, amount: 49.99 }), }) }

Admin-Impersonierungs-Beispiel

async function impersonateUser(adminToken: string, targetUserId: string) { const response = await fetch('https://auth.example.com/api/auth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange', subject_token: adminToken, subject_token_type: 'urn:ietf:params:oauth:token-type:access_token', requested_token_type: 'urn:ietf:params:oauth:token-type:access_token', audience: 'frontend-app', }), }) if (!response.ok) { const error = await response.json() if (error.error === 'insufficient_scope') { throw new Error('Admin hat keine impersonate:users-Berechtigung') } throw new Error(`Token Exchange fehlgeschlagen: ${error.error_description}`) } const { access_token } = await response.json() // Dieses Token hat: // sub: zielbenutzer-id // act: { sub: admin-benutzer-id } // Nachgelagerte Dienste sehen Anfragen „von" dem Zielbenutzer return access_token }

Jeder Token-Exchange — ob Impersonierung oder Delegation — erstellt einen Eintrag im Auris-Audit-Log. Administratoren können Logs nach Exchange-Typ filtern, um alle Impersonierungsaktivitäten zu überprüfen. In hochsicherheitsrelevanten Umgebungen können Impersonierungsereignisse Echtzeit-Benachrichtigungen an das Sicherheitsteam auslösen.

Verwandte Konzepte