Skip to content

Token Exchange Grant

Der OAuth 2.0 Token Exchange (RFC 8693) ist ein offen spezifizierter Flow, mit dem ein Client ein bestehendes Token (das subject_token) gegen ein neues Token mit anderem Issuer, anderer Audience oder anderem Scope eintauschen lässt. Optional kann ein zweites Token (das actor_token) mitgeliefert werden, um eine Delegierungskette mit kryptographisch nachgewiesenem Akteur zu belegen.

Im CMI STS deckt der Token-Exchange-Grant Anwendungsfälle ab, die mit dem On-Behalf-Of-Grant nicht oder nur eingeschränkt abbildbar sind:

  • Cross-Tenant: Ein Subject-Token, das ein anderer CMI-STS-Mandant ausgestellt hat, wird im Ziel-Mandanten gegen ein dort gültiges Access-Token getauscht. Die externe Identität (external_idp und external_sub) wird im Ziel-Mandanten gegen die dortige CMI-Server-Konfiguration aufgelöst.
  • Partner-STS: Ein Subject-Token, das ein föderierter Partner-STS (kein Identity Provider, sondern ein vorgelagerter Security Token Service) ausgestellt hat, wird gegen ein Access-Token für den CMI Server getauscht.
  • Delegation mit Akteur-Nachweis: Eine Web-API ruft den CMI Server stellvertretend für den Benutzer auf und weist sich gleichzeitig per actor_token selbst aus. Das ausgestellte Token erhält einen act-Claim gemäss RFC 8693 §4.1; Mehrhop-Delegierung bleibt strukturell nachvollziehbar (act.act-Verschachtelung).

Hinweis: Der OBO-Grant (urn:ietf:params:oauth:grant-type:jwt-bearer) bleibt für reine OIDC-IDP-Tausch-Fälle (z. B. Azure AD App-Registrierung → CMI-Access-Token) bestehen und wird durch den Token-Exchange-Grant nicht ersetzt. Wer einen Akteur belegen, einen Partner-STS einbinden oder Cross-Tenant arbeiten muss, verwendet den Token-Exchange-Grant.

Ein vollständiges End-to-End-Beispiel mit zwei STS-Installationen und drei Mandanten ist im Cross-Domain-Token-Chaining-Walkthrough dokumentiert.

Anforderungen

Damit der CMI STS einen Token-Exchange-Request akzeptiert, müssen folgende Bedingungen erfüllt sein:

  • Es muss eine Trust-Quelle für das subject_token konfiguriert sein:
  • OIDC-IDP (OpenIdConnectIdPs) — gleiche Konfiguration wie für den OBO-Grant, siehe Externe Identity Provider (IDP).
  • Partner-STS (TrustedPartnerStsIssuers) — siehe Kapitel Konfiguration: Partner-STS.
  • Eigener CMI STS (Cross-Tenant) — der Quell-Mandant muss im Ziel-Mandanten unter TrustedSourceTenants eingetragen sein, siehe Kapitel Konfiguration: Cross-Tenant.
  • Es werden ausschliesslich JWT-formatierte Subject- und Actor-Tokens unterstützt: urn:ietf:params:oauth:token-type:jwt und urn:ietf:params:oauth:token-type:access_token. SAML-Assertions und opake Tokens werden abgelehnt.
  • Der Parameter resource wird nicht unterstützt; das Ziel des Tokens muss zwingend über audience benannt werden.
  • Der ausgestellte Token-Typ ist immer urn:ietf:params:oauth:token-type:access_token (Bearer). ID-Tokens, Refresh-Tokens und SAML-Assertions werden nicht ausgestellt.
  • Subject- und Actor-Tokens sind auf 16 KB begrenzt; grössere Tokens werden abgelehnt, bevor die Signatur geprüft wird.

