OAuth2 für den MCP Client und der Identity Assertion Grant | eSeGeCe Blog

OAuth2 für den MCP Client und der Identity Assertion Grant

· Komponenten

Entfernte MCP Server sind geschützte Ressourcen, und die Server, die tatsächlich in Betrieb sind, stehen hinter einem Autorisierungsserver. Bisher konnte der MCP Client von sgcWebSockets einen API Key oder einen eigenen Header senden. Das reichte für ein statisches Token, aber alles mit einer Lebensdauer war Ihre Sache: Sie riefen den Token-Endpunkt selbst auf, werteten die Antwort aus, behielten die Uhr im Blick und erneuerten das Token, bevor es ablief.

sgcWebSockets 2026.7 verlagert diese Arbeit in die Komponente. Der MCP Client kann sich jetzt mit OAuth2 authentifizieren, holt also das Access Token, speichert es zwischen und verwendet es bei jeder Anfrage wieder, bis es abläuft. Dieselbe Version fügt einen neuen Grant-Typ hinzu, den Identity Assertion Authorization Grant, der eine Identität aus einer Domäne in eine andere verkettet, ohne den Benutzer durch eine zweite interaktive Anmeldung zu schicken.

Client, nicht Server

Zuerst eine Klarstellung, denn die beiden Seiten werden leicht verwechselt. sgcWebSockets 2026.5 brachte OAuth 2.1 auf dem MCP Server: Discovery-Endpunkte, PKCE und dynamische Client-Registrierung, damit sich ein browserbasierter MCP Client gegenüber Ihrem Delphi Server autorisieren kann. Das wurde bereits behandelt. In diesem Beitrag geht es um das andere Ende der Leitung, den MCP Client, der jetzt ein eigenes Token beschafft und es dem entfernten MCP Server vorlegt, mit dem er sich verbindet.

Was der MCP Client bisher konnte

Zwei Optionen, beide weiterhin verfügbar und beide unverändert. Ein API Key, gesendet als Authorization: Bearer <value>, und ein eigener Header für Mandanten- oder Regions-Routing. Der frühere Beitrag MCP Authentifizierung in Delphi behandelt beides im Detail.

MCPClient.MCPOptions.AuthenticationOptions.ApiKey.Enabled := True;
MCPClient.MCPOptions.AuthenticationOptions.ApiKey.Value := 'YOUR_API_KEY';

MCPClient.MCPOptions.AuthenticationOptions.CustomHeader.Enabled := True;
MCPClient.MCPOptions.AuthenticationOptions.CustomHeader.Header := 'X-Tenant';
MCPClient.MCPOptions.AuthenticationOptions.CustomHeader.Value := 'Retail';

Was fehlte, war alles dazwischen: ein Token-Endpunkt, eine Client-ID und ein Secret, ein Scope, eine Ablaufzeit. Genau das ergänzt das neue OAuth2-Objekt.

OAuth2 im MCP Client

Die Authentifizierung hat jetzt einen dritten Zweig, MCPOptions.AuthenticationOptions.OAuth2, vom Typ TsgcWSMCPClientAuthenticationOAuth2_Options. Die Eigenschaften sind die, die man von einem OAuth2 Client erwartet: Enabled, TokenURL, ClientId, ClientSecret, Scope, ein GrantType und ein IdentityAssertionOptions-Objekt, das der neue Grant verwendet. Es gibt eine Methode, GetBearerToken, und normalerweise rufen Sie sie nie auf: Der Client ruft sie bei jeder MCP-Anfrage für Sie auf und legt das Ergebnis in den Authorization-Header.

Der Grant wird mit GrantType ausgewählt, vom Typ TsgcOAuth2GrantTypes:

TsgcOAuth2GrantTypes = (auth2Code, auth2CodePKCE, auth2ClientCredentials,
  auth2ResourceOwnerPassword, auth2DeviceCode, auth2IdentityAssertion);

Der letzte Wert, auth2IdentityAssertion, ist neu in 2026.7. Die anderen wurden vom OAuth2 HTTP Client bereits unterstützt und sind jetzt auch aus dem MCP Client heraus erreichbar.

