MCP İstemcisi için OAuth2 ve Identity Assertion Grant | eSeGeCe Blog

MCP İstemcisi için OAuth2 ve Identity Assertion Grant

· Bileşenler

Uzak MCP sunucuları korunan kaynaklardır ve insanların gerçekten yayına aldığı sunucular bir yetkilendirme sunucusunun arkasında durur. Şimdiye kadar sgcWebSockets MCP istemcisi bir API anahtarı veya özel bir başlık gönderebiliyordu. Bu, sabit bir jeton için yeterliydi, ancak ömrü olan her şey sizin sorununuz oluyordu: jeton uç noktasını kendiniz çağırıyor, yanıtı ayrıştırıyor, saati takip ediyor ve jetonun süresi dolmadan yeniliyordunuz.

sgcWebSockets 2026.7 bu işi bileşenin içine taşıyor. MCP istemcisi artık OAuth2 ile kimlik doğrulaması yapabiliyor, yani erişim jetonunu kendisi alıyor, önbelleğe koyuyor ve süresi dolana kadar her istekte yeniden kullanıyor. Aynı sürüm, bir kimliği bir alandan diğerine zincirleyen ve kullanıcıyı ikinci bir etkileşimli oturum açma işleminden geçirmeyen yeni bir grant tipi olan Identity Assertion Authorization Grant tipini de ekliyor.

Sunucu değil, istemci

Önce bir açıklama, çünkü bu iki tarafı karıştırmak kolaydır. sgcWebSockets 2026.5, MCP Sunucusunda OAuth 2.1 desteğini getirmişti: keşif uç noktaları, PKCE ve dinamik istemci kaydı, böylece tarayıcı tabanlı bir MCP istemcisi Delphi sunucunuza karşı yetkilendirme yapabiliyordu. Bu konu daha önce ele alınmıştı. Bu yazı ise hattın diğer ucuyla, yani artık kendi jetonunu alıp bağlandığı uzak MCP sunucusuna sunan MCP İstemcisi ile ilgili.

MCP istemcisi daha önce ne yapabiliyordu

İki seçenek vardı, ikisi de hâlâ mevcut ve ikisi de değişmedi. Authorization: Bearer <value> olarak gönderilen bir API anahtarı ve kiracı veya bölge yönlendirmesi için özel bir başlık. Daha önceki MCP Authentication Delphi yazısı her ikisini de ayrıntılı olarak anlatıyor.

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';

Eksik olan, aradaki her şeydi: bir jeton uç noktası, bir istemci kimliği ve gizli anahtarı, bir kapsam, bir son kullanma süresi. Yeni OAuth2 nesnesinin eklediği tam olarak budur.

MCP istemcisinde OAuth2

Kimlik doğrulamanın artık üçüncü bir dalı var, TsgcWSMCPClientAuthenticationOAuth2_Options tipinde MCPOptions.AuthenticationOptions.OAuth2. Özellikleri bir OAuth2 istemcisinden bekleyeceğiniz türden: Enabled, TokenURL, ClientId, ClientSecret, Scope, bir GrantType ve yeni grant tipinin kullandığı bir IdentityAssertionOptions nesnesi. Tek bir metot var, GetBearerToken, ve normalde onu hiç çağırmazsınız: istemci her MCP isteğinde sizin yerinize çağırır ve sonucu Authorization başlığına koyar.

Grant tipi, TsgcOAuth2GrantTypes tipindeki GrantType ile seçilir:

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

Son değer olan auth2IdentityAssertion, 2026.7 ile geliyor. Diğerleri zaten OAuth2 HTTP istemcisi tarafından destekleniyordu ve artık MCP istemcisinden de erişilebilir durumda.

İstemci kimlik bilgileri: yaygın senaryo

Uzak bir MCP sunucusuyla konuşan ve döngüde hiçbir kullanıcı bulunmayan bir servis, istemci kimlik bilgileri grant tipine karşılık gelir. Jeton uç noktasını ve kimlik bilgilerini doldurun, etkinleştirin ve bağlanın:

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;

Kodunuzda hiçbir jeton yönetimi yok. Jetona ihtiyaç duyan ilk istek grant akışını tetikler ve ondan sonraki her istek önbellekteki jetonu yeniden kullanır.

Jeton önbelleği ve API anahtarına göre önceliği

Jeton, ilk istekte tembel biçimde alınır ve son kullanma süresiyle birlikte bellekte tutulur. Hâlâ geçerliyken istemci onu yeniden kullanır. Süresi dolduğunda ya da sunucu size hiçbir son kullanma süresi bildirmemişse ve jeton boş dönerse, grant akışı yeniden çalışır. İstemci ayrıca belirtilen son kullanma süresinden biraz önce jetonu yeniler, böylece süresi dolmak üzere olan bir jeton, vardığında zaten reddedilecek bir isteğe eklenmez.

Öncelik basittir ve açıkça belirtmeye değer: OAuth2.Enabled True olduğunda OAuth2 kazanır. Authorization başlığı grant akışından gelen bearer jetonunu taşır ve ApiKey etkin olsa bile yok sayılır. Bu sayede geçiş sırasında mevcut bir API anahtarını yapılandırılmış olarak bırakabilir, sonra OAuth2.Enabled özelliğini açıp başka hiçbir şeyi değiştirmeyebilirsiniz. CustomHeader bundan bağımsızdır ve her iki durumda da gönderilmeye devam eder.