Wichtig: Der CMI STS validiert Subject- und Actor-Token gegen die konfigurierte Trust-Quelle. Pflicht-Prüfungen sind Signatur, iss, sub (Tokens ohne nicht-leeren sub-Claim werden abgelehnt), exp und iat. Für Subject-/Actor-Token gilt kein maximaler Lebensdauer-Cap und kein Vergangenheitsfenster (es sind reguläre Access-Tokens, z. B. mit 60 Minuten Laufzeit): exp begrenzt die Gültigkeit, ein iat in der Zukunft (jenseits der Uhrendrift) wird abgelehnt, und das ausgestellte Token überlebt die Quell-Tokens ohnehin nie (siehe unten). Der aud-Claim wird nur geprüft, wenn die Trust-Quelle eine Audience vorgibt: bei Partner-STS immer (TrustedPartnerStsIssuers.Audience), bei OIDC-IDP- und Cross-Tenant-Quellen greifen stattdessen AllowedSubjectAudiences bzw. AllowedSourceTenants. Es erfolgt keine Online-Introspection beim ausstellenden System.

Konfiguration: CMI STS

Ein regulärer Duende-Client wird angelegt; siehe auch Clients. Die folgende Konfiguration dient als Referenzkonfiguration. Alle Schlüssel unter TokenExchange sind CMI-spezifische Erweiterungen.

{
  // ...
  "tenants": {
    "mandant": {
      "Clients": [
        {
          "ClientId": "<CLIENT_ID>",
          "AllowedGrantTypes": ["urn:ietf:params:oauth:grant-type:token-exchange"],
          "AllowedScopes": ["metatool"],
          "ClientSecrets": [{ "value": "..." }],
          "TokenExchange": {
            // Erlaubte Ziel-Audiences des Requests:
            "AllowedAudiences": ["https://cmi-server.example.com"],

            // Eine oder mehrere Subject-Token-Quellen freischalten:
            "AllowedSourceIssuers": ["azuread-tenant1", "https://login.microsoftonline.com/<TENANT_ID>/v2.0"],
            "AllowedSourceTenants": ["mandant-partner"],

            // Bei OIDC-IDP-Quellen erforderlich: erlaubte aud-Werte des Subject-Tokens:
            "AllowedSubjectAudiences": ["api://cmi-metatool"],

            // Delegation (actor_token) — standardmässig deaktiviert:
            "AllowDelegation": true,
            "AllowedActorIssuers": ["azuread-tenant1"],
            "AllowedActorAudiences": ["api://cmi-metatool"],

            "ValidationClockSkewSeconds": 60
          }
        }
      ]
    }
  }
}
  • ClientId (erforderlich): Eine beliebige Client ID.
  • AllowedGrantTypes (erforderlich): Der Grant Type urn:ietf:params:oauth:grant-type:token-exchange aktiviert den Token-Exchange-Grant.
  • AllowedScopes (erforderlich): Scopes, die im ausgestellten Access-Token enthalten sein dürfen. Der CMI Server erwartet den Scope metatool.
  • ClientSecrets (erforderlich): Ein oder mehrere Client Secrets als SHA-512-Hash, siehe Clients.
  • TokenExchange (erforderlich): CMI-spezifische Optionen des Token-Exchange-Grants. Alle Allow-Listen sind JSON-String-Arrays. Leere oder fehlende Listen bedeuten Deny: Ohne explizite Einträge wird keine Quelle, keine Audience und kein Akteur akzeptiert. Wildcards werden nicht unterstützt.
  • AllowedAudiences (erforderlich): Erlaubte Werte für den Request-Parameter audience. Alle angefragten Audiences müssen in der Liste enthalten sein (Schutz gegen Audience-Smuggling).
  • AllowedSourceIssuers (bedingt erforderlich): Erlaubte Subject-Token-Aussteller, geprüft gegen den internen Schlüssel (aus OpenIdConnectIdPs oder TrustedPartnerStsIssuers) oder den Roh-iss-Wert (Discovery-Issuer-URI). Ein Treffer auf einem der beiden genügt.
  • AllowedSourceTenants (bedingt erforderlich): Erlaubte fremde CMI-STS-Mandanten (Cross-Tenant) als Quelle des Subject-Tokens.
  • AllowedSubjectAudiences (erforderlich bei OIDC-IDP-Quellen): Erlaubte aud-Werte des Subject-Tokens, wenn dessen Aussteller ein OIDC-IDP ist. Ein OIDC-Issuer stellt Tokens für viele Anwendungen aus; ohne diese Audience-Bindung könnte ein Token, das der IDP für eine fremde Anwendung ausgestellt hat, eingetauscht werden (Confused-Deputy). Leer ⇒ kein OIDC-Subject-Token wird akzeptiert. Trägt das Token ein aud-Array, genügt ein Treffer. Nicht zu verwechseln mit AllowedAudiences (Ziel-Audience des Requests). Bei Partner-STS-Quellen wird die Audience am Issuer-Eintrag (Audience) gepinnt, bei Cross-Tenant über AllowedSourceTenants — dort ist diese Liste wirkungslos.
  • AllowDelegation (optional, Standardwert: false): Aktiviert die Annahme eines actor_token. Ohne diese Freischaltung wird ein mitgesendetes actor_token mit unauthorized_client abgelehnt — noch bevor es kryptographisch geprüft wird. Erst AllowDelegation=true und mindestens ein Eintrag in AllowedActorIssuers schalten Delegation frei.
  • AllowedActorIssuers (erforderlich bei Delegation): Erlaubte Aussteller des actor_token (Issuer-Key oder Roh-iss-URI). Leere Liste ⇒ strikter Deny — Delegation kann nicht versehentlich nur durch Setzen von AllowDelegation aktiviert werden. Jeder sub eines erlaubten Actor-Issuers wird akzeptiert; ein bestimmter Akteur kann zusätzlich über den may_act-Claim im Subject-Token gepinnt werden (siehe Beispiel mit Actor-Token).
  • AllowedActorAudiences (erforderlich bei OIDC-IDP-Actor-Quellen): Erlaubte aud-Werte des Actor-Tokens, wenn dessen Aussteller ein OIDC-IDP ist. Actor-Pendant zu AllowedSubjectAudiences. Leer ⇒ kein OIDC-Actor-Token wird akzeptiert. Bei Partner-STS- oder Cross-Tenant-Actor-Quellen wirkungslos.
  • ValidationClockSkewSeconds (optional, Standardwert: 60): Akzeptierte Uhrendrift in Sekunden für die exp/nbf/iat-Validierung der Subject- und Actor-Tokens. Der Wert ist ein Operator-Entscheid und wird nach oben nicht begrenzt; negative Werte werden als 0 behandelt.