Client Credentials: der häufigste Fall

Ein Dienst, der mit einem entfernten MCP Server spricht, ohne dass ein Benutzer beteiligt ist, entspricht dem Client Credentials Grant. Tragen Sie den Token-Endpunkt und die Zugangsdaten ein, aktivieren Sie ihn und verbinden Sie sich:

uses
  sgcAI, sgcAI_MCP_Client, sgcHTTP_OAuth_Types;

var
  MCPClient: TsgcWSAPIClient_MCP;
begin
  MCPClient := TsgcWSAPIClient_MCP.Create(nil);

  MCPClient.MCPOptions.HttpOptions.URL := 'https://mcp.example.com/mcp';

  MCPClient.MCPOptions.AuthenticationOptions.OAuth2.Enabled := True;
  MCPClient.MCPOptions.AuthenticationOptions.OAuth2.GrantType := auth2ClientCredentials;
  MCPClient.MCPOptions.AuthenticationOptions.OAuth2.TokenURL := 'https://auth.example.com/oauth2/token';
  MCPClient.MCPOptions.AuthenticationOptions.OAuth2.ClientId := 'YOUR_CLIENT_ID';
  MCPClient.MCPOptions.AuthenticationOptions.OAuth2.ClientSecret := 'YOUR_CLIENT_SECRET';
  MCPClient.MCPOptions.AuthenticationOptions.OAuth2.Scope := 'mcp.read mcp.tools';

  MCPClient.Initialize;
  MCPClient.ListTools;
end;

Keine Token-Verwaltung in Ihrem Code. Die erste Anfrage, die ein Token benötigt, löst den Grant aus, und jede weitere Anfrage verwendet das Zwischengespeicherte wieder.

Token-Zwischenspeicher und Vorrang vor dem API Key

Das Token wird verzögert beschafft, bei der ersten Anfrage, und mit seiner Ablaufzeit im Speicher gehalten. Solange es gültig ist, verwendet der Client es wieder. Wenn es abläuft, oder wenn der Server nie eine Ablaufzeit mitgeteilt hat und das Token leer zurückkommt, läuft der Grant erneut. Der Client erneuert das Token außerdem etwas vor der angegebenen Ablaufzeit, damit ein Token, das kurz vor dem Verfall steht, nicht mit einer Anfrage gesendet wird, die bei ihrer Ankunft bereits abgelehnt würde.

Der Vorrang ist einfach und sollte klar gesagt werden: wenn OAuth2.Enabled True ist, gewinnt OAuth2. Der Authorization-Header trägt das Bearer Token aus dem Grant, und ApiKey wird ignoriert, auch wenn er ebenfalls aktiviert ist. So können Sie während der Migration einen vorhandenen API Key konfiguriert lassen, dann OAuth2.Enabled umschalten und sonst nichts ändern. CustomHeader ist davon unabhängig und wird in beiden Fällen weiterhin gesendet.

TLS-Optionen im MCP Client

Der HTTP-Transport des MCP Clients stellt jetzt über MCPOptions.HttpOptions.TLSOptions eine eigene TLS-Konfiguration bereit, sodass die Verbindung zum MCP Server und zum Token-Endpunkt genau auf die Einstellungen festgelegt werden kann, die Ihre Umgebung verlangt, statt auf die Standardwerte.

MCPClient.MCPOptions.HttpOptions.TLSOptions.Version := tls1_3;
MCPClient.MCPOptions.HttpOptions.TLSOptions.VerifyCertificate := True;
MCPClient.MCPOptions.HttpOptions.TLSOptions.RootCertFile := 'C:\certs\ca-bundle.pem';
MCPClient.MCPOptions.HttpOptions.TLSOptions.IOHandler := iohOpenSSL;   // or iohSChannel on Windows

Der Identity Assertion Grant

Jetzt der interessante Teil. auth2IdentityAssertion ist ein von der IETF definierter OAuth2 Grant, um eine Identität über Domänengrenzen hinweg zu verketten, und er folgt dem Muster eines Token-Austauschs.

