CodeGym /Kurse /ChatGPT Apps /MCP Server als geschützte Ressource konfigurieren:

MCP Server als geschützte Ressource konfigurieren: .well-known, Bearer, Audience/Scope

ChatGPT Apps
Level 10 , Lektion 3
Verfügbar

1. MCP Server als Resource Server: Was genau konfigurieren wir

In der letzten Vorlesung haben wir den Auth Server konfiguriert – die Komponente, die Tokens ausstellt. Jetzt kümmern wir uns um die zweite Seite dieses Duos: den MCP‑Server als Resource Server, der diese Tokens entgegennimmt und prüft.

Aus Sicht von OAuth 2.1 ist Ihr MCP‑Server ein Resource Server. Er speichert „Ressourcen“ (MCP‑Tools, Benutzerdaten) und empfängt Anfragen mit dem Access‑Token im Header Authorization: Bearer .... Bevor ein Tool ausgeführt wird, muss er prüfen, dass das Token echt ist, nicht abgelaufen, von einem vertrauenswürdigen Autorisierungsserver (Auth Server) ausgestellt wurde, genau für diesen MCP‑Server bestimmt ist und die erforderlichen Rechte (scope) mitbringt.

Wichtig ist die Trennung in zwei Ebenen:

  1. Transportschicht – hier werden HTTP‑Header und Tokens verarbeitet. Dort:
    • nehmen Sie Authorization: Bearer entgegen/parsen ihn,
    • geben Sie bei fehlendem/fehlerhaftem Token 401 Unauthorized mit WWW-Authenticate: Bearer ... zurück,
    • bilden Sie bei gültigem Token den Benutzerkontext.
  2. MCP‑SDK‑Ebene, die gar nichts über JWT wissen muss. Sie erhält einfach einen „bereits authentifizierten“ Aufruf und kann im Handler ctx.userId, ctx.scopes usw. verwenden.

Analogie: Das MCP SDK ist der Koch in der Küche, das OAuth‑Middleware der Sicherheitsdienst am Eingang. Der Koch kontrolliert keine Pässe, er bereitet einfach Bestellungen zu.

Als Übungsbeispiel bleiben wir bei GiftGenius: ein MCP‑Server auf http://localhost:3000 mit dem Tool list_my_gifts und ein Auth Server (z. B. Keycloak oder ein eigener Mini‑AS) auf http://localhost:4000.

2. .well-known/oauth-protected-resource: die Visitenkarte Ihrer MCP‑Ressource

Wozu dient .well-known für die Ressource

Wenn ChatGPT (oder MCP Jam) Ihren MCP‑Server zum ersten Mal anfragt und 401 erhält, muss der Client zwei Dinge verstehen:

  • wo er ein Token bekommt;
  • welche Berechtigungen diese Ressource überhaupt unterstützt.

Damit Clients das nicht „hardcodieren“ müssen, gibt es einen Discovery‑Endpunkt:

GET /.well-known/oauth-protected-resource

Dieser Endpunkt liefert JSON mit Metadaten der geschützten Ressource (Protected Resource Metadata) gemäß RFC 9728.

Beispiel aus GiftGenius:

{
  "resource": "http://localhost:3000",
  "authorization_servers": ["http://localhost:4000"],
  "scopes_supported": ["gifts:read", "gifts:write"],
  "bearer_methods_supported": ["header"]
}

OpenAI zeigt in seinen Leitfäden ein fast identisches Beispiel, nur mit HTTPS und echten Domains.

Der Client (ChatGPT/Jam) liest dieses Dokument und:

  • erkennt, dass das Token die Audience http://localhost:3000 haben muss,
  • erkennt, mit welchen authorization_servers (Issuer‑URLs) er arbeiten soll,
  • sieht die Liste der unterstützten Scopes (damit ist das Consent‑UI und die Anforderung einfacher).

Feldübersicht der Metadaten

Zusammenfassung der wichtigsten Felder:

Feld Zweck
resource
Der kanonische HTTPS/HTTP‑Bezeichner des MCP‑Servers. Entspricht später dem aud des Tokens.
authorization_servers
Liste der URLs Ihrer Autorisierungsserver (Auth Server/Issuer). Der Client holt dort OAuth/OIDC‑Metadaten.
scopes_supported
Array der unterstützten Scopes; dient dem Client für gutes UX und eine korrekte Tokenanforderung.
bearer_methods_supported
Übertragungsarten des Tokens: üblicherweise ["header"], d. h. Authorization: Bearer ....