Wichtig: Pro Subject-Token-Quelle muss mindestens eine der beiden Allow-Listen (AllowedSourceIssuers, AllowedSourceTenants) gefüllt sein. Ohne Eintrag wird der Tausch unabhängig vom Token-Inhalt verweigert.

Hinweis: Scopes werden nicht separat am TokenExchange-Block konfiguriert. Ausgestellt wird die Schnittmenge aus angefragten Scopes, AllowedScopes des Clients und den Scopes des Subject-Tokens; eine leere Schnittmenge führt zur Ablehnung.

Konfiguration: Partner-STS

Ein Partner-STS (z. B. ein Token-Service eines Drittsystems) wird pro Mandant unter TrustedPartnerStsIssuers eingetragen. Pro Eintrag werden Authority, erwartete Audience und optional Pflicht-Claims hinterlegt.

{
  "tenants": {
    "mandant": {
      "TrustedPartnerStsIssuers": {
        "partner-sts-1": {
          "Authority": "https://partner-sts.example.com",
          "Audience": "https://mandant.cmi.example.com",
          "RequiredClaimRequirements": {
            "ver": "1.0",
            "auth_method": "mfa"
          }
        }
      }
    }
  }
}
  • Authority (erforderlich): URL des Partner-STS. Wird verwendet, um das OpenID-Discovery-Dokument (JWKS-Endpoint, iss-Wert) zu laden. JWKS wird automatisch aktualisiert; bei Ausfällen greift ein Last-Known-Good-Fallback.
  • Audience (erforderlich): Erwarteter aud-Claim der Tokens des Partner-STS. Tokens mit abweichender Audience werden abgelehnt.
  • RequiredClaimRequirements (optional): Pflicht-Claims, die im Subject-/Actor-Token vorhanden sein und exakt mit dem angegebenen Wert übereinstimmen müssen.

