Jak serwer MCP wywołuje twoje API w imieniu użytkowników: strategia tokenów produkcyjnych
Wyjaśnia strategię tokenów przy wywołaniach wychodzących z serwerów MCP: dlaczego przekazywanie tokenów i M2M zawodzą oraz jak wymiana tokenów utrzymuje zgodność uprawnień z użytkownikiem.
YijunDeveloper
Przestań tracić tygodnie na uwierzytelnianie użytkowników
Uruchamiaj bezpieczne aplikacje szybciej z Logto. Zintegruj uwierzytelnianie użytkowników w kilka minut i skup się na swoim głównym produkcie.
Jak serwer MCP wywołuje twoje API w imieniu użytkowników: strategia tokenów produkcyjnych#
W naszym poprzednim artykule podzieliliśmy się naszym ogólnym doświadczeniem z budowania zdalnego serwera MCP Logto. Ten artykuł szczegółowo opisuje projekt architektury oraz przepływ OAuth.
Z serwerem MCP jako granicą uwierzytelnianie dla zdalnego serwera MCP ma dwa etapy: przychodzący i wychodzący.
Przychodzący: klient MCP (VS Code, Cursor etc.) loguje się przez OAuth, otrzymuje token dostępu i używa go, aby dostać się do twojego serwera MCP
Wychodzący: podczas obsługi wywołań narzędzi, serwer MCP żąda twojego API biznesowego w imieniu użytkownika
Etap przychodzący jest dobrze zdefiniowany w specyfikacji MCP, z licznymi dyskusjami i implementacjami w społeczności. Napisaliśmy także wcześniej przewodnik implementacyjny. Etap wychodzący przyciąga znacznie mniej uwagi: serwer MCP posiada wyłącznie token uzyskany do dostępu do siebie samego. Jak zatem może dzwonić do twojego API biznesowego w imieniu użytkownika?
To pytanie sprawiało nam największą trudność podczas budowy serwera MCP Logto. Logto Cloud to typowy produkt B2B dla wielu najemców: użytkownik może należeć do wielu najemców, a uprawnienia wynikają z ról użytkownika w każdym z nich. AI nie tylko musi działać jak użytkownik, ale także trafić do właściwego najemcy.
Ten artykuł prowadzi przez nasz rzeczywisty proces podejmowania decyzji:
Dlaczego serwer MCP powinien być wdrożony niezależnie jako osobny chroniony zasób
Dwa podejścia do wywołań wychodzących, które odrzuciliśmy (przekazywanie tokenów i M2M) oraz ich problemy
Ostateczny projekt: wymiana tokenów i token podmiotu, w tym jak tokeny organizacyjne obsługują wiele najemców
Kilka zasad, których warto się trzymać budując podobne rozwiązanie
Z perspektywy OAuth zdalny serwer MCP pełni dwie role jednocześnie:
Dla klienta MCP jest serwerem zasobu: klient musi dostarczyć token, by go użyć
Dla API biznesowego jest klientem: przynosi token, by korzystać z innego zasobu
Innymi słowy, serwer MCP pełni inną rolę po każdej stronie i każda strona używa innego tokena. Token, który klient MCP otrzymuje przez OAuth, jako odbiorcę (audience) ma serwer MCP, więc API biznesowe go odrzuci podczas walidacji odbiorcy.
Wywołanie wychodzące to więc zasadniczo problem delegacji: jak serwer MCP może wywołać API downstream jako użytkownik, w ramach jego uprawnień, nie posiadając jego poświadczeń?
Decyzja architektoniczna: serwer MCP jako samodzielna usługa#
Wbudowanie punktu końcowego MCP w usługę API biznesowego, współdzieląc ten sam proces i stos autoryzacji, wydaje się opcją najprostszą. Po rozważeniu wybraliśmy wdrożenie osobne:
Izolacja ryzyka: gdy podejmowaliśmy decyzję, oficjalne SDK MCP nie było gotowe produkcyjnie, a sam protokół szybko ewoluował (np. zmiana transportu z SSE na Streamable HTTP). Logto to usługa IAM, a dostępność ścieżki logowania to podstawa działania. Dzięki samodzielnemu wdrożeniu, gdy serwer MCP się zepsuje, pada tylko wejście z AI. Główna usługa pozostaje nietknięta.
Niezależne iterowanie: ekosystem MCP zmienia się co tydzień, problemy zgodności z klientami wymagają hotfixów o dowolnej porze, podczas gdy główny serwis ma rygorystyczny proces wydawania i regresji. Oddzielne wdrożenie pozwala jednym nie spowalniać drugich.
Swoboda środowiska uruchomieniowego: samodzielna usługa może wybrać środowisko najlepiej dopasowane do swoich potrzeb. Serwer MCP Logto działa na Cloudflare Workers: bezstanowo, skalowanie na żądanie i niemal zerowa obsługa. W opcji wbudowanej tej swobody nie ma.
Serwer MCP jest wdrożony pod własną domeną mcp.logto.io, bez żadnych prywatnych powiązań z główną usługą. Jeśli chcemy go kiedyś otworzyć jako open source – nic nie stoi na przeszkodzie.
Wersja wbudowana i tak nie rozwiązuje problemu tokenów: punkt końcowy MCP i API biznesowe miałyby ten sam zasób (resource identifier), więc token klienta MCP otrzymywałby pełne uprawnienia do API. Pytanie „z jakimi uprawnieniami działa to wywołanie” zmienia się jedynie z międzyserwisowej wymiany tokenów na przekazywanie uprawnień wewnątrz procesu, a problem pozostaje ten sam. Samodzielne wdrożenie wymusza jawne zaprojektowanie granicy uprawnień, o czym jest reszta artykułu.
Dyskusja o wywołanych wychodzących zaczyna się od bardziej podstawowego pytania: kto powinien być odbiorcą tokena wystawionego dla klienta MCP?
Ponowne użycie identyfikatora zasobu API biznesowego wydaje się najprostsze: API Zarządzania Logto to już standardowy zasób OAuth, więc klient MCP mógłby otrzymać taki token wprost, a serwer MCP przekazywałby go dalej bez zmian. Wiele pierwszych implementacji tak robiło.
Koszt: jeśli audiencja tokena MCP to API biznesowe, granica uprawnień znika.
Starannie zaprojektowane narzędzia MCP stają się ozdobą: sam token pozwala już wywołać pełne API, z pominięciem twoich narzędzi
Zasięg potencjalnego wycieku tokena rośnie z „kilka kontrolowanych operacji przez MCP” do „całe API Zarządzania”
Niezależny audyt, rate-limiting i zakres uprawnień dla MCP nie mają żadnej podstawy
Nasza pierwsza decyzja: serwer MCP to osobny chroniony zasób, z własnym identyfikatorem (https://mcp.logto.io) i własnym zakresem.
Dzięki temu granica uprawnień jest jasna, a pytanie wychodzące konkretne: token daje dostęp tylko do serwera MCP, więc czego używa MCP do API biznesowego?
Konsola Logto to SPA. Wywołania do API Zarządzania są proste: użytkownik loguje się przez OAuth w przeglądarce, dostaje token z odbiorcą jako API Zarządzania, frontend używa go bezpośrednio w wywołaniach.
Czy MCP może działać jako inna odmiana Konsoli? Może klient MCP żąda tokena API biznesowego już przy logowaniu, a serwer MCP tylko go przekazuje – bez żadnej konwersji:
To podejście jest bardzo proste: serwer MCP tylko przekazuje tokeny. Ale są oczywiste problemy:
Po pierwsze, to wprost łamie wcześniejszą decyzję granicy uprawnień. Przekazywanie tokena wymusza, by klient MCP miał token API biznesowego – dokładnie to właśnie odrzucono.
Po drugie, serwer MCP przestaje być faktycznym chronionym zasobem. Odbiorca tokenów nie jest nim samym, więc weryfikacja odbiorcy nie ma sensu, pozostaje tylko „sprawdź podpis i wystawcę”. To nie odpowiada wymogom MCP – serwer MCP powinien pełnić rolę resource server i deklarować się przez RFC 9728. W praktyce udaje backendową usługę za SPA w przeglądarce.
Po trzecie, klienci MCP się nie dostosują. Klient zgodny ze specyfikacją MCP żąda tokenów tylko zgodnych z deklaracją chronionego zasobu, a odbiorcą będzie serwer MCP. Nie ma standardowego sposobu, żeby poprosić o token API biznesowego, więc ta ścieżka na kliencie nie działa.
Skoro token użytkownika nie działa, a co z tożsamością MCP?
Nadaj serwerowi MCP aplikację M2M (machine-to-machine), zdobądź token przez client credentials i korzystaj z API biznesowego. To także standard wewnętrzny dla usług.
Autoryzacja przychodząca działa: token klienta MCP wskazuje na serwer MCP jako odbiorcę, serwer MCP normalnie go weryfikuje, a następnie pracuje na własnym tokenie M2M.
Fatalny problem: uprawnienia tokena M2M nie mają żadnego powiązania z uprawnieniami użytkownika:
Nadmierne uprawnienia: token M2M pokazuje „co może serwer MCP”, nie „co może ten użytkownik”. Użytkownik z rolą tylko do odczytu może przez MCP skasować aplikacje, bo M2M ma te uprawnienia
Utrata tożsamości: downstream API zawsze widzi aplikację M2M jako nadawcę – brak możliwości audytu na konkretnego człowieka
Problem „confused deputy”: serwer MCP staje się uprzywilejowanym proxy i każdy, kto potrafi go nakłonić do wywołania, korzysta z jego uprawnień
M2M pasuje do scenariuszy bez kontekstu użytkownika, np. zadania harmonogramowane czy synchronizacja system to system. Serwer MCP jest inny: każde wywołanie pochodzi od użytkownika i powinno działać z jego tożsamością i uprawnieniami.
Podsumowując wnioski z poprzednich podejść, powstają wymagania na dobre rozwiązanie:
Token trzymany przez klienta MCP pozwala wejść wyłącznie do serwera MCP (wniosek z podejścia 1)
Wywołując API downstream, serwer MCP musi działać jako użytkownik, w ramach jego uprawnień (wniosek z podejścia 2)
Daje to standardowy mechanizm: wymiana tokenów (RFC 8693). Serwer MCP bierze poświadczenie reprezentujące użytkownika i wymienia w serwerze autoryzacji na token do API downstream. Tożsamość i uprawnienia użytkownika są zachowane.
W Logto „poświadczenie reprezentujące użytkownika” pochodzi z funkcji podszywania się pod użytkownika: token podmiotu (subject token). To krótkotrwałe poświadczenie wydane przez serwer dla konkretnego użytkownika, przy którym kolejne wymiany tokenów wykonywane są jako ten użytkownik. Użytkownik nie musi sam nic konfigurować, cały przepływ jest automatyczny. Token podmiotu jest jednorazowy i szybko wygasa.
W architekturze są cztery role. Serwer MCP wdrożony jest osobno pod mcp.logto.io:
Pełny przepływ tokenów przy jednym wywołaniu narzędzia:
Krok po kroku:
① Walidacja przychodząca. Klient MCP wywołuje narzędzie z tokenem użytkownika. Odbiorcą jest identyfikator zasobu serwera MCP, scope to mcp:all. Serwer MCP weryfikuje podpis, issuer, audience i scope i pobiera tożsamość użytkownika. Tu kończy się droga przychodząca. Ten token nigdy nie idzie downstream.
② Tożsamość usługi. Serwer MCP używa własnych poświadczeń M2M, by uzyskać token dostępu ze specjalnym zakresem access:mcp:api. Służy on tylko do wywołania endpointa z następnego kroku.
③ Pozyskanie tokena podmiotu – kluczowy krok. Serwer MCP wywołuje POST /api/mcp/subject-tokens, endpoint otwarty w Cloud specjalnie dla MCP, przekazując dwa poświadczenia jednocześnie:
Nagłówek x-mcp-user-token: token użytkownika, dowód „ten użytkownik mnie autoryzował i autoryzacja nadal jest ważna”
Cloud weryfikuje token użytkownika całkowicie: podpis, issuer, ważność, audience musi być identyfikator MCP, a scope zawierać mcp:all. Po weryfikacji userId brane jest bezpośrednio z claimu sub. Endpoint nie przyjmuje żadnego userId jako parametru.
To zabezpiecza przed nadużyciem poświadczeń M2M. Gdyby endpoint przyjmował dowolny userId, ktoś z poświadczeniem M2M mógłby podszywać się pod dowolnego użytkownika. W tej konstrukcji serwer MCP może wymienić poświadczenia tylko wtedy, gdy obecna jest ważna autoryzacja danego użytkownika.
Token podmiotu jest krótkotrwały i jednorazowy. Implementacja nigdy go nie buforuje – zawsze żąda świeżego.
④ Wymiana na tokeny robocze. Mając token podmiotu, można wymienić go na dwa typy tokenów:
Token Cloud API: operacje na poziomie użytkownika, np. wylistowanie i utworzenie najemcy
Token organizacji: ograniczony do wybranego najemcy (każdy najemca w Logto odpowiada organizacji). Token wystawiany jako użytkownik, a zakres uprawnień odzwierciedla rolę w danej organizacji
⑤ Wywołanie wychodzące. Wywołaj API biznesowe z wymienionym tokenem i zwróć wynik do klienta MCP.
Kontekst wielu najemców też rozwiązuje się na etapie wymiany: najemca to parametr wymiany, nie połączenia. list_tenants daje wybór tokenem Cloud API, użytkownik wybiera w rozmowie, a serwer MCP wymienia token organizacyjny na wybranego najemcę. Jeden endpoint obsługuje wszystkich najemców, a nowy pojawia się natychmiast po utworzeniu.
Porównując z niedoskonałościami poprzednich podejść, każde jest pokryte:
Zasięg tokena użytkownika jest ograniczony (wniosek z podejścia 1): odbiorcą jest tylko serwer MCP. Nawet jeśli wycieknie, można wywołać tylko kontrolowane narzędzia
Token M2M nie może być nadużyty (wniosek z podejścia 2): nie wywołuje już bezpośrednio API biznesowego, służy tylko za dowód tożsamości serwisu. Wydanie tokena podmiotu wymaga obecności ważnego tokena użytkownika, więc wymianę zrobisz tylko dla autoryzowanego użytkownika. Problem confused deputy znika
Nie trzeba nigdzie przechowywać poświadczeń użytkownika: tokeny podmiotu są żądane na bieżąco i zaraz usuwane. Serwer MCP przechowuje tylko własne poświadczenia M2M
Odwołanie uprawnień działa: gdy użytkownik cofa autoryzację dla MCP, token użytkownika traci ważność, sprawdzenie x-mcp-user-token się nie powodzi, łańcuch wychodzący się zatrzymuje
Uprawnienia są równe temu, co może użytkownik: wymienione tokeny są wystawiane jako użytkownik. Użytkownik tylko do odczytu przez MCP dalej pozostaje tylko do odczytu, a potencjalna eskalacja uprawnień kończy się na poziomie serwera autoryzacji. Logi audytowe także pokazują prawdziwą tożsamość
Patrząc z perspektywy: poświadczenie M2M ma w tym projekcie dokładnie swoje miejsce: daje dowód „kim jestem”, a możliwość „działania jako użytkownik” wymagana jest każdorazowo świeżym uprawnieniem użytkownika.
W całym łańcuchu strategia tokenów dla serwera MCP sprowadza się do kilku punktów:
Przychodzący i wychodzący etap uwierzytelniania to osobne sprawy, a odbiorcą tokena MCP musi być sam serwer MCP
Wychodzące rozwiązuje wymiana tokenów: jeśli serwer autoryzacji obsługuje wymianę tokena przychodzącego, standardowa wymiana wystarczy; jeśli nie (np. wymiana Logto wymaga wyjściowego tokena podmiotu), skorzystaj z funkcji podszywania się po użytkowniku
Funkcje multi-tenant nie zmieniają mechanizmu. Kontekst organizacji to parametr wymiany; produkty single-tenant po prostu go pomijają
AI działa w imieniu użytkownika, więc token downstream zawsze ma być ograniczony do tego konkretnego użytkownika
Prosty test: załóżmy, że token klienta MCP wycieka. Atakujący powinien móc wywoływać tylko kontrolowane narzędzia na serwerze MCP, ciągle w granicach uprawnień użytkownika. Jeśli może wejść do pełnego API, granica uprawnień jest złamana.
Ekosystem MCP nadal szybko się rozwija. Autoryzacja przychodząca jest dobrze opisana przez specyfikację, natomiast „jak serwer MCP wywołuje downstream” pozostaje w gestii każdego zespołu. Mamy nadzieję, że nasze praktyki okażą się przydatne.
Jeśli budujesz serwer MCP na potrzeby własnego produktu, zobacz rozwiązanie Logto dla AI: Auth dla AI, agentów i serwerów MCP. Chcesz zobaczyć cały łańcuch tokenowy w praktyce? Podłącz się do serwera MCP Logto i spróbuj.