Bu hafta beni düşündüren bir durumla karşılaştım: uzaktan bir MCP bağlantısı, OAuth sunucumu keşfetmeyi sürekli olarak başaramıyordu, oysa OAuth sunucusu tam oradaydı ve çalışıyordu. Çözüm, bir yön ve 308 yönlendirmesi oldu — ama bu durumun neden olduğu, keşif spesifikasyonlarının pratikte nasıl birbirinden uzaklaştığının güzel bir dersi. Bu konuyu ele alalım.
Bu, cleaniquecoders/laravel-mcp-kit adlı kamuya açık bir paket içinde yer alıyor, bu nedenle aşağıdaki tüm kod gerçek.
Kurulum: kim geliyor ve ne bekliyor
Kurulum: kim geliyor ve ne bekliyor
MCP sunucusunu akışa uygun HTTP ile açtığınızda ve uzaktan istemcilerin (örneğin, claude.ai) kimlik doğrulaması yapmasını istediğinizde, OAuth’u etkinleştirirsiniz. laravel/mcp, keşif belgeleri için bir satırlık bir çözüm sağlar:
Mcp::oauthRoutes();
Bu, OAuth 2.1 dünyasının beklediği iki belgeyi kaydeder:
-
/.well-known/oauth-authorization-server— RFC 8414, yetkilendirme sunucusu kimlik bilgileri. -
/.well-known/oauth-protected-resource— RFC 9728, korunmuş kaynak kimlik bilgileri.
Headersiz bir bağlantı noktası bunlara erişir, authorization_endpoint, token_endpoint ve registration_endpoint‘in nerede olduğunu öğrenir, Dinamik İstemci Kaydı (RFC 7591) aracılığıyla kendini kaydeder ve hareket eder. Temiz.
Fakat bazı istemciler — ve laravel/mcp’nin kendi istemcisi, belirli yollarında — OAuth metadata’sını sorgulamaz. Onlar yerine /.well-known/openid-configuration’ı sorgularlar. Bu, OpenID Connect keşif belgesidir. Farklı spesifikasyon, aynı mahalle. Ancak oauthRoutes() bunu kaydetmez, çünkü OIDC, bu paketin uygulamaya koymadığı OAuth’un bir üst kümesidir.
Bu durumda, OAuth sunucunuz doğru ve tam olsa da, istemci, hiç inşa etmediğiniz bir kapıya vuruyor, 404 alıyor ve vazgeçiyor.
Ucuz çözüm vs. doğru çözüm
Ucuz çözüm vs. doğru çözüm
Tembel çözüm bir ters proxy yönlendirmesi yapmaktır — nginx’e /.well-known/openid-configuration‘ı yetkilendirme sunucusu belgesine yeniden yazmasını söylemek. Bu çalışıyor, ancak artık kimlik doğrulama keşifleriniz uygulama dışındaki altyapı yapılandırmasına bağlı, test edilmemiş ve birisi bu kuralın olmadığı bir ana bilgisayara dağıtıldığında aniden bozuluyor. Bu, “makinemde çalışıyor” türünden bir kara mayınını, paket dışına çıkarmaya çalıştığım türden bir şeydir.
Doğru çözüm, uygulamanın kendisinin sorguyu yanıtlamasıdır. Çünkü bu istemcilerin gerçekten istediği OIDC belgesi, yetkilendirme sunucusu kimlik bilgileriyle neredeyse tamamen örtüşmektedir, biri diğerine alias koymaktır:
if ($oauth) {
Mcp::oauthRoutes();
// oauthRoutes() iki OAuth keşif belgesini kaydeder ancak
// OpenID Connect keşfini kaydetmez. Bazı bağlantılar (ve laravel/mcp'nin kendisi
// istemcisi) hala /.well-known/openid-configuration sorgular; bunu
// yetkilendirme sunucusu kimlik bilgilerine yönlendirmek için alias yapın, böylece sunucular
// ters proxy yönlendirmesine ihtiyaç duymamalıdır. 308, bunu takip eden istemciler için isteği korur.
if (config('mcp-kit.web.oauth.openid_configuration', true)) {
Route::get(
'/.well-known/openid-configuration',
fn () => redirect()->route('mcp.oauth.authorization-server', [], 308)
)->name('mcp-kit.openid-configuration');
}
}
Durmaya değer iki tasarım kararı var.
308 neden 302 değil? Bir 302, yönlendirmeye gelen bir istemcinin POST’u GET’e çevirmesine davet eder (tarihsel olarak 302 uygulamaları tam olarak bu şekilde hareket etmiştir). 308 Permanent Redirect ise daha katıdır: istemciye “burası yerine git, ve yöntemini ve gövdesini tam olarak olduğu gibi koru.” Keşif probe’ü için GET-on-GET iki durumda da geçerli olsa da, 308, gerçekte kaynağın diğer URL’de yaşadığı anlamına gelen doğru anlamı taşır ve istemcinin isteği sessizce değiştirmesini istemem. Gerçekte ne demek istediğinizi belirten yönlendirme kodunu kullanın.
Bu neden bir yapılandırma bayrağının arkasında? Alias, OAuth aktif olduğunda anlam kazanır, bu nedenle if ($oauth) bloğunun içine yerleştirilmiştir — kaydedilmemiş bir yetkilendirme sunucusu ruta alias yapmanın anlamı yoktur. Ve aynı zamanda config('mcp-kit.web.oauth.openid_configuration', true) ile korunduğundan, varsayılan olarak açılmaktadır, böylece gerçekten kendi OIDC keşfini uygulayan bir ana bilgisayar alias’ı kapatabilir ve çakışmaktan kaçınabilir. Ortak durumu çalışır hale getiren yürütme varsayılanıdır; daha iyi bilen ana bilgisayara bir kaçış kapısı bırakılır.
Test ile güvence altına alın
Test ile güvence altına alın
Bunu nginx’de değil de uygulamada yapmanın amacı, bunu test edebilmemdir. İki Pest testi: biri yönlendirmeyi doğrular, diğeri yönlendirmeyi takip ederek, geri dönen metadata’nın gerçekten bir bağlayıcının ihtiyaç duyduğu yapı ile geldiğini doğrular.
it('aliases openid-configuration to the authorization-server discovery', function () {
$this->get('/.well-known/openid-configuration')
->assertStatus(308)
->assertRedirect(route('mcp.oauth.authorization-server'));
});
it( function () {
$this->followingRedirects()
->get()
->assertOk()
->assertJsonStructure([
,
,
,
,
,
]);
});
İlk test mekanizmayı (doğru adlandırılmış yere 308 yönlendirmesini) sabitler. İkincisi ise sözleşmeyi sabitler — followingRedirects() alias’ı en baştan takip eder ve assertJsonStructure, gerçek bir bağlantının bu yol boyunca gitmesi durumunda, kendisini kaydedebileceği, yetkilendirmek için nereye gitmesi gerektiği, token almak için nereye gitmesi gerektiği, kaydolması gereken yer ve kritik olarak code_challenge_methods_supported (PKCE) ve grant_types_supported. laravel/mcp metadata yapısını benim için değiştirirse, ikinci test kırmızı olur ve bunu CI’da öğrenirim, claude.ai bağlanamadığında değil.
Bu bölünme — bir test kablo için, bir test sözleşmesi için — geri geldiğim bir alışkanlık. Mekanizma testi beni rota kırılmalarından korur. Sözleşme testi ise bir bağımlılığı tüketici parçalamalarından korur.
Çıkarım
Çıkarım
Değiştirilebilir olması beklenen keşif spesifikasyonları pratikte nadiren değiştirilebilir. OAuth 2.1 ve OpenID Connect, /.well-known/ mahallesini ve birçok kelime dağarcığını paylaşırken, istemciler, hangi kapıyı vuracakları konusunda karar verirler. Sunucu konumundayken yapabileceğiniz en dostça şey, her iki kapıya cevap vermek ve onları aynı odaya yönlendirmektir — uygulama içinde, ne demek istediğinizi belirten bir yönlendirme kodu ile, bir bayrak arkasında ve bir test ile sabitlenmiştir. Ters proxy magic, ana bilgisayar spesifik yapılandırma yok, bir sonraki dağıtımda bozulan hiçbir şey yok.
Gelecek: /.well-known/oauth-protected-resource kenar durumlarına aynı tedavi uygulanması; bazı daha katı istemcilerin sorgu parametreleri ile sorguladığı durumlar. Aynı fikir, biraz daha karmaşık.
Kaynak: Orijinal Makale