Der Schlüssel der Map (im Beispiel partner-sts-1) ist der Issuer-Key, der in TokenExchange.AllowedSourceIssuers und TokenExchange.AllowedActorIssuers verwendet wird.

Konfiguration: Cross-Tenant

Soll ein anderer CMI-STS-Mandant als Quelle akzeptiert werden, muss der Ziel-Mandant den Quell-Mandanten unter TrustedSourceTenants deklarieren. Pro Quelle wird ein Mapping der externen IDP-Schlüssel hinterlegt, falls Quell- und Ziel-Mandant unterschiedliche IDP-Schlüssel für denselben föderierten IDP verwenden.

{
  "tenants": {
    "mandant-ziel": {
      "TrustedSourceTenants": {
        "mandant-quelle": {
          "ProviderKeyMapping": {
            "azuread-source": "azuread-target"
          }
        }
      },
      "OpenIdConnectIdPs": {
        "azuread-target": { /* ... übliche OIDC-IDP-Konfiguration ... */ }
      },
      "Clients": [
        {
          "ClientId": "cross-tenant-bridge",
          "AllowedGrantTypes": ["urn:ietf:params:oauth:grant-type:token-exchange"],
          "AllowedScopes": ["metatool"],
          "ClientSecrets": [{ "value": "..." }],
          "TokenExchange": {
            "AllowedAudiences": ["https://cmi-server-ziel.example.com"],
            "AllowedSourceTenants": ["mandant-quelle"]
          }
        }
      ]
    }
  }
}
  • Das Subject-Token des Quell-Mandanten muss die Pflicht-Claims external_idp (Provider-Key in der Quell-Mandanten-Konfiguration) und external_sub (Subject des Benutzers beim föderierten IDP) tragen.
  • ProviderKeyMapping arbeitet als Default-Deny: Jeder external_idp-Wert, der getauscht werden soll, braucht einen expliziten Eintrag — so wird die Cross-Tenant-Identität nie versehentlich gegen einen unbeabsichtigten lokalen Provider aufgelöst. Verwenden Quell- und Ziel-Mandant denselben Provider-Key-Namensraum, wird der Key als identisches Paar eingetragen (z. B. "aad-mandantA": "aad-mandantA").
  • Zusätzlich muss der Quell-Mandant im Client unter TokenExchange.AllowedSourceTenants aufgeführt sein. Ein Mandant, der dort nicht aufgeführt ist, darf den Tausch nicht auslösen — auch dann nicht, wenn er in TrustedSourceTenants für einen anderen Client eingetragen wäre.

Der Ablauf eines Cross-Tenant-Tauschs in fünf Schritten:

  1. Der CMI STS erkennt anhand des iss-Werts, dass das Subject-Token von einem eigenen Mandanten stammt.
  2. Der Quell-Mandant wird gegen TokenExchange.AllowedSourceTenants des Clients geprüft.
  3. external_idp und external_sub werden aus dem Subject-Token gelesen.
  4. ProviderKeyMapping bildet den external_idp-Wert auf den Provider-Key des Ziel-Mandanten ab; der CMI Server des Ziel-Mandanten wird mit diesem Key und external_sub aufgerufen.
  5. Der CMI Server löst den Benutzer auf. Subject und Claims des ausgestellten Tokens stammen ausschliesslich vom CMI Server des Ziel-Mandanten — Claims aus dem Request werden ignoriert.

Ein vollständiges Cross-Tenant- und Partner-STS-Szenario inklusive Verifikation ist im Cross-Domain-Token-Chaining-Walkthrough beschrieben.

Backend-Aufruf: CMI Server Identity Resolution

Die Identitätsauflösung gegen den CMI Server erfolgt über die private STS-Schnittstelle POST {CmiPrivateUri}api/core/sts/AuthenticateExternal. Der CMI Server liefert Subject und Claims, die unverändert in das ausgestellte Access-Token übernommen werden. Fehler des Backends werden auf Token-Exchange-Fehlercodes gemappt, siehe Fehlercodes.

Grant Request

Der Token-Exchange-Grant wird über den Token-Endpunkt ausgeführt, der im Discovery-Dokument des CMI STS publiziert ist ({tenant id}/identity/.well-known/openid-configuration, grant_types_supported enthält urn:ietf:params:oauth:grant-type:token-exchange).

Der Request muss wie folgt erfolgen:

(Zeilenumbrüche und fehlende URL-Codierung dienen nur der besseren Lesbarkeit)

POST /{tenant id}/identity/connect/token HTTP/1.1
Host: {CMI STS host}
Content-Type: application/x-www-form-urlencoded
Content-Length: ...

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&client_id=<CLIENT_ID>
&client_secret=<CLIENT_SECRET>
&subject_token=<SUBJECT_TOKEN>
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=https://cmi-server.example.com
&scope=metatool

Pflicht-Parameter:

  • grant_type — fest urn:ietf:params:oauth:grant-type:token-exchange.
  • subject_token — das vorhandene Token, das getauscht werden soll. Ohne Bearer-Preamble übermitteln.
  • subject_token_typeurn:ietf:params:oauth:token-type:jwt oder urn:ietf:params:oauth:token-type:access_token. Andere Werte (z. B. SAML) werden abgelehnt.
  • audience — angefragte Ziel-Audience. Dient der Autorisierung: jeder Wert muss in TokenExchange.AllowedAudiences enthalten sein, sonst wird der Request abgelehnt (STS930). Mehrfach-Werte werden unterstützt. Der Wert wird nicht in den aud-Claim des ausgestellten Tokens übernommen (siehe Hinweis unter Grant Response).
  • client_id und client_secret — Client-Authentisierung.

Optionale Parameter:

  • scope — Space-separierter Scope-Request. Leer ⇒ es werden die im Client erlaubten Scopes ausgegeben, soweit sie auch im Subject-Token enthalten sind.
  • actor_token und actor_token_type — Akteur-Nachweis für Delegation, siehe Beispiel mit Actor-Token.
  • requested_token_type — wenn gesetzt, muss der Wert urn:ietf:params:oauth:token-type:access_token sein.

Der Parameter resource wird nicht unterstützt; das Ziel ist immer über audience zu benennen.

Grant Response

Die Antwort entspricht RFC 8693 §2.2. Erfolg ⇒ HTTP 2XX, Fehler ⇒ HTTP 4XX mit error/error_description und CMI-spezifischen error_codes. Alle Token-Exchange-Fehlercodes (Bereich STS92x bis STS951) sind in ErrorCodes.md dokumentiert und werden hier nicht dupliziert.

Erfolgsantwort

{
  "access_token": "ey...",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "metatool",
  "error_codes": ["STS920"],
  "trace_id": "0HMV...",
  "timestamp": "2026-05-20T08:42:17Z"
}
  • access_token (erforderlich): Das ausgestellte Bearer-Access-Token.
  • issued_token_type (erforderlich): Der Typ des ausgestellten Tokens, immer urn:ietf:params:oauth:token-type:access_token.
  • token_type (erforderlich): Fest Bearer.
  • expires_in (optional): Lebensdauer in Sekunden. Berechnet als Minimum über die Restlaufzeit des Subject-Tokens, die Restlaufzeit des Actor-Tokens (falls vorhanden) und InternalTokenLifetimeSeconds (falls gesetzt), mit einer Untergrenze von 60 Sekunden. Das ausgestellte Token kann die Quell-Tokens damit nie überleben. Lässt sich keine Obergrenze ableiten, gilt ein konservativer Fallback von 300 Sekunden.
  • scope (optional): Wird nur ausgegeben, wenn die Scope-Schnittmenge vom Request abweicht (Scope-Downgrade).
  • error_codes (optional): Enthält bei Erfolg den Erfolgscode STS920 (analog STS900 beim OBO-Grant).
  • trace_id (optional): Trace-ID für die Korrelation mit den STS-Logs.
  • timestamp (optional): UTC-Zeitstempel der Antwort.