Die Situation, die er löst, sieht so aus. Ihre Anwendung besitzt bereits ein Token, das belegt, wer der Benutzer in Domäne A ist, typischerweise ein ID Token des Identitätsanbieters, bei dem sich der Benutzer angemeldet hat. Jetzt müssen Sie einen MCP Server in Domäne B aufrufen, der das Token aus Domäne A nicht akzeptiert: Er vertraut seinem eigenen Autorisierungsserver, nicht dem eines anderen. Der Benutzer ist anwesend und bereits authentifiziert, und ihn durch eine zweite interaktive Anmeldung zu schicken, ist genau die Reibung, die Sie vermeiden wollen.

Der Identity Assertion Grant ist der Ausweg. Sie legen das Token aus Domäne A als Subject Token vor, und der Austausch liefert ein Token zurück, das auf die angeforderte Audience oder Resource zugeschnitten ist und das Domäne B akzeptiert. Deshalb sind die Optionen so aufgebaut, wie sie sind: SubjectToken und SubjectTokenType beschreiben, was Sie besitzen, RequestedTokenType, Audience und Resource beschreiben, was Sie haben wollen, und mit ActorToken / ActorTokenType benennen Sie den Dienst, der im Namen des Benutzers handelt, wenn das Ziel es verlangt.

Es ist ein mehrstufiger Austausch, und der Client durchläuft ihn für Sie. Das Subject Token geht an den Autorisierungsserver der anfragenden Partei (RequestingPartyTokenURL, mit RequestingPartyClientId und RequestingPartyClientSecret), der eine domänenübergreifende Assertion zurückgibt. Diese Assertion wird dann dem Autorisierungsserver der Ressource unter TokenURL vorgelegt, der das Access Token ausstellt, das der MCP Client tatsächlich sendet. Wenn Sie die Assertion bereits anderweitig haben, setzen Sie Assertion, und der erste Schritt entfällt.

with MCPClient.MCPOptions.AuthenticationOptions.OAuth2 do
begin
  Enabled := True;
  GrantType := auth2IdentityAssertion;

  // resource authorization server (domain B): issues the token the MCP server accepts
  TokenURL := 'https://auth.domain-b.com/oauth2/token';
  ClientId := 'YOUR_CLIENT_ID';
  ClientSecret := 'YOUR_CLIENT_SECRET';
  Scope := 'mcp.tools';

  // the identity you already hold in domain A
  IdentityAssertionOptions.SubjectToken := vIdTokenFromDomainA;
  IdentityAssertionOptions.SubjectTokenType := 'urn:ietf:params:oauth:token-type:id_token';
  IdentityAssertionOptions.RequestedTokenType := 'urn:ietf:params:oauth:token-type:id-jag';

  // where the exchange happens, and who you are exchanging as
  IdentityAssertionOptions.RequestingPartyTokenURL := 'https://auth.domain-a.com/oauth2/token';
  IdentityAssertionOptions.RequestingPartyClientId := 'DOMAIN_A_CLIENT_ID';
  IdentityAssertionOptions.RequestingPartyClientSecret := 'DOMAIN_A_CLIENT_SECRET';

  // what the resulting token is for
  IdentityAssertionOptions.Audience := 'https://mcp.domain-b.com';
  IdentityAssertionOptions.Resource := 'https://mcp.domain-b.com/mcp';
end;

MCPClient.Initialize;

Aus Sicht des aufrufenden Codes bleibt es bei einer Eigenschaft und einem Verbindungsaufbau. Alles Obige passiert innerhalb von GetBearerToken, und das resultierende Access Token wird wie jedes andere zwischengespeichert.

Den Austausch verfolgen

Der Grant ist nicht auf MCP beschränkt. TsgcHTTPComponentClient_OAuth2 unterstützt ihn direkt, und das ist genau das, was Sie brauchen, wenn Sie jeden Schritt sehen, protokollieren oder das Token anderswo weiterverwenden wollen. Wählen Sie den Grant über OAuth2Options.GrantType, füllen Sie IdentityAssertionOptions aus und rufen Sie Start auf. Die Zwischen-Assertion bleibt in der schreibgeschützten Eigenschaft IdentityAssertion stehen, und DoIdentityAssertionGrant steht bereit, wenn Sie den Ablauf explizit steuern wollen.