Zusätzlich werden manchmal resource_documentation, jwks_uri, introspection_endpoint u. a. veröffentlicht, aber für das Basisszenario reichen die ersten vier.

Kritischer Punkt: resource muss dem entsprechen, was der Auth Server ins aud des Tokens schreibt. Wenn das nicht übereinstimmt, meckern der MCP‑Client (und Sie selbst) und lehnen das Token ab.

Implementierung von .well-known in Next.js 16

Nehmen wir an, unser MCP‑Server läuft in einer Next.js‑App (Apps SDK Backend, Port 3000). Der einfachste Weg ist ein Route‑Handler unter app/.well-known/oauth-protected-resource/route.ts:


// app/.well-known/oauth-protected-resource/route.ts
import { NextResponse } from "next/server";

export async function GET() {
  const body = {
    resource: "http://localhost:3000",
    authorization_servers: ["http://localhost:4000"],
    scopes_supported: ["gifts:read", "gifts:write"],
    bearer_methods_supported: ["header"],
  };

  return NextResponse.json(body);
}

In Produktion sollte resource die HTTPS‑URL der Prod‑Umgebung Ihres MCP‑Servers sein (z. B. https://mcp.giftgenius.com) und mit dem aud in den Tokens des IdP übereinstimmen.

3. WWW-Authenticate und 401: wie der MCP mitteilt, dass ein Token erforderlich ist

Wir haben bereits die „Visitenkarte“ der Ressource unter .well-known/oauth-protected-resource erstellt. Schauen wir uns nun an, wie der MCP‑Server dem Client mitteilt, dass er diese Visitenkarte überhaupt abrufen soll – über 401 und den Header WWW-Authenticate.

Basisszenario: Aufruf ohne Token

Stellen wir uns vor, ChatGPT ruft das Tool list_my_gifts zum ersten Mal auf. Die Netzwerkanfrage sieht etwa so aus:

GET /mcp/tools/list_my_gifts HTTP/1.1
Host: localhost:3000

Es gibt kein Token. Der MCP‑Server sollte nicht stumm 403 oder irgendeine HTML‑Seite zurückgeben. Korrektes Verhalten einer geschützten Ressource in der OAuth‑Welt ist, 401 Unauthorized zu senden und über den Header WWW-Authenticate zu erklären, wie man sich autorisiert.

Beispiel für eine korrekte Antwort:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="http://localhost:3000/.well-known/oauth-protected-resource", scope="gifts:read"
Content-Type: application/json

{"error":"unauthorized","error_description":"Missing or invalid access token"}

Wichtige Details:

  • das Schema Bearer signalisiert, dass wir ein OAuth‑Bearer‑Token erwarten;
  • der Parameter resource_metadata verweist auf die URL von .well-known/oauth-protected-resource;
  • der Parameter scope gibt an, welche minimale Berechtigung erforderlich ist (z. B. gifts:read).

MCP Jam und ChatGPT können diesen Header auswerten. Sobald sie ihn sehen, werden sie:

  1. .well-known/oauth-protected-resource abrufen.
  2. Über authorization_servers den Auth Server und dessen OpenID/OAuth‑Metadaten finden.
  3. Den Authorization‑Code‑Flow mit PKCE starten, dem Benutzer eine Login‑Seite öffnen und ein Token erhalten.

Das heißt, WWW-Authenticate ist der Auslöser: Ohne ihn kommt der Client gar nicht auf die Idee, dass hier OAuth im Spiel ist.

Middleware für 401‑Antworten (Next.js)

Schreiben wir eine kleine Utility, die auf allen geschützten Endpunkten verwendet wird. Zuerst eine Funktion, die die Antwort formt:

// lib/authResponses.ts
import { NextResponse } from "next/server";

export function unauthorized(scope?: string) {
  const wwwAuth = [
    `Bearer resource_metadata="http://localhost:3000/.well-known/oauth-protected-resource"`,
    scope ? `scope="${scope}"` : null,
  ]
    .filter(Boolean)
    .join(", ");

  return new NextResponse(
    JSON.stringify({
      error: "unauthorized",
      error_description: "Missing or invalid access token",
    }),
    {
      status: 401,
      headers: {
        "WWW-Authenticate": wwwAuth,
        "Content-Type": "application/json",
      },
    }
  );
}

Jeder beliebige Route (z. B. unser MCP‑Endpunkt) kann nun einfach return unauthorized("gifts:read") verwenden, und der Client erhält den korrekten Challenge. Die Funktion unauthorized() gibt ein NextResponse‑Objekt zurück (kompatibel mit dem Standard‑Response). In späteren Beispielen werden wir dieses Objekt gelegentlich als Ausnahme werfen und in den Route‑Handlern speziell Response abfangen, um die Bildung der 401‑Antwort nicht in jedem Route zu duplizieren.

4. Empfang und Verifikation des Bearer‑Tokens

Jetzt zum interessantesten Teil: Wie nimmt man ein Bearer‑Token entgegen und prüft es.

Wo prüfen?

Ihr MCP‑Transport ist höchstwahrscheinlich entweder:

  • ein Next.js‑Route‑Handler (app/mcp/route.ts), der POST annimmt und dann in das MCP SDK delegiert,
  • oder ein Express/Fastify‑Server, der auf /mcp hört und JSON an den MCP‑Handler weitergibt.

In all diesen Varianten muss die HTTP‑Schicht:

  1. Authorization aus dem Header entnehmen,
  2. bei Fehlen/Fehler unsere unauthorized mit 401 zurückgeben,
  3. bei Erfolg ein Kontextobjekt (userId, scopes, roles) erzeugen und an das MCP SDK übergeben (über Handler‑Argumente/Kontext).

Das MCP SDK (z. B. @modelcontextprotocol/sdk) muss überhaupt nicht wissen, was ein JWT ist. Das ist Ihre Verantwortung.

Prüfvarianten: JWT vs. Introspection

Es gibt zwei gängige Ansätze:

  1. Die Signatur und Claims des JWT lokal prüfen, mithilfe der JWK‑Keys des Auth Servers.
  2. Den Autorisierungsserver unter /introspect fragen: „Lebt dieses Token noch? Welche Scopes hat es?“.

In diesem Kurs gehen wir davon aus, dass der Auth Server JWTs ausstellt und jwks_uri veröffentlicht, während der MCP‑Server Signatur und Claims lokal prüft (das ist schneller und autonomer).

Hilfsfunktion verifyAccessToken in TypeScript

Wir verwenden die populäre Bibliothek jose (ESM‑freundlich). Wir benötigen in etwa folgenden Helper:

// lib/verifyAccessToken.ts
import { jwtVerify, createRemoteJWKSet } from "jose";

const JWKS = createRemoteJWKSet(
  new URL("http://localhost:4000/.well-known/jwks.json")
);
const EXPECTED_ISS = "http://localhost:4000";
const EXPECTED_AUD = "http://localhost:3000";

export async function verifyAccessToken(token: string) {
  const { payload } = await jwtVerify(token, JWKS, {
    issuer: EXPECTED_ISS,
    audience: EXPECTED_AUD,
  });

  return {
    sub: String(payload.sub),
    scopes: String(payload.scope || "").split(" ").filter(Boolean),
    raw: payload,
  };
}

In diesem Helper:

  • laden wir die JWK‑Keys des Auth Servers über jwks_uri,
  • prüfen wir Signatur und Standard‑Claims (iss, aud),
  • extrahieren wir sub (User‑ID) und scope (String mit Leerzeichen, daher split(" ")).

audience muss mit dem resource aus unserem .well-known/oauth-protected-resource übereinstimmen – so ist sichergestellt, dass dieses Token genau für unseren MCP‑Server ausgestellt wurde.

Einfache Prüfung des Authorization‑Headers

Nun erstellen wir einen kleinen Helper, der das Token aus dem Header holt und durch verifyAccessToken schickt:

// lib/getUserFromRequest.ts
import type { NextRequest } from "next/server";
import { unauthorized } from "./authResponses";
import { verifyAccessToken } from "./verifyAccessToken";

export async function getUserFromRequest(req: NextRequest) {
  const auth = req.headers.get("authorization") || "";
  const [, token] = auth.split(" ");

  if (!token) throw unauthorized("gifts:read");

  try {
    return await verifyAccessToken(token);
  } catch {
    throw unauthorized("gifts:read");
  }
}

Beachten Sie: Hier werfen wir unauthorized(...) (also ein Response‑Objekt) als Ausnahme, damit man es im Route‑Handler elegant abfangen und direkt als Antwort zurückgeben kann.

5. Audience und Scope: Bindung des Tokens an Ressource und Aktionen

Audience (aud): „für wen“ das Token ausgestellt ist

Der Claim aud beantwortet die Frage, ob das Token für diese Ressource bestimmt ist. In unserem Fall:

  • setzt der Auth Server aud im Token auf http://localhost:3000;
  • unser .well-known/oauth-protected-resource veröffentlicht resource: "http://localhost:3000";
  • verifyAccessToken prüft genau das.

Ist das Token für eine andere Ressource bestimmt (z. B. https://api.other-app.com), muss Ihr MCP‑Server es als „nicht an mich adressiert“ ablehnen.

Ein typischer Fehler ist es, resource und aud nicht zu synchronisieren. Dann scheint alles konfiguriert, aber ChatGPT erhält ständig 401. Darauf kommen wir im Block „Häufige Fehler“ zurück.

Scopes: „was genau“ erlaubt ist

Der Claim scope im Token ist die Liste der Berechtigungen, die der Benutzer dem Client erteilt hat. In unserem Beispiel:

  • gifts:read – Recht, die eigenen Geschenke zu lesen;
  • gifts:write – Recht, Geschenke zu erstellen/aktualisieren.

In .well-known/oauth-protected-resource erscheinen diese Werte als scopes_supported, damit der Client im Voraus weiß, was er anfordern kann.

Der Autorisierungsserver veröffentlicht in seinem Discovery‑Dokument (.well-known/openid-configuration) ebenfalls scopes_supported, das ist jedoch die Liste der globalen Scopes des IdP. (bitte nicht verwechseln mit .well-known/oauth-protected-resource des Resource Servers)

Verwechseln Sie diese beiden Listen nicht: scopes_supported der Ressource beschreibt, welche Rechte speziell Ihr MCP‑Server benötigt, während scopes_supported des IdP den gesamten „Katalog“ der globalen Scopes des Providers darstellt. Der Client nimmt in der Regel die Schnittmenge beider Welten.

Auf Ebene des MCP‑Servers müssen Sie:

  • für jedes Tool festlegen, welche Scopes erforderlich sind,
  • bei jedem Tool‑Aufruf prüfen, dass das Token diese Scopes enthält.

Schreiben wir einen Helper:

// lib/requireScope.ts
import { unauthorized } from "./authResponses";

export function requireScope(
  user: { scopes: string[] },
  needed: string[]
) {
  const hasAll = needed.every((s) => user.scopes.includes(s));
  if (!hasAll) throw unauthorized(needed.join(" "));
}

Jetzt können wir vor der Ausführung eines Tools requireScope(user, ["gifts:read"]) aufrufen.

6. Verknüpfung mit MCP‑Tools: vom Token zu list_my_gifts

MCP‑Route in Next.js

Angenommen, wir haben einen MCP‑Server auf Basis eines SDKs, der HTTP‑Anfragen verarbeiten kann. Aus Sicht von Next.js könnte das so aussehen:

// app/api/mcp/route.ts
import { NextRequest } from "next/server";
import { unauthorized } from "@/lib/authResponses";
import { getUserFromRequest } from "@/lib/getUserFromRequest";
import { mcpServer } from "@/lib/mcpServer";

export async function POST(req: NextRequest) {
  try {
    const user = await getUserFromRequest(req);

    const body = await req.json();
    const result = await mcpServer.handle(body, { user });

    return Response.json(result);
  } catch (err) {
    if (err instanceof Response) return err; // unauthorized(...)
    console.error(err);
    return unauthorized();
  }
}

Wichtig ist hier:

  • wir extrahieren Benutzer und Scopes aus dem Token (getUserFromRequest),
  • geben sie über den Kontext { user } an den MCP‑Server weiter,
  • geben bei fehlendem/fehlerhaftem Token unseren 401 mit WWW-Authenticate zurück.

Die konkrete API des MCP SDK kann variieren, aber die Idee ist überall dieselbe: den MCP‑Aufruf mit einem Middleware‑Schutz auf HTTP‑Ebene zu umhüllen, der bereits weiß, „wer“ anklopft.

Tool list_my_gifts mit Scope‑Prüfung

Schauen wir in die Implementierung des Tools. Nehmen wir an, wir verwenden ein TypeScript‑SDK für MCP und haben etwas in der Art:

// lib/mcpServer.ts (Ausschnitt)
import { createMcpServer } from "@modelcontextprotocol/sdk";
import { requireScope } from "./requireScope";

export const mcpServer = createMcpServer<{ user: any }>();

mcpServer.registerTool(
  "list_my_gifts",
  {
    title: "List my gifts",
    description: "Shows your saved gift ideas.",
    inputSchema: { type: "object", properties: {}, additionalProperties: false },
  },
  async (_input, ctx) => {
    requireScope(ctx.user, ["gifts:read"]);

    const gifts = await loadGiftsForUser(ctx.user.sub);
    return {
      content: [{ type: "text", text: `Found ${gifts.length} gifts` }],
      structuredContent: { gifts },
    };
  }
);

Wir machen drei zentrale Schritte:

  • wir verlangen gifts:read vor der Ausführung des Hauptcodes,
  • wir verwenden ctx.user.sub als Benutzeridentifier (aus dem Token),
  • wir liefern Daten nur für diesen Benutzer zurück.

So ist Ihr Tool nicht länger ein „allgemeines API“, sondern personalisiert – gebunden an die Identity des Auth Servers.

7. Ablaufübersicht: von 401 bis zum erfolgreichen Aufruf

Zur Festigung fassen wir den Ablauf zusammen, den Ihr geschützter MCP‑Server nun implementiert.

sequenceDiagram
    participant ChatGPT
    participant MCP as MCP Server (3000)
    participant AS as Auth Server (4000)

    ChatGPT->>MCP: POST /api/mcp (no Authorization)
    MCP-->>ChatGPT: 401 + WWW-Authenticate: Bearer resource_metadata=...

    ChatGPT->>MCP: GET /.well-known/oauth-protected-resource
    MCP-->>ChatGPT: { resource, authorization_servers, scopes_supported }

    ChatGPT->>AS: GET /authorize?scope=gifts:read&resource=...
    AS-->>ChatGPT: redirect with ?code=XYZ

    ChatGPT->>AS: POST /token (code + code_verifier)
    AS-->>ChatGPT: { access_token, scope, ... }

    ChatGPT->>MCP: POST /api/mcp Authorization: Bearer token
    MCP->>MCP: verify JWT (iss, aud, exp, scope)
    MCP-->>ChatGPT: tool result for this user

Achten Sie auf den Parameter resource in den Anfragen an den Auth Server: Er wird ins aud des Tokens übernommen und muss mit resource in .well-known/oauth-protected-resource übereinstimmen.

8. Kleine praktische Prüfung mit curl

Zur Beruhigung können wir zwei Anfragen manuell ausführen.

Erstens – Versuch, den MCP ohne Token aufzurufen:

curl -i http://localhost:3000/api/mcp \
  -H "Content-Type: application/json" \
  -d '{"method":"tools/call","params":{"name":"list_my_gifts","arguments":{}}}'

Wir erwarten den Status 401 und unseren WWW-Authenticate mit resource_metadata und scope="gifts:read".

Zweitens – mit einem gültigen Token (vom Auth Server erhalten):

curl -i http://localhost:3000/api/mcp \
  -H "Authorization: Bearer abc123" \
  -H "Content-Type: application/json" \
  -d '{"method":"tools/call","params":{"name":"list_my_gifts","arguments":{}}}'

Wenn abc123 ein gültiges JWT mit korrekt gesetzten iss, aud="http://localhost:3000" ist und der scope gifts:read enthält, erhalten Sie die JSON‑Antwort des Tools, und in structuredContent.gifts stehen die Geschenke des aktuellen Benutzers.

9. Häufige Fehler bei der Konfiguration des MCP Server als geschützte Ressource

Im Folgenden eine Sammlung typischer Stolpersteine genau bei der Implementierung dessen, was wir gerade gebaut haben: .well-known, WWW-Authenticate, Token‑Verifikation und Scope‑Prüfung.

Fehler Nr. 1: Nicht synchronisierte resource und audience.
Häufig steht in .well-known/oauth-protected-resource ein Wert für resource, während der Auth Server in Tokens einen anderen aud ausstellt. Am Ende verwirft jwtVerify das Token, selbst wenn Signatur und Lebensdauer passen. Besonders leicht passiert das, wenn Sie Domain/Port des MCP‑Servers ändern und vergessen, entweder .well-known oder die Konfiguration des Auth Servers zu aktualisieren. In unserem Beispiel ist es dieselbe Zeichenkette http://localhost:3000 im Feld resource von .well-known und in EXPECTED_AUD innerhalb von verifyAccessToken. Legen Sie am besten eine einzige Konstante RESOURCE_ID an und verwenden Sie sie an beiden Stellen, um Abweichungen zu vermeiden.

Fehler Nr. 2: Fehlendes WWW-Authenticate bei 401.
Entwickler geben manchmal einfach 401 oder 403 ohne den Header WWW-Authenticate zurück. Aus Browser‑Sicht mag das ok sein, aber ChatGPT und MCP Jam wissen dann nicht, wohin sie für das Token gehen sollen und welche Scopes erforderlich sind. Infolgedessen halten sie Ihren MCP‑Server für „kaputt“ und zeigen dem Benutzer kein Linking‑UI. Das notwendige Minimum ist: WWW-Authenticate: Bearer mit resource_metadata=".../.well-known/oauth-protected-resource". Besser gleich auch scope="..." hinzufügen, damit der Flow transparenter ist. Unser Helper unauthorized() stellt sicher, dass dieser Header bei 401 immer vorhanden ist.

Fehler Nr. 3: Vertrauen in das Token ohne Signatur‑ und iss‑Prüfung.
Gerade in frühen Phasen ist die Versuchung groß: „Es ist doch ein Token, es kommt von meinem Auth Server, ich mache einfach JSON.parse(atob(..)) und fertig“. Das geht nicht: Sie akzeptieren dann jedes Token im richtigen Format, auch ein manipuliertes. Der richtige Weg ist, die Keys über jwks_uri zu laden und Signatur sowie iss/aud mit einer Bibliothek (jose, jsonwebtoken u. a.) zu prüfen. Erst danach dürfen Sie den Claims vertrauen.

Fehler Nr. 4: Vermischung von Token‑Prüfung und Business‑Logik.
Manchmal verteilt sich die Token‑Prüfung über den Tool‑Code: Ein Tool prüft den scope, ein anderes nicht; irgendwo hat man aud vergessen, anderswo akzeptiert man eine Benutzer‑ID aus dem Tool‑Argument. Das führt zu merkwürdigen Bugs und potenziellen Schwachstellen. Besser ist eine klare Trennung: Das Middleware auf HTTP‑Ebene kümmert sich um das Token (Signatur, iss, aud, Ablauf), und im Tool stützen Sie sich auf ctx.user als „Single Source of Truth“ und ergänzen nur fachliche Checks (z. B. Rolle/Tenant).

Fehler Nr. 5: Inkonsistenz zwischen scopes_supported und tatsächlich genutzten Scopes.
Ein weiterer Klassiker: In .well-known/oauth-protected-resource veröffentlichen Sie einen Scope‑Satz, im Auth Server gibt es einen anderen, und in den Tools prüfen Sie einen dritten. ChatGPT/MCP Jam bilden die Autorisierungsanfrage auf Basis der veröffentlichten scopes_supported, während Ihr Server später meckert, dass der erforderliche Scope fehlt. Minimieren Sie die Anzahl der Scopes und führen Sie sie als „Single Source of Truth“ – z. B. per TypeScript‑Enum, das sowohl bei der Generierung von .well-known als auch in der Client‑Konfiguration im Auth Server verwendet wird.

Fehler Nr. 6: Sich nur auf Apps SDK‑securitySchemes verlassen und die Server‑Prüfung vergessen.
Das Apps SDK erlaubt, securitySchemes für Tools zu beschreiben (noauth, oauth2, Scopes), und ChatGPT zeigt dem Benutzer dann das passende UX. Aber diese Annotationen machen Ihren Server nicht automatisch sicher. Selbst wenn ein Tool als OAuth‑pflichtig deklariert ist, muss Ihr MCP‑Server bei jeder Anfrage Token, Issuer, Audience und Scopes prüfen. Andernfalls lassen sich Prüfungen umgehen, indem man direkt die MCP‑URL aufruft.

Fehler Nr. 7: Nur auf lange lebende Tokens setzen bzw. Ablauf nicht behandeln.
Wenn Access‑Tokens zu lange leben, leidet die Sicherheit; wenn sie zu kurz leben, der Server aber das Ablaufen nicht sauber behandelt, stößt der Benutzer ständig auf Fehler. Das richtige Modell ist ein kurzlebiges Access‑Token plus die Fähigkeit des MCP‑Servers, bei abgelaufenem exp ein 401 mit WWW-Authenticate zurückzugeben. Der Client (ChatGPT) wiederholt dann den OAuth‑Flow und aktualisiert das Token.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION