Les serveurs MCP distants sont des ressources protégées, et les serveurs que les gens déploient réellement se trouvent derrière un serveur d'autorisation. Jusqu'à présent, le client MCP de sgcWebSockets pouvait envoyer une clé d'API ou un en-tête personnalisé, ce qui convenait pour un jeton statique mais signifiait que tout ce qui avait une durée de vie devenait votre problème : vous appeliez vous-même le point de terminaison de jeton, analysiez la réponse, surveilliez l'horloge et rafraîchissiez le jeton avant son expiration.
sgcWebSockets 2026.7 déplace ce travail dans le composant. Le client MCP peut maintenant s'authentifier avec OAuth2, donc il récupère le jeton d'accès, le met en cache et le réutilise à chaque requête jusqu'à son expiration. La même version ajoute un nouveau type de grant, l'Identity Assertion Authorization Grant, qui chaîne une identité d'un domaine vers un autre sans faire passer l'utilisateur par une seconde connexion interactive.
Client, pas serveur
Une précision d'abord, car les deux côtés sont faciles à confondre. sgcWebSockets 2026.5 a livré OAuth 2.1 sur le serveur MCP : points de terminaison de découverte, PKCE et enregistrement dynamique de client, afin qu'un client MCP dans le navigateur puisse s'autoriser auprès de votre serveur Delphi. Cela a déjà été traité. Cet article porte sur l'autre extrémité du fil, le client MCP, qui obtient désormais son propre jeton et le présente au serveur MCP distant auquel il se connecte.
Ce que le client MCP savait faire auparavant
Deux options, toutes deux toujours disponibles et toutes deux inchangées. Une clé d'API, envoyée sous la forme Authorization: Bearer <value>, et un en-tête personnalisé pour le routage par locataire ou par région. L'article précédent Authentification MCP en Delphi couvre les deux en détail.
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';
Ce qui manquait, c'est tout ce qu'il y a entre les deux : un point de terminaison de jeton, un identifiant et un secret client, une portée, une expiration. C'est ce qu'apporte le nouvel objet OAuth2.
OAuth2 sur le client MCP
L'authentification comporte désormais une troisième branche, MCPOptions.AuthenticationOptions.OAuth2, de type TsgcWSMCPClientAuthenticationOAuth2_Options. Ses propriétés sont celles que l'on attend d'un client OAuth2 : Enabled, TokenURL, ClientId, ClientSecret, Scope, un GrantType, et un objet IdentityAssertionOptions utilisé par le nouveau grant. Il existe une seule méthode, GetBearerToken, et normalement vous ne l'appelez jamais : le client l'appelle pour vous à chaque requête MCP et place le résultat dans l'en-tête Authorization.
Le grant se sélectionne avec GrantType, de type TsgcOAuth2GrantTypes :
TsgcOAuth2GrantTypes = (auth2Code, auth2CodePKCE, auth2ClientCredentials,
auth2ResourceOwnerPassword, auth2DeviceCode, auth2IdentityAssertion);
La dernière valeur, auth2IdentityAssertion, est nouvelle dans 2026.7. Les autres étaient déjà prises en charge par le client HTTP OAuth2 et sont maintenant accessibles depuis le client MCP également.
Client credentials : le cas courant
Un service qui dialogue avec un serveur MCP distant, sans utilisateur dans la boucle, correspond au grant client credentials. Renseignez le point de terminaison de jeton et les identifiants, activez-le, et connectez-vous :
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;
Aucune gestion de jeton dans votre code. La première requête qui a besoin d'un jeton déclenche le grant, et toutes les requêtes suivantes réutilisent ce qui est en cache.
Mise en cache du jeton et priorité sur la clé d'API
Le jeton est acquis de manière paresseuse, à la première requête, et conservé en mémoire avec son expiration. Tant qu'il est encore valide, le client le réutilise. Lorsqu'il expire, ou lorsque le serveur ne vous a jamais indiqué d'expiration et que le jeton revient vide, le grant est relancé. Le client rafraîchit également le jeton légèrement avant l'expiration annoncée, afin qu'un jeton sur le point d'expirer ne soit pas envoyé sur une requête qui serait rejetée au moment de son arrivée.
La priorité est simple et mérite d'être énoncée clairement : lorsque OAuth2.Enabled vaut True, OAuth2 l'emporte. L'en-tête Authorization transporte le jeton bearer issu du grant, et ApiKey est ignoré même s'il est également activé. Cela vous permet de laisser une clé d'API existante configurée pendant votre migration, puis de basculer OAuth2.Enabled sans rien changer d'autre. CustomHeader est orthogonal et reste envoyé dans les deux cas.
Options TLS sur le client MCP
Le transport HTTP du client MCP expose désormais sa propre configuration TLS via MCPOptions.HttpOptions.TLSOptions, afin que la connexion au serveur MCP et au point de terminaison de jeton puisse être fixée aux réglages exigés par votre environnement plutôt qu'aux valeurs par défaut.
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
Le grant Identity Assertion
Passons à la partie intéressante. auth2IdentityAssertion est un grant OAuth2 défini par l'IETF pour chaîner une identité entre plusieurs domaines, et c'est un flux de type échange de jetons.
La situation qu'il résout ressemble à ceci. Votre application détient déjà un jeton qui prouve qui est l'utilisateur dans le domaine A, typiquement un id token du fournisseur d'identité avec lequel l'utilisateur s'est connecté. Vous devez maintenant appeler un serveur MCP du domaine B, qui n'acceptera pas le jeton du domaine A : il fait confiance à son propre serveur d'autorisation, pas à celui de quelqu'un d'autre. L'utilisateur est juste là et déjà authentifié, donc le faire passer par une seconde connexion interactive est exactement la friction que vous voulez éviter.
Le grant Identity Assertion est la solution. Vous présentez le jeton du domaine A comme subject token, et l'échange renvoie un jeton limité à l'audience ou à la ressource que vous avez demandée, un jeton que le domaine B accepte. C'est la raison pour laquelle les options ont cette forme : SubjectToken et SubjectTokenType décrivent ce que vous détenez, RequestedTokenType, Audience et Resource décrivent ce que vous voulez, et ActorToken / ActorTokenType vous permettent de nommer le service agissant pour le compte de l'utilisateur lorsque la cible l'exige.
C'est un échange en plusieurs étapes, et le client les parcourt pour vous. Le subject token est envoyé au serveur d'autorisation de la partie demandeuse (RequestingPartyTokenURL, avec RequestingPartyClientId et RequestingPartyClientSecret), qui renvoie une assertion inter-domaines. Cette assertion est ensuite présentée au serveur d'autorisation de la ressource à l'adresse TokenURL, qui émet le jeton d'accès que le client MCP envoie réellement. Si vous disposez déjà de l'assertion par un autre moyen, définissez Assertion et la première étape est ignorée.
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;
Depuis le code appelant, cela reste une propriété et une connexion. Tout ce qui précède se produit à l'intérieur de GetBearerToken, et le jeton d'accès obtenu est mis en cache comme n'importe quel autre.
Suivre l'échange
Le grant ne se limite pas à MCP. TsgcHTTPComponentClient_OAuth2 le prend en charge directement, ce qui est exactement ce qu'il vous faut lorsque vous devez voir chaque étape, la journaliser, ou réutiliser le jeton ailleurs. Sélectionnez le grant sur OAuth2Options.GrantType, renseignez IdentityAssertionOptions, et appelez Start. L'assertion intermédiaire est laissée dans la propriété en lecture seule IdentityAssertion, et DoIdentityAssertionGrant est là si vous souhaitez piloter le flux explicitement.
Les événements rendent compte de chaque étape. OnBeforeTokenExchange, OnAfterTokenExchange et OnErrorTokenExchange couvrent l'échange inter-domaines, et les événements existants OnAuthToken et OnAuthTokenError rendent compte du résultat final.
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;
Si une étape échoue, l'événement d'erreur transporte le code d'erreur OAuth2 et sa description tels que le serveur d'autorisation les a renvoyés, de sorte qu'une audience mal configurée ou un type de subject token non accepté est visible immédiatement, au lieu de se manifester plus tard sous la forme d'un 401 renvoyé par le serveur MCP.
Disponibilité
L'authentification OAuth2 sur le client MCP, le grant Identity Assertion et les options TLS du client MCP sont disponibles dans sgcWebSockets 2026.7 pour Delphi 7 à 13 et C++Builder, sur Win32/Win64, Linux64, macOS, Android et iOS. Rien ne change pour le code existant : OAuth2 est désactivé tant que vous ne définissez pas Enabled, et l'authentification par clé d'API et par en-tête personnalisé se comporte exactement comme avant.
Les clients disposant d'un abonnement actif peuvent télécharger la nouvelle version depuis l'espace client. Les utilisateurs de la version d'essai peuvent récupérer le programme d'installation mis à jour sur esegece.com/products/websockets/download.
Des questions, des remarques ou besoin d'aide pour câbler un grant dans votre application ? Contactez-nous, vous obtiendrez une réponse des personnes qui ont écrit le code.