Ein Refresh-Token wird nicht ausgestellt: der Scope offline_access wird beim Token-Exchange immer aus der ausgestellten Scope-Menge entfernt, auch wenn Client und Subject-Token ihn autorisieren. Andernfalls würde der Refresh-Pfad den auf die Quell-Token-Restlaufzeit berechneten Lifetime-Cap umgehen.

Hinweis zur Audience: Das ausgestellte Token trägt die statische Audience des CMI STS ({issuer}/resources), nicht die im Request angefragte audience. TokenExchange.AllowedAudiences ist ein Autorisierungs-Gate (welche Audiences ein Client anfragen darf), keine kryptographische Bindung des Tokens an ein Ziel. Resource Server validieren das ausgestellte Token daher über den Issuer (iss) und die STS-Audience, nicht über einen zielspezifischen aud-Wert.

Fehlerantwort

{
  "error": "invalid_request",
  "error_description": "subject_token validation failed",
  "error_codes": ["STS925"],
  "trace_id": "0HMV...",
  "timestamp": "2026-05-20T08:42:17Z"
}
  • error (erforderlich): invalid_request, invalid_target, invalid_scope oder unauthorized_client (RFC 8693 §2.2.2).
  • error_description (optional): Kurze, lesbare Begründung ohne sensible Daten.
  • error_codes (optional): CMI-spezifische Fehlercodes, dokumentiert in ErrorCodes.md.

Beispiel mit Actor-Token

Eine Web-API ruft den CMI Server stellvertretend für den eingeloggten Benutzer auf und weist sich gleichzeitig per actor_token aus. Der Client muss TokenExchange.AllowDelegation=true und mindestens einen Eintrag in TokenExchange.AllowedActorIssuers haben.

(Zeilenumbrüche und fehlende URL-Codierung dienen nur der besseren Lesbarkeit)

POST /{tenant id}/identity/connect/token HTTP/1.1
Host: {CMI STS host}
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&client_id=<CLIENT_ID>
&client_secret=<CLIENT_SECRET>
&subject_token=<SUBJECT_TOKEN>
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&actor_token=<ACTOR_TOKEN>
&actor_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=https://cmi-server.example.com
&scope=metatool

Das ausgestellte Token enthält gemäss RFC 8693 §4.1 einen act-Claim als JSON-Objekt. Hat das Subject-Token bereits einen act-Claim aus einer früheren Delegationsstufe, wird die bestehende Kette unter dem neuen Akteur verschachtelt (act.act):

// JWT-Payload-Auszug (illustrativ)
{
  "iss": "https://sts.example.com/mandant",
  "sub": "user-1234",
  "aud": "https://sts.example.com/mandant/resources", // statische STS-Audience, nicht die angefragte audience
  "exp": 1747746000,
  "act": {
    "sub": "app-web-api",
    "iss": "https://login.microsoftonline.com/<TENANT_ID>/v2.0",
    "act": {
      "sub": "app-gateway", // ursprünglicher Akteur einer früheren Delegationsstufe
      "iss": "https://login.microsoftonline.com/<TENANT_ID>/v2.0"
    }
  }
}

may_act-Pre-Authorization (optional): Trägt das Subject-Token einen may_act-Claim (RFC 8693 §4.4), gilt er als Vorab-Autorisierung des Subject-Token-Ausstellers für einen bestimmten Akteur. Der CMI STS erzwingt strikte Übereinstimmung: may_act.sub (und may_act.iss, falls vorhanden) müssen mit dem actor_token übereinstimmen, und ein Subject-Token mit may_act verlangt zwingend ein actor_token. Verstösse werden abgelehnt, siehe ErrorCodes.md.