MCP istemcisinde TLS seçenekleri

MCP istemcisinin HTTP taşıma katmanı artık kendi TLS yapılandırmasını MCPOptions.HttpOptions.TLSOptions üzerinden sunuyor, böylece MCP sunucusuna ve jeton uç noktasına giden bağlantı, varsayılanlar yerine ortamınızın gerektirdiği ayarlara sabitlenebilir.

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

Identity Assertion grant tipi

Şimdi ilginç kısma geliyoruz. auth2IdentityAssertion, bir kimliği alanlar arasında zincirlemek için IETF tarafından tanımlanmış bir OAuth2 grant tipidir ve jeton takası tarzında bir akıştır.

Çözdüğü durum şuna benzer. Uygulamanız, kullanıcının A alanındaki kim olduğunu kanıtlayan bir jetonu zaten elinde tutuyor, tipik olarak kullanıcının oturum açtığı kimlik sağlayıcıdan gelen bir id jetonu. Şimdi B alanındaki bir MCP sunucusunu çağırmanız gerekiyor, ancak o sunucu A alanının jetonunu kabul etmeyecek: kendi yetkilendirme sunucusuna güvenir, başkasınınkine değil. Kullanıcı tam orada ve zaten kimliği doğrulanmış durumda, dolayısıyla onu ikinci bir etkileşimli oturum açmadan geçirmek tam da kaçınmak istediğiniz sürtünmedir.

Identity Assertion grant tipi bu çıkmazdan kurtulmanın yoludur. A alanının jetonunu subject token olarak sunarsınız ve takas, istediğiniz audience ya da resource için kapsamlandırılmış, B alanının kabul edeceği bir jeton döndürür. Seçeneklerin bu biçimde tasarlanmış olmasının nedeni budur: SubjectToken ve SubjectTokenType elinizdekini tanımlar, RequestedTokenType, Audience ve Resource istediğinizi tanımlar, ActorToken / ActorTokenType ise hedef gerektirdiğinde kullanıcı adına hareket eden servisi adlandırmanızı sağlar.

Bu çok adımlı bir takastır ve istemci adımları sizin yerinize yürütür. Subject token, istek sahibi tarafın yetkilendirme sunucusuna (RequestingPartyClientId ve RequestingPartyClientSecret ile birlikte RequestingPartyTokenURL) gider ve alanlar arası bir assertion döner. Bu assertion daha sonra TokenURL adresindeki kaynak yetkilendirme sunucusuna sunulur ve o sunucu, MCP istemcisinin gerçekten göndereceği erişim jetonunu verir. Assertion değerine başka bir yerden zaten sahipseniz, Assertion özelliğini ayarlayın, ilk ayak atlanır.

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;

Çağıran kodun tarafından bakıldığında bu hâlâ tek bir özellik ve tek bir bağlanmadır. Yukarıdaki her şey GetBearerToken içinde olup biter ve ortaya çıkan erişim jetonu diğerleri gibi önbelleğe alınır.

Takası adım adım izlemek

Bu grant tipi yalnızca MCP ile sınırlı değil. TsgcHTTPComponentClient_OAuth2 onu doğrudan destekler ve her adımı görmeniz, kaydetmeniz veya jetonu başka bir yerde yeniden kullanmanız gerektiğinde isteyeceğiniz şey tam olarak budur. Grant tipini OAuth2Options.GrantType üzerinde seçin, IdentityAssertionOptions alanlarını doldurun ve Start çağırın. Aradaki assertion, salt okunur IdentityAssertion özelliğinde bırakılır ve akışı açıkça yönetmek isterseniz DoIdentityAssertionGrant orada durur.

Olaylar her ayağı raporlar. OnBeforeTokenExchange, OnAfterTokenExchange ve OnErrorTokenExchange alanlar arası takası kapsar, mevcut OnAuthToken ve OnAuthTokenError ise nihai sonucu raporlar.

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;

Bir ayak başarısız olursa, hata olayı OAuth2 hata kodunu ve açıklamasını yetkilendirme sunucusunun döndürdüğü şekliyle taşır, böylece yanlış yapılandırılmış bir audience veya kabul edilmeyen bir subject token tipi, daha sonra MCP sunucusundan gelen bir 401 olarak ortaya çıkmak yerine hemen görünür olur.

Kullanılabilirlik

MCP istemcisinde OAuth2 kimlik doğrulaması, Identity Assertion grant tipi ve MCP istemcisi TLS seçenekleri, sgcWebSockets 2026.7 sürümünde Delphi 7 ile 13 arası ve C++Builder için, Win32/Win64, Linux64, macOS, Android ve iOS platformlarında kullanılabilir. Mevcut kod için hiçbir şey değişmiyor: Enabled özelliğini ayarlayana kadar OAuth2 kapalıdır ve API anahtarı ile özel başlık kimlik doğrulaması tam olarak eskisi gibi çalışır.

Aktif aboneliği olan müşteriler yeni yapıyı müşteri alanından indirebilir. Deneme kullanıcıları güncellenmiş kurulum dosyasını esegece.com/products/websockets/download adresinden alabilir.

Sorularınız, geri bildirimleriniz ya da uygulamanıza bir grant tipi bağlamak için yardıma mı ihtiyacınız var? Bize ulaşın, yanıtı kodu yazan kişilerden alacaksınız.