Wie ein MCP-Server deine API im Auftrag von Nutzern aufruft: ein produktionsreifes Token-Strategie
Erläutert die Outbound-Token-Strategie für MCP-Server: warum Token-Passthrough und M2M scheitern und wie Token-Austausch die Berechtigungen auf Nutzer-Ebene hält.
YijunDeveloper
Verschwenden Sie keine Wochen mit Benutzerauthentifizierung
Bringen Sie sichere Apps schneller mit Logto auf den Markt. Integrieren Sie Benutzerauthentifizierung in Minuten und konzentrieren Sie sich auf Ihr Kernprodukt.
Wie ein MCP-Server deine API im Auftrag von Nutzern aufruft: ein produktionsreifes Token-Strategie#
Im vorherigen Artikel haben wir unsere allgemeinen Erfahrungen beim Bau des Logto Remote MCP Servers geteilt. In diesem Artikel gehen wir detailliert auf das Architekturdesign und den OAuth-Flow ein.
Mit dem MCP-Server als Grenze hat die Authentifizierung für einen Remote-MCP-Server zwei Bereiche: inbound und outbound.
Inbound: Der MCP-Client (VS Code, Cursor, etc.) meldet sich via OAuth an, erhält ein Access Token und nutzt dieses für den Zugriff auf deinen MCP-Server.
Outbound: Beim Ausführen von Tool-Aufrufen fordert der MCP-Server im Namen des Nutzers deine eigene Business-API an.
Der inbound-Bereich ist in der MCP-Spezifikation klar definiert und wurde in der Community viel diskutiert und umgesetzt. Wir haben auch vorher schon einen Implementierungsleitfaden geschrieben. Dem outbound-Bereich wird viel weniger Aufmerksamkeit geschenkt: Der MCP-Server hält nur ein Token für den Zugriff auf den MCP-Server selbst. Wie kann er im Namen des Nutzers deine Business-API aufrufen?
Mit dieser Frage haben wir uns beim Bau des Logto-MCP-Servers lange Zeit beschäftigt. Logto Cloud ist ein typisches B2B-Multi-Tenant-Produkt: Ein Nutzer kann zu mehreren Mandanten gehören; die Berechtigungen werden durch die Nutzerrolle in jedem Mandanten bestimmt. Die KI muss nicht nur als Nutzer agieren, sondern auch im richtigen Mandanten landen.
Dieser Artikel folgt unserem tatsächlichen Entscheidungsprozess:
Warum der MCP-Server unabhängig als separate geschützte Ressource bereitgestellt werden sollte
Zwei outbound-Ansätze, die wir verworfen haben (Token passthrough und M2M-Token) und deren Probleme
Das finale Design: Token-Austausch plus Subject Token, inklusive wie Organisationstoken Multi-Tenancy steuern
Einige Prinzipien, die du beachten solltest, wenn du etwas Ähnliches baust
Das Kernproblem: die Doppelrolle eines MCP-Servers#
Aus OAuth-Sicht spielt ein Remote-MCP-Server zwei Rollen gleichzeitig:
Für den MCP-Client ist er ein Resource Server: Der Client muss ein Token mitbringen, um darauf zuzugreifen.
Für die Business API ist er ein Client: Er bringt ein Token mit, um auf etwas anderes zuzugreifen.
Anders gesagt: Der MCP-Server hat auf jeder Seite eine andere Rolle und beide Seiten benutzen ein unterschiedliches Token. Das durch OAuth erhaltene Token des MCP-Clients hat den MCP-Server als Audience, daher lehnt die Business-API dieses bei der Audience-Prüfung ab.
Der outbound-Bereich ist im Grunde ein Delegationsproblem: Wie ruft der MCP-Server Downstream-APIs im Namen des Nutzers und innerhalb dessen Berechtigungen auf, ohne die Nutzer-Credentials zu speichern?
Architekturentscheidung: Der MCP-Server als eigenständiger Dienst#
Den MCP-Endpunkt einfach in die Business-API zu integrieren, also dasselbe Prozess und derselbe Auth-Stack: Das wäre die direkteste Option. Wir haben uns jedoch für eine eigenständige Bereitstellung entschieden:
Risikotrennung: Als wir diese Entscheidung trafen, war das offizielle MCP SDK noch nicht produktionsreif und das Protokoll entwickelte sich sehr schnell weiter (zum Beispiel der Wechsel von SSE zu Streamable HTTP). Logto ist ein IAM-Dienst und die Verfügbarkeit des Sign-In-Pfads hat oberste Priorität. Mit separater Bereitstellung: Wenn der MCP-Server ausfällt, fällt nur der KI-Einstiegspunkt aus. Der Hauptdienst bleibt intakt.
Unabhängige Weiterentwicklung: Das MCP-Ökosystem ändert sich wöchentlich, und Kompatibilitätsprobleme mit Clients verlangen jederzeit Hotfixes, während der Kerndienst einen strengen Release-Prozess mit Regressionstests hat. Getrennte Deployments verhindern, dass sie sich gegenseitig bremsen.
Freie Wahl zur Laufzeit: Ein eigenständiger Dienst kann die für ihn geeignete Runtime wählen. Der Logto-MCP-Server läuft auf Cloudflare Workers: zustandslos, skalierbar pro Anfrage, fast kein Betriebsaufwand. Die eingebettete Option gibt dir diese Wahl nicht.
Der MCP-Server wird auf einer eigenen Domain (mcp.logto.io) bereitgestellt, ohne private Kopplung zum Hauptdienst. Soll er eines Tages Open Source werden, steht dem nichts im Wege.
Auch die eingebaute Option kann das Token-Problem nicht lösen: Der MCP-Endpunkt und die Business-API würden dieselbe Ressourcen-ID teilen, daher würde das Token des MCP-Clients vollen API-Zugriff gewähren. Die Frage "In wessen Berechtigungsrahmen läuft dieser Aufruf?" verschiebt sich also lediglich von einem Cross-Service-Token-Austausch zum Permission Passing im Prozess. Das Problem bleibt. Die getrennte Bereitstellung zwingt dich dazu, die Berechtigungsgrenze explizit zu gestalten – und darum geht es jetzt.
Die Outbound-Diskussion beginnt mit einer grundlegenden Frage: Was sollte die Audience des Tokens sein, das der MCP-Client erhält?
Die Ressourcen-ID der Business-API wiederzuverwenden, wäre der direkteste Weg: Die Logto Management API ist bereits eine OAuth-geschützte Ressource, der MCP-Client könnte also direkt ein Token dafür anfragen und der MCP-Server würde es einfach weiterleiten. So haben das viele frühe MCP-Server-Implementierungen getan.
Der Preis: Wenn die Audience des MCP-Tokens die Business-API ist, existiert keine Berechtigungsgrenze mehr.
Deine sorgfältig gestalteten MCP-Tools sind nutzlos: Das Token allein kann die gesamte API ansprechen, ohne über Tools zu gehen.
Die Auswirkung eines geleakten Tokens wächst von "ein paar Kontrolloperationen, die der MCP-Server exponiert" zu "die gesamte Management API".
Unabhängiges Auditing, Rate Limiting und Permission Scoping speziell für MCP-Szenarios lassen sich nicht realisieren.
Unsere erste Entscheidung: Der MCP-Server ist eine eigene geschützte Ressource – mit eigener Ressourcen-ID (https://mcp.logto.io) und eigenem Scope.
Diese Entscheidung hält die Berechtigungsgrenze sauber und konkretisiert gleichzeitig das Outbound-Problem: Das Token kann nur auf den MCP-Server zugreifen – womit ruft der MCP-Server die API auf?
Die erste Idee stammt aus einer natürlichen Analogie.
Die Logto Console ist eine SPA: Die Nutzerin authentifiziert sich über OAuth im Browser, erhält ein Token mit der Management-API als Audience und der Frontend ruft die API direkt damit auf.
Kann der MCP-Server also wie eine weitere Art von Console arbeiten? Der MCP-Client fordert beim Sign-In direkt das Business-API-Token an und der MCP-Server leitet es einfach unverändert weiter:
Der Charme liegt in der Einfachheit: Der MCP-Server leitet nur Tokens weiter. Aber die Probleme sind offensichtlich:
Erstens widerspricht das direkt der getroffenen Entscheidung zur Berechtigungsgrenze. Token-Passthrough verlangt, dass der MCP-Client das Business-API-Token hält – was wir gerade ausgeschlossen haben.
Zweitens ist der MCP-Server keine echte geschützte Ressource mehr. Die Audience der empfangenen Tokens ist nicht der MCP-Server selbst, daher wird die Audience-Prüfung bedeutungslos und degeneriert zu "Signatur und Aussteller prüfen". Das entspricht nicht der MCP-Spezifikation (RFC 9728), nach der der MCP-Server sich als Resource Server deklarieren sollte. Im Endeffekt wird ein Backend-Dienst als SPA getarnt.
Drittens machen MCP-Clients nicht mit. MCP-Clients, die sich an die Spezifikation halten, fordern Tokens nach Protected Resource Metadata an und setzen die Audience auf den MCP-Server. Es gibt keinen Standardweg, damit ein Client das Token für die Business-API anfordert – dieser Weg funktioniert also clientseitig nicht.
Wenn das Nutzer-Token nicht funktioniert, wie wäre es mit der eigenen Identität des MCP-Servers?
Wir geben dem MCP-Server eine M2M (machine-to-machine)-Anwendung, holen ein Token per Client Credentials und greifen damit auf die Business-API zu. Das ist Standard zwischen internen Diensten.
Inbound-Auth funktioniert jetzt: Das MCP-Client-Token hat den MCP-Server als Audience, dieser validiert es, dann arbeitet er mit dem eigenen M2M-Token weiter.
Der fatale Fehler: Die Berechtigungen des M2M-Tokens haben nichts mit denen des Nutzers zu tun:
Berechtigungsüberschreitung: Die M2M-Berechtigungen beziehen sich auf "was der MCP-Server darf", nicht auf "was dieser Nutzer darf". Ein User mit Leserechten könnte per MCP trotzdem Apps löschen, da das M2M-Token diese Möglichkeit liefert.
Verlust der Identität: Die Downstream-API sieht immer die M2M-Anwendung als Aufrufer, Logs lassen sich nicht zurückverfolgen.
Confused Deputy: Der MCP-Server wird zum hochprivilegierten Proxy und jeder mit Zugriff kann seine Berechtigungen "leihen".
M2M passt für Szenarien ohne Nutzerkontext – etwa geplante Jobs oder System-Syncs. Ein MCP-Server ist anders: Jeder Aufruf kommt konkret von einem Nutzer und muss mit dessen Identität/Berechtigungen laufen.
Das finale Design: Token-Austausch + Subject Token#
Aus den beiden Ansätzen ergeben sich die Anforderungen:
Das vom MCP-Client gehaltene Token darf nur auf den MCP-Server zugreifen (Lektion aus Ansatz 1).
Beim Aufruf von Downstream-APIs muss der MCP-Server als Nutzer agieren, mit dessen Berechtigungen (Lektion aus Ansatz 2).
Das führt zu einem Standardmechanismus: Token Exchange (RFC 8693). Der MCP-Server nimmt ein Nutzer-bezogenes Credential und tauscht es beim Auth-Server gegen ein Downstream-API-Token. Identität und Berechtigungen bleiben erhalten.
Bei Logto ist dieses "Credential, das den Nutzer repräsentiert" das User Impersonation: das Subject Token. Es ist ein kurzlebiges Credential, das der Server für einen bestimmten Nutzer abruft, mit der Bedeutung "der nächste Token-Austausch läuft in dessen Namen". Der Nutzer muss nichts konfigurieren – der Ablauf ist komplett automatisiert. Dieses Subject Token ist nur kurz gültig, kann einmal verwendet werden und verfällt dann.
In der Architektur gibt es vier Rollen; der MCP-Server ist unabhängig unter mcp.logto.io bereitgestellt:
Der vollständige Token-Flow beim Aufruf eines Tools:
Schritt für Schritt:
① Inbound-Validierung. Der MCP-Client ruft ein Tool mit dem Nutzer-Token auf. Die Audience ist die eigene Ressourcen-ID des MCP-Servers, Scope mcp:all. Der MCP-Server prüft Signatur, Aussteller, Audience und Scope und erhält die Identität des Nutzers. Hier endet Inbound. Dieses Token geht nie "downstream" weiter.
② Service-Identität. Der MCP-Server nutzt die eigenen M2M Credentials für ein Access Token mit dediziertem Scope access:mcp:api. Dieses Token dient ausschließlich für den folgenden Schritt.
③ Subject Token anfordern – der entscheidende Schritt. Der MCP-Server ruft POST /api/mcp/subject-tokens auf, ein dedizierter Cloud-Endpunkt für MCP, und bringt zwei Nachweise:
Authorization-Header: das M2M-Token – Beweis "Ich bin der offizielle MCP-Server"
x-mcp-user-token-Header: das Nutzer-Token – Beweis "Dieser Nutzer hat mich autorisiert, und die Autorisierung ist noch gültig"
Cloud prüft das Nutzer-Token vollständig: Signatur, Aussteller, Ablauf, Audience = Ressourcen-ID des MCP-Servers, und Scope enthält mcp:all. Die userId kommt direkt aus dem sub-Claim. Es gibt keinen Parameter, um einen Nutzer anzugeben.
Dieses Design verhindert, dass das M2M Credential missbraucht wird. Würde der Endpunkt eine beliebige userId akzeptieren, könnte jeder mit dem M2M Credential beliebige Nutzer imitieren. Mit diesem Aufbau kann der MCP-Server Credentials nur für Nutzer austauschen, deren gültige Autorisierung vorliegt.
Das Subject Token ist kurzlebig und nur einmal nutzbar. Es wird nie zwischengespeichert, sondern immer frisch angefordert.
④ Token Exchange für Nutzertokens. Mit dem Subject Token wird auf Standardweg der Token Exchange durchgeführt. Zwei Token kommen dabei raus:
Cloud API Token: Für user-level-Operationen als Nutzer, z. B. Mandanten auflisten/erstelle
Org Token: Bezogen auf einen spezifischen Mandanten (in Logto Cloud ist jeder Mandant eine Organisation). Das Token wird als Nutzer ausgestellt mit den Rollenspezifischen Berechtigungen für diesen Mandanten.
⑤ Outbound-Call. Die Geschäfts-API wird mit dem getauschten Token aufgerufen und das Ergebnis an den MCP-Client zurückgegeben.
Der Multi-Tenancy-Kontext wird ebenfalls im Exchange-Schritt aufgelöst: Der Mandant lebt in der Exchange-Schicht, nicht in der Verbindungsschicht.list_tenants liefert die Optionen mit dem Cloud API Token, die Auswahl erfolgt im Dialog, und der MCP-Server tauscht ein Org Token für den gewählten Mandanten. Ein Endpunkt reicht für alle Mandanten, keine Mandant-spezifische Bereitstellung, und ein neuer Mandant ist sofort verfügbar.
Gegenprüfung bei den gescheiterten Ansätzen – jeder Punkt ist abgedeckt:
Das Nutzer-Token bleibt eingeschränkt (s. Ansatz 1): Audience ist nur MCP-Server. Auch geleakt, kann es nur die kontrollierten Tools aufrufen.
Das M2M-Token kann nicht missbraucht werden (s. Ansatz 2): Es ruft nicht mehr direkt die Business-API auf, sondern dient als Service-Nachweis. Für das Subject Token ist zusätzlich ein gültiges Nutzer-Token erforderlich – der MCP-Server kann Credentials also nur für autorisierte Nutzer tauschen. Das Confused Deputy Problem entfällt.
Keine Nutzer-Credentials werden gespeichert: Subject Tokens werden immer direkt, nur bei Benutzung erstellt und gleich wieder verworfen. Der MCP-Server speichert nur M2M Credentials.
Widerruf funktioniert korrekt: Widerruft der Nutzer die MCP-Autorisierung, wird das Token ungültig, die x-mcp-user-token-Prüfung schlägt fehl und die Chain bricht sofort ab.
Berechtigungen stimmen mit dem Nutzer überein: Getauschte Tokens werden im Namen des Nutzers ausgestellt. Leserechte bleiben auch für die KI erhalten, Privilegienerweiterung scheitert bereits beim Auth-Server. Logs zeigen die echte Nutzeridentität.
Im Nachhinein ist das M2M-Credential ideal platziert: Es beweist "wer bin ich", während "im Namen eines Nutzers handeln" immer ad hoc mit dessen gültiger Autorisierung getauscht werden muss.
Über die gesamte Kette betrachtet, läuft die Token-Strategie eines Remote-MCP-Servers auf diese Punkte hinaus:
Inbound und Outbound sind zwei eigenständige Bereiche der Authentifizierung. Die Audience des MCP-Tokens muss der MCP-Server selbst sein.
Outbound löst man via Token Exchange: Unterstützt der Auth-Server einen direkten Token-Austausch, reicht der Standard-Exchange; sonst (wie bei Logto, wo der Austausch ein Subject Token braucht), startet man den Exchange mit einer Impersonation-ähnlichen Funktion serverseitig.
Multi-Tenancy verändert das Prinzip nicht: Der Organisationskontext ist Parameter zum Exchange-Zeitpunkt; Single-Tenant-Produkte ignorieren ihn einfach.
Die KI handelt immer im Namen des Nutzers, daher müssen Downstream-Tokens auf dessen Berechtigungen beschränkt sein.
Ein einfacher Test: Angenommen, das vom MCP-Client gehaltene Token wird geleakt. Alles, was ein Angreifer tun kann, ist, die kontrollierten Tools auf dem MCP-Server zu rufen – und das auch nur im Rahmen der Nutzer-Berechtigungen. Ist stattdessen der volle API-Zugang möglich, ist die Berechtigungsgrenze verletzt.
Das MCP-Ökosystem entwickelt sich schnell weiter. Die Inbound-Authentifizierung ist in der Spezifikation gut abgedeckt, während "wie der MCP-Server Downstream aufruft" jeweils von den Teams selbst gelöst wird. Wir hoffen, unsere Praxis ist dir dabei eine hilfreiche Referenz!