Fehlercodes

Alle Fehlercodes des Token-Exchange-Grants (Bereich STS92x bis STS951) sind zentral in ErrorCodes.md dokumentiert. STS920 ist der Erfolgscode.

Sicherheitsmodell

Der Token-Exchange-Grant folgt durchgängig dem Default-Deny-Prinzip: Vor dem Einbau einer neuen Quelle, eines neuen Akteurs oder einer neuen Audience müssen die jeweiligen Allow-Listen explizit ergänzt werden; das Entfernen eines Eintrags sperrt die Quelle sofort. Die wichtigsten Hardening-Massnahmen:

  • Trust-Chain: Nur Issuer aus TrustedPartnerStsIssuers, OpenIdConnectIdPs oder TrustedSourceTenants werden akzeptiert; pro Issuer JWKS-Discovery mit Last-Known-Good-Fallback.
  • Audience-Smuggling: All-or-Nothing-Check gegen AllowedAudiences; bei OIDC-Quellen zusätzlich Audience-Bindung der Subject-/Actor-Tokens über AllowedSubjectAudiences/AllowedActorAudiences.
  • Subject-Spoofing: Die Identität stammt ausschliesslich aus dem validierten Subject-Token bzw. der CMI-Server-Antwort; Identitäts-Claims im Request-Body werden ignoriert.
  • Delegation-Chain-Splicing: AllowDelegation und AllowedActorIssuers müssen beide gesetzt sein; may_act-Strict-Match; die act-Kette ist auf eine maximale Tiefe von 10 begrenzt.
  • Scope-Elevation: Ausgestellt wird nur die Schnittmenge aus angefragten Scopes, Client-Scopes und Subject-Token-Scopes.
  • Token-Lifetime-Extension: Das ausgestellte Token ist nie länger gültig als Subject- und Actor-Token (kryptographisch durchgesetzter Output-Lifetime-Cap). Für die eingereichten Tokens gibt es keinen zusätzlichen Lebensdauer-Cap; ein iat in der Zukunft wird abgelehnt.
  • JWT-Profil: Signatur-Algorithmen-Allow-Liste (RS256/PS256/ES256), Pflicht-Claims iss/sub/exp/iat, Token-Grössen-Limit 16 KB.
  • Replay: Kein jti-Replay-Cache — innerhalb der Token-Lebensdauer ist ein Mehrfach-Tausch desselben subject_token möglich; die Eingrenzung erfolgt über die kurzen Lebensdauern und die Audience-Bindung.
  • Audit-Datenlecks: Subject und Actor werden im Audit-Log nur als SHA-256-Prefix gespeichert, nie im Klartext.

Diagnose und Metriken

Der CMI STS emittiert zu jedem Token-Exchange-Request genau ein strukturiertes Audit-Event (TokenExchangeAuditEvent mit Client, Quelle, gehashten Subject-/Actor-Werten, Outcome, Fehlercode, Jti und TraceId) sowie Metriken:

  • CMI_STS3_tokenexchange_counter — zählt alle Token-Exchange-Requests. Labels: Tenant, CMI_STS3_tokenexchange_outcome (success oder der STS-Fehlercode, z. B. STS925) und CMI_STS3_tokenexchange_source_kind (InternalSts, OidcIdP, PartnerSts oder none bei frühen Ablehnungen ohne Subject-Token-Inspektion).
  • CMI_STS3_tokenexchange_delegation_counter — zählt Requests, die ein actor_token mitgeliefert haben, unabhängig vom Ergebnis.

Alerting-Hinweise: Häufungen des Outcomes STS930 deuten auf falsch konfigurierte Clients oder auf das Durchprobieren von Audiences hin; wiederkehrende STS925/STS946 auf einer source_kind deuten auf eine Drift zwischen Partner-STS-Konfiguration und dessen tatsächlichem Token-Inhalt hin. Jede Fehlerantwort trägt eine trace_id, mit der sich STS-Logs, Audit-Events und die HTTP-Antwort des Clients verknüpfen lassen.