Die Ereignisse melden jeden Schritt. OnBeforeTokenExchange, OnAfterTokenExchange und OnErrorTokenExchange decken den domänenübergreifenden Austausch ab, und die vorhandenen OnAuthToken und OnAuthTokenError melden das Endergebnis.

uses
  sgcHTTP_OAuth2_Client, sgcHTTP_OAuth_Types;

procedure TForm1.OnAfterTokenExchange(Sender: TObject; const Access_Token,
  Issued_Token_Type, Token_Type, Expires_In, Scope, RawParams: String;
  var Handled: Boolean);
begin
  // ... leg 1 done: we now hold the cross-domain assertion
  Log('assertion issued, type: ' + Issued_Token_Type);
end;

procedure TForm1.OnAuthToken(Sender: TObject; const TokenType, Token,
  Data: String);
begin
  // ... leg 2 done: this is the token domain B accepts
  Log('access token: ' + TokenType + ' ' + Token);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  oOAuth2: TsgcHTTPComponentClient_OAuth2;
begin
  oOAuth2 := TsgcHTTPComponentClient_OAuth2.Create(nil);
  try
    oOAuth2.OAuth2Options.GrantType := auth2IdentityAssertion;
    oOAuth2.OAuth2Options.ClientId := 'YOUR_CLIENT_ID';
    oOAuth2.OAuth2Options.ClientSecret := 'YOUR_CLIENT_SECRET';
    oOAuth2.AuthorizationServerOptions.TokenURL := 'https://auth.domain-b.com/oauth2/token';

    oOAuth2.IdentityAssertionOptions.SubjectToken := vIdTokenFromDomainA;
    oOAuth2.IdentityAssertionOptions.SubjectTokenType := 'urn:ietf:params:oauth:token-type:id_token';
    oOAuth2.IdentityAssertionOptions.RequestedTokenType := 'urn:ietf:params:oauth:token-type:id-jag';
    oOAuth2.IdentityAssertionOptions.RequestingPartyTokenURL := 'https://auth.domain-a.com/oauth2/token';
    oOAuth2.IdentityAssertionOptions.Audience := 'https://mcp.domain-b.com';

    oOAuth2.OnAfterTokenExchange := OnAfterTokenExchange;
    oOAuth2.OnErrorTokenExchange := OnErrorTokenExchange;
    oOAuth2.OnAuthToken := OnAuthToken;
    oOAuth2.OnAuthTokenError := OnAuthTokenError;

    oOAuth2.Start;
  finally
    oOAuth2.Free;
  end;
end;

Schlägt ein Schritt fehl, trägt das Fehlerereignis den OAuth2-Fehlercode und die Beschreibung genau so, wie der Autorisierungsserver sie zurückgegeben hat. Eine falsch konfigurierte Audience oder ein nicht akzeptierter Subject-Token-Typ wird also sofort sichtbar, statt später als 401 vom MCP Server aufzutauchen.

Verfügbarkeit

Die OAuth2-Authentifizierung im MCP Client, der Identity Assertion Grant und die TLS-Optionen des MCP Clients sind in sgcWebSockets 2026.7 für Delphi 7 bis 13 und C++Builder verfügbar, auf Win32/Win64, Linux64, macOS, Android und iOS. Für bestehenden Code ändert sich nichts: OAuth2 ist deaktiviert, bis Sie Enabled setzen, und die Authentifizierung per API Key und eigenem Header verhält sich genau wie zuvor.

Kunden mit einem aktiven Abonnement können den neuen Build im Kundenbereich herunterladen. Testnutzer erhalten das aktualisierte Installationsprogramm unter esegece.com/products/websockets/download.

Fragen, Feedback oder Hilfe beim Einbinden eines Grants in Ihre Anwendung? Nehmen Sie Kontakt auf, Sie erhalten eine Antwort von den Leuten, die den Code geschrieben haben.