Token Exchange mit Keycloak

Die sichere und kontrollierte Delegation von Identitäten und Rechten ist in modernen Plattformarchitekturen eine zentrale Herausforderung. Besonders in Systemen, in denen eine Vielzahl von Microservices miteinander kommunizieren und dabei Benutzerkontexte weitergereicht werden müssen, stoßen einfach ausgeprägte OAuth2-Modelle schnell an ihre Grenzen. Wie lässt sich sicherstellen, dass jeder Dienst nur die Berechtigungen erhält, die er tatsächlich benötigt? Wie kann man verhindern, dass Benutzer-Tokens mit übergreifenden Rechten unkontrolliert weitergereicht werden? Und wie bleibt die ursprüngliche Benutzeridentität über Servicegrenzen hinweg nachvollziehbar?
Dieser Artikel zeigt, wie sich diese Fragen mit Hilfe des OAuth 2.0 Token Exchange Standards beantworten lassen – und wie man diesen Mechanismus konkret mit Keycloak implementiert. Neben einer technischen Einführung in RFC 8693 beleuchtet der Beitrag die praktische Umsetzung mit Keycloak und Terraform, erklärt typische Token-Austauschszenarien zwischen Microservices und gibt Empfehlungen für einen sicheren produktiven Einsatz. Dabei richtet sich der Text vor allem an Software- und Sicherheitsarchitekt:innen, die Plattformen entwerfen – mit einem besonderen Fokus auf Praxisnähe.
- Warum klassische OAuth-Delegation nicht ausreicht
- OAuth 2.0 Token Exchange (RFC 8693) als Lösung
- Implementierung von Token Exchange in Keycloak
- Konfiguration mit dem Keycloak Terraform Provider
- Token Exchange Request und Response: Beispiel
- Best Practices für den produktiven Einsatz
- Ausblick auf Keycloak-Version 26.2
Identitäts- und Berechtigungsmanagement: Warum klassische OAuth-Delegation nicht ausreicht
In komplexen Plattformen und Microservice-Landschaften ist Identitäts- und Berechtigungsmanagement kein Randthema, sondern ein sicherheitsrelevanter, architektonischer Kernbaustein. Vor allem dort, wo Dienste im Namen eines Benutzers "on behalf of" mit anderen Diensten kommunizieren, entstehen neue Anforderungen an eine sichere, nachvollziehbare und klar abgegrenzte Delegation von Rechten. Einfach ausgeprägte OAuth2-Ansätze stoßen dabei schnell an ihre Grenzen – obwohl OAuth2 als Framework eigentlich alle Bausteine beinhaltet, die für diesen Anwendungsfall notwendig wären.
Ein häufig beobachtetes Muster dieser einfachen OAuth2-Implementierungen besteht darin, dass ein Benutzer sich gegenüber einem Frontend authentifiziert und das dabei ausgestellte Access Token vom Frontend direkt an ein Backend weitergegeben wird. Dieses Backend wiederum reicht dasselbe Access Token an nachgelagerte Dienste weiter. Was auf den ersten Blick wie eine pragmatische Lösung wirkt, birgt gravierende Risiken: Ein einziges Access Token kann plötzlich weit mehr Dienste erreichen, als ursprünglich beabsichtigt. Die Kontrolle über Audience, Scope und tatsächliche Autorisierung beginnt zu verschwimmen. In vielen Teams ist dieses Muster unter dem inoffiziellen Begriff "Poor Man’s Delegation" bekannt – und das mit gutem Grund. Es ist ein typischer Shortcut, der langfristig teuer wird [1].
Als einfache Alternative bietet sich in manchen Fällen ein Client Credentials Grant an, bei dem sich ein Dienst mit seinen eigenen Zugangsdaten gegenüber einem Identity Provider authentifiziert [2]. Doch dieser Ansatz hat einen fundamentalen Nachteil: Er trennt den Aufruf vollständig vom Benutzerkontext. Dienste, die im Auftrag eines Nutzers handeln sollen, verlieren dabei jede Spur dieser Beziehung. Die Folge: Entweder wird der Zugriff pauschal erlaubt – oder granularer Zugriff auf Benutzerebene wird unmöglich. Keine der beiden Varianten ist für moderne Plattformen akzeptabel.
Sichere Delegation in verteilten IT-Architekturen: OAuth 2.0 Token Exchange (RFC 8693) als Lösung
Für das Problem der sicheren Delegation in verteilten Architekturen hat die IETF eine elegante Lösung geschaffen: den OAuth 2.0 Token Exchange Standard, spezifiziert in RFC 8693. Er beschreibt einen standardisierten Ablauf, bei dem ein vorhandenes Token gegen ein neues Token für einen anderen Zielservice eingetauscht wird. Dieses neue Token ist nicht nur spezifisch für die gewünschte Audience ausgestellt, sondern trägt auch den Benutzerkontext weiter – eine essentielle Eigenschaft, um nachvollziehen zu können, in wessen Auftrag ein Zugriff erfolgt.
Im Gegensatz zum Weiterreichen desselben Tokens entlang der Service-Kette, wie beim sogenannten "Poor Man’s Delegation", erhält bei Token Exchange jeder Microservice ein individuell für ihn ausgestelltes Token – mit eigener Audience und gegebenenfalls reduziertem Scope. Dadurch entsteht eine starke Isolation zwischen den Services: Kein Dienst akzeptiert Tokens, die nicht explizit für ihn gedacht sind. Gleichzeitig bleibt die Benutzeridentität durchgängig erhalten – eine ideale Kombination aus Sicherheit und Delegation.
Ein typischer Ablauf eines solchen "on behalf of"-Szenarios (s. Abb. 2) gestaltet sich wie folgt: Ein Benutzer authentifiziert sich beim Frontend (Client) und erhält ein Access Token (A). Dieses Token gelangt im nächsten Schritt an eine API (z. B. ein API-Gateway), das im Auftrag des Benutzers nun eine weitere API aufrufen soll. Anstatt das ursprüngliche Token (A) weiterzuleiten, stellt die erste API eine Anfrage an den Token-Endpunkt von Identity Provider. Dort wird das Benutzer-Token (A) als subject_token eingereicht, gemeinsam mit den Angaben zur gewünschten Ziel-Audience – also der zweiten API. Identity Provider validiert das Token und stellt ein neues Token (B) aus, das speziell für die zweite API bestimmt ist. Dieses neue Token (B) enthält nach wie vor die Identität des ursprünglichen Benutzers und ggf. einen act-Claim, der aufzeigt, dass die erste API im Auftrag des Users handelt. Gleichzeitig ist das neue Token aber nur für die zweite API nutzbar – andere Services würden es aufgrund der Audience-Angabe ablehnen.
Token Exchange nutzt dabei einen dedizierten OAuth2-Grant-Type: urn:ietf:params:oauth:grant-type:token-exchange. Die Spezifikation erlaubt zahlreiche Feinsteuerungen mittels entsprechender Anfrage-Parameter:
- subject_token: Das Token, das ausgetauscht werden soll (z. B. ein Access Token des Benutzers).
- subject_token_type: Der Typ des ursprünglichen Tokens, meist ein Access Token (access_token).
- audience: Die Ziel-Client-ID (z. B. api2).
- requested_token_type: Der Typ des erwarteten Tokens, um z. B. ein Refresh-Token zu erhalten anstelle eines Access Tokens.
Eine konkrete Anfrage könnte beispielsweise wie folgt aussehen:
POST /realms/<realm>/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&client_id=<Client ID der ersten API)
&client_secret=<Client Secret der ersten API>
&subject_token=<JWT des Benutzers / Frontends>
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=<Cliend ID der zweiten API>
&requested_token_type=urn:ietf:params:oauth:token-type:refresh_tokenDie Antwort eines Identity Providers könnte dann wie folgt aussehen:
{
"access_token": "eyJhbGciOiJSUzI1Ni...",
"refresh_token": "eyJhbGciOiJIUzI1Ni...",
"expires_in": 3600,
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer"
}Damit kann die erste API sicher auf die zweite API zugreifen – unter klarer Angabe des Auftragskontextes und mit einer Audience, die sicherstellt, dass andere Dienste das Token nicht zweckentfremden können. Der Token-Exchange-Mechanismus schafft damit ein Gleichgewicht zwischen Sicherheit, Kontrolle und Benutzerdelegation – interoperabel und standardkonform, auch über Systemgrenzen hinweg.
Implementierung von Token Exchange in Keycloak
Keycloak unterstützt diesen Mechanismus seit mehreren Versionen – zunächst als Preview-Feature. Wer es nutzen wollte, musste den Keycloak-Server explizit mit dem Feature-Flag --features=token-exchange starten. Abhängig vom Anwendungsfall war es zusätzlich erforderlich, auch das Feature admin-fine-grained-authz zu aktivieren, insbesondere, wenn die Berechtigungen über das feingranulare Authz-System von Keycloak gesteuert werden sollten.
Die Konfiguration ist durchdacht, aber erfordert Präzision. Auf der Seite des Ziel-Clients – also des Dienstes, für den das neue Token ausgestellt werden soll – müssen die sogenannten "Fine Grained Permissions" aktiviert werden. Über den "Permissions"-Reiter in der Keycloak-Konsole lässt sich dann der Scope token-exchange konfigurieren. Entscheidend ist nun festzulegen, welcher Quell-Client – typischerweise ein anderer Dienst – Tokens für diesen Ziel-Client anfordern darf. Dies geschieht über Policies, die auf konkrete Client-IDs referenzieren. Ein Dienst, der Tokens für andere austauschen möchte, benötigt also explizit das Recht, genau das tun zu dürfen – für genau definierte Ziel-Clients. Keycloak erzwingt diese Trennung und schützt so vor unkontrolliertem Austausch.
Konfiguration mit dem Keycloak Terraform Provider
Für Implementierung empfiehlt sich ein Infrastruktur-als-Code-Ansatz (IaC) – etwa mit dem Keycloak Terraform Provider [3]. Hier lässt sich die gesamte Berechtigungskette deklarativ beschreiben. Clients, Policies und Permissions werden versioniert und automatisiert ausgerollt. Das ist gerade in Plattformen mit vielen Diensten und Umgebungen – von Entwicklung bis Produktion – ein immenser Vorteil. Die Entkopplung der Konfiguration von der Admin-Oberfläche vermeidet Inkonsistenzen und macht das Sicherheitsmodell überprüfbar.
Betrachten wir eine vollständige Terraform-Konfiguration, die exemplarisch einen einfachen Plattform-Use-Case abbildet: Ein Benutzer authentifiziert sich über ein Frontend. Das Frontend ruft das API-Gateway mit einem Access Token auf, das speziell für das Gateway bestimmt ist. Das Gateway wiederum muss im Auftrag des Benutzers mit einem internen Backend-Service kommunizieren. Dieser Zugriff erfolgt auf Basis eines neuen Tokens, das das Gateway über einen Token Exchange erhält.
Zunächst wird in Keycloak ein Realm definiert, in dem alle Clients und Ressourcen existieren:
resource "keycloak_realm" "platform" {
realm = "platform"
enabled = true
}Anschließend definieren wir einen Public Client für das Frontend. Wichtig ist hier, dass full_scope_allowed deaktiviert ist, um zu verhindern, dass das vom Benutzer erhaltene Token Zugriff auf alle möglichen, im Realm definierten Ressourcen erlaubt:
resource "keycloak_openid_client" "frontend" {
realm_id = keycloak_realm.platform.id
client_id = "frontend"
name = "Frontend"
access_type = "PUBLIC"
full_scope_allowed = false
}Das API-Gateway wird als vertraulicher Client konfiguriert. Es führt später den eigentlichen Token Exchange aus:
resource "keycloak_openid_client" "api_gateway" {
realm_id = keycloak_realm.platform.id
client_id = "api-gateway"
name = "API Gateway"
access_type = "CONFIDENTIAL"
service_accounts_enabled = false
full_scope_allowed = false
}Damit ein Token für das Gateway korrekt ausgestellt wird, müssen wir einen Client Scope mit Audience Mapping definieren. Dies stellt sicher, dass Tokens für das Gateway explizit eine Audience enthalten, die das Gateway validieren kann:
resource "keycloak_openid_client_scope" "openid_client_scope_api" {
realm_id = keycloak_realm.platform.id
name = "api"
description = "When requested, this scope will map audience of api-gateway"
include_in_token_scope = true
}
resource "keycloak_openid_audience_protocol_mapper" "audience_mapper_api" {
realm_id = keycloak_realm.platform.id
client_scope_id = keycloak_openid_client_scope.openid_client_scope_api.id
name = "API Gateway Audience"
included_client_audience = keycloak_openid_client.api_gateway.client_id
}Dieser Scope wird anschließend als Default Scope für den Frontend-Client hinzugefügt:
resource "keycloak_openid_client_default_scopes" "client_default_scopes_fronted" {
realm_id = keycloak_realm.platform.id
client_id = keycloak_openid_client.frontend.id
default_scopes = [
keycloak_openid_client_scope.openid_client_scope_api.name,
// ...
]
}Jetzt definieren wir den Backend-Service, für den ein neues Token via Token Exchange ausgestellt werden soll. Dabei aktivieren wir die Authorization Services und deaktivieren standardmäßige Ressourcen/Policies:
resource "keycloak_openid_client" "backend" {
realm_id = keycloak_realm.platform.id
client_id = "backend-service"
name = "Backend Service"
access_type = "CONFIDENTIAL"
full_scope_allowed = false
service_accounts_enabled = true
authorization {
policy_enforcement_mode = "ENFORCING"
decision_strategy = "UNANIMOUS"
allow_remote_resource_management = false
keep_defaults = false
}
}Im nächsten Schritt wird eine Policy definiert, die nur dem API-Gateway erlaubt, Token für den Backend-Service zu tauschen:
resource "keycloak_openid_client_client_policy" "token_exchange_backend_clients" {
resource_server_id = keycloak_openid_client.backend.id
realm_id = keycloak_realm.platform.id
name = "Allow token exchange from clients (target: backend-service)"
logic = "POSITIVE"
decision_strategy = "UNANIMOUS"
clients = [
keycloak_openid_client.api_gateway.id
]
}Diese Policy wird nun mit dem Scope token-exchange des Ziel-Clients (Backend-Service) verknüpft. Damit wird die Berechtigung für den Austausch formal festgelegt:
resource "keycloak_openid_client_permissions" "token_exchange_backend" {
realm_id = keycloak_realm.platform.id
client_id = keycloak_openid_client.backend.id
token_exchange_scope {
policies = [
keycloak_openid_client_client_policy.token_exchange_backend_clients.id,
]
description = "Token exchange (target: backend-service)"
decision_strategy = "UNANIMOUS"
}
}Diese Konfiguration bildet einen vollständigen Token-Exchange-Pfad ab, der es einem API-Gateway erlaubt, im Auftrag eines Benutzers mit einem dedizierten Token einen geschützten Backend-Dienst aufzurufen. Alle Schritte sind deklarativ beschrieben und können nachvollziehbar versioniert und getestet werden – ein idealer Ansatz für skalierbare und sichere Plattformarchitekturen.
Token Exchange Request und Response: Beispiel
Basierend auf der zuvor beschriebenen Konfiguration mit einem Frontend, einem API-Gateway und einem Backend-Service, soll nun das API-Gateway im Namen des Benutzers ein Token für den Backend-Service anfordern. Hierbei liegt dem Gateway bereits ein Benutzer-Token vor, das vom Frontend übermittelt wurde. Das Gateway initiiert nun den Austauschvorgang bei Keycloak.
Der tatsächliche Austausch erfolgt über eine POST-Anfrage an den Token-Endpunkt von Keycloak. Der Client meldet sich mit seiner Client-ID und seinem Secret an und übergibt das Token (subject_token), das getauscht werden soll – also typischerweise das Benutzer-Token, das er beim Eingang einer Anfrage erhalten hat. Zusätzlich spezifiziert er, für welchen Ziel-Client das neue Token gedacht ist, indem er dessen Audience angibt. Keycloak prüft die übergebene Konfiguration, validiert das ursprüngliche Token und erstellt ein neues Access Token, das exakt auf den Ziel-Client zugeschnitten ist.
POST /realms/platform/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&client_id=api-gateway
&client_secret=<API_GATEWAY_SECRET>
&subject_token=eyJhbGciOiJIUzI1NiIsIn...
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=backend-serviceDie Antwort enthält ein neues Access Token, das nun für den Client backend-service bestimmt ist. Dieses Token kann vom API-Gateway verwendet werden, um auf den geschützten Backend-Dienst zuzugreifen – unter Beibehaltung der Identität des Benutzers. Das neue Token enthält typischerweise folgende Informationen:
- Subject(sub): Benutzer-ID
- Authorized Party (azp): Client-ID des austauschenden Clients (hier: api-gateway)
- Audience (aud): Ziel-Client (hier: backend-service)
Durch diesen Mechanismus erhält der Ziel-Service ein vertrauenswürdiges Token mit klarer Herkunft, begrenztem Scope und eindeutiger Audience. Dieses neue Token kann dann für den nächsten Aufruf verwendet werden. Der ursprüngliche Benutzer bleibt dabei technisch sichtbar: Im JWT bleibt der sub-Claim erhalten, optional ergänzt durch einen act-Claim, der anzeigt, welcher Dienst im Auftrag des Benutzers agiert hat.
Best Practices für den produktiven Einsatz
In produktiven Umgebungen ist dieser Mechanismus ein echter Sicherheitsgewinn. Die Weitergabe überprivilegierter Tokens entfällt. Dienste müssen nicht länger fremde Tokens verarbeiten, die für sie nie ausgestellt wurden. Und der gesamte Ablauf bleibt nachvollziehbar und prüfbar.
Doch so überzeugend die Vorteile sind – ein solcher Mechanismus muss auch sicher betrieben werden. Ein zentrales Prinzip lautet: So restriktiv wie möglich, so offen wie nötig. Der Token-Austausch sollte nur zwischen explizit berechtigten Diensten stattfinden. Eine zu großzügige Policy-Konfiguration konterkariert die Vorteile des Ansatzes. Das Prinzip der minimalen Berechtigung muss auch hier gelten.
Sicherheitsempfehlung: Kein Token Exchange in Public Clients
Ein weiterer sicherheitsrelevanter Aspekt betrifft den Einsatz von Public Clients. Da Public Clients – also Clients ohne eigene Zugangsdaten, wie z. B. das Frontend im obigen Beispiel – nicht in der Lage sind, sich gegenüber dem Token-Endpunkt von Keycloak eindeutig zu authentifizieren, sollten sie keinen Token Exchange durchführen. Das Risiko eines Missbrauchs ist hier besonders hoch, da der Endpoint öffentlich zugänglich ist und keine Absicherung über ein Client-Secret oder eine andere Authentifizierung besteht. Der Token Exchange sollte daher ausschließlich durch vertrauliche Clients (Confidential Clients) mit aktivierter Client-Authentifizierung erfolgen – wie im Beispiel durch das API-Gateway dargestellt.
Logging und Auditing
Für die vollständige Protokollierung von Token-Exchange-Vorgängen – sowohl bei Fehlern als auch bei erfolgreichen Operationen – sollte die Keycloak-Instanz mit den folgenden Umgebungsvariablen konfiguriert werden:
KC_SPI_EVENTS_LISTENER_JBOSS_LOGGING_SUCCESS_LEVEL=info
KC_SPI_EVENTS_LISTENER_JBOSS_LOGGING_ERROR_LEVEL=warnDiese Konfiguration sorgt dafür, dass sowohl erfolgreiche als auch fehlgeschlagene Token Exchanges im Log landen – mit der jeweils passenden Log-Level-Klassifikation. Dadurch wird ein zentrales Audit-Trail möglich, das sowohl für operatives Troubleshooting als auch für externe Prüfungen und Nachvollziehbarkeit essenziell ist.
Darüber hinaus empfiehlt es sich, die Audit Logs – auch User Events genannt – von Keycloak zu aktivieren. Sie liefern Informationen über alle sicherheitsrelevanten Aktionen, darunter auch durchgeführte Token Exchanges. In Keycloak können diese Events über die Admin-Konsole konfiguriert und anschließend via REST oder Event Listener Provider in zentrale Log-Systeme (z. B. SIEM) übertragen werden. Besonders im Umfeld von Datenschutz, GDPR/DSGVO oder branchenspezifischen Regulierungen (z. B. ISO 27001, SOC 2 oder BSI IT-Grundschutz) sind diese Logs eine entscheidende Grundlage für Audits und Nachweispflichten.
Metriken
Keycloak liefert über das Prometheus-Interface die Metrik keycloak_user_events_total, die Aufschluss über alle User Events gibt – darunter auch Token-Exchange-Vorgänge. Um aussagekräftige Dashboards und Warnungen zu ermöglichen, lassen sich diese Metriken mit verschiedenen Labels anreichern:
- realm: zur Unterscheidung von Mandanten oder Umgebungen
- idp: zur Identifikation des verwendeten Identity Providers (z. B. OIDC oder SAML)
- clientId: der anfragende Client, z. B. das API-Gateway
Ein besonders wertvolles Merkmal dieser Metrik ist das nicht konfigurierbare Label error, das automatisch bei fehlgeschlagenen Events gesetzt wird. Darüber lassen sich unterschiedliche Fehlerarten wie nicht autorisierte Token Exchanges, ungültige Tokens oder falsche Audience-Ziele präzise unterscheiden. Damit ermöglicht die Metrik nicht nur ein Monitoring des Volumens erfolgreicher Token Exchanges, sondern auch eine granulare Fehleranalyse und die Grundlage für Alerting-Strategien.
IT-Security auf den diesjährigen IT-Tagen
Spannende Vorträge und Workshops zum Thema IT-Security erwarten Euch auch auf den IT-Tagen, der Jahreskonferenz von Informatik Aktuell. Die IT-Konferenz findet jedes Jahr im Dezember in Frankfurt statt – dieses Jahr vom 08.-11.12.
Ausblick auf Keycloak-Version 26.2
Zum Zeitpunkt dieses Artikels war Token Exchange in Keycloak noch als Preview-Feature deklariert. Mit Version 26.2 ist dies jedoch nicht mehr der Fall: Token Exchange ist jetzt vollständig unterstützt und nicht länger als experimentell gekennzeichnet. Damit entfällt die Notwendigkeit, das Feature über den Startparameter zu aktivieren. Auch die Unterstützung im Bereich Monitoring und Feingranularität wurde weiter ausgebaut. Der Weg ist somit frei, Token Exchange als strategischen Baustein in moderne Plattformarchitekturen zu integrieren.
Für Architekten bedeutet das: Wer Plattformen baut, sollte Token Exchange konzeptionell mitdenken. Wer mit Keycloak arbeitet, kann heute schon damit beginnen. Und wer Sicherheit nicht als nachträgliche Compliance-Disziplin, sondern als integralen Bestandteil der Architektur versteht, wird an diesem Mechanismus kaum vorbeikommen. Keycloak liefert das Fundament – es liegt an uns, es richtig zu nutzen.
















