主导航

身份验证

Apps SDK 应用的身份验证模式。

对您的用户进行身份验证

许多 Apps SDK 应用可以在只读的匿名模式下运行,但任何暴露客户特定数据或涉及写入操作的应用都应验证用户身份。

当您需要连接到现有后端或在用户之间共享数据时,可以与您自己的授权服务器进行集成。

使用 OAuth 2.1 进行自定义身份验证

对于经过身份验证的 MCP 服务器,您需要实现符合 MCP 授权规范 的 OAuth 2.1 流程。

组件

  • 资源服务器 – 您的 MCP 服务器,负责公开工具并在每个请求上验证访问令牌。
  • 授权服务器 – 您的身份提供商(Auth0、Okta、Cognito 或自定义实现),负责颁发令牌并发布发现元数据。
  • 客户端 – 代表用户行事的 ChatGPT。它支持客户端 ID 元数据文档 (CIMD)、动态客户端注册 (DCR)、预定义 OAuth 客户端和 PKCE。

MCP 授权规范要求

  • 在您的 MCP 服务器上托管受保护的资源元数据
  • 从您的授权服务器发布 OAuth 元数据
  • 在整个 OAuth 流程中回传 resource 参数
  • 选择 ChatGPT 识别或注册其 OAuth 客户端的方式:CIMD、DCR 或预定义 OAuth 客户端
  • 发布您的授权服务器接受的令牌端点身份验证方法

以下是该规范的通俗解释。

在您的 MCP 服务器上托管受保护的资源元数据

  • 您需要一个 HTTPS 端点(例如 GET https://your-mcp.example.com/.well-known/oauth-protected-resource,或者在 401 Unauthorized 响应的 WWW-Authenticate 标头中通告同一 URL),以便 ChatGPT 知道在哪里获取您的元数据。
  • 该端点返回一个 JSON 文档,描述资源服务器及其可用的授权服务器。
{
  "resource": "https://your-mcp.example.com",
  "authorization_servers": ["https://auth.yourcompany.com"],
  "scopes_supported": ["files:read", "files:write"],
  "resource_documentation": "https://yourcompany.com/docs/mcp"
}
  • 您必须填充的关键字段
    • resource:您的 MCP 服务器的规范 HTTPS 标识符。在 OAuth 过程中,ChatGPT 会将此确切值作为 resource 查询参数发送。
    • authorization_servers:一个或多个指向您身份提供商的颁发者基本 URL。ChatGPT 将尝试每一个以查找 OAuth 元数据。
    • scopes_supported:可选列表,有助于 ChatGPT 向用户说明它将请求哪些权限。
    • 来自 RFC 9728 的可选扩展(如 resource_documentationtoken_endpoint_auth_methods_supportedintrospection_endpoint)使客户端和管理员更容易理解您的设置。

当您因未通过身份验证而拦截请求时,请返回如下质询:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://your-mcp.example.com/.well-known/oauth-protected-resource",
                         scope="files:read"

这一个标头即使在 ChatGPT 之前未见过该 URL 的情况下,也能让其发现元数据 URL。

从您的授权服务器发布 OAuth 元数据

  • 您的身份提供商必须公开其中一个众所周知的发现文档,以便 ChatGPT 可以读取其配置。
    • 位于 https://auth.yourcompany.com/.well-known/oauth-authorization-server 的 OAuth 2.0 元数据
    • 位于 https://auth.yourcompany.com/.well-known/openid-configuration 的 OpenID Connect 元数据
  • 每个文档都为 ChatGPT 回答了三个重要问题:将用户发送到哪里、如何交换代码以及如何识别自身。一个典型的响应如下:
{
  "issuer": "https://auth.yourcompany.com",
  "authorization_endpoint": "https://auth.yourcompany.com/oauth2/v1/authorize",
  "token_endpoint": "https://auth.yourcompany.com/oauth2/v1/token",
  "client_id_metadata_document_supported": true,
  "token_endpoint_auth_methods_supported": ["none", "private_key_jwt"],
  "registration_endpoint": "https://auth.yourcompany.com/oauth2/v1/register",
  "code_challenge_methods_supported": ["S256"],
  "scopes_supported": ["files:read", "files:write"]
}
  • 必须正确的字段
    • authorization_endpoint, token_endpoint:ChatGPT 运行 OAuth 授权码 + PKCE 流程所需的 URL。
    • client_id_metadata_document_supported:当您希望 ChatGPT 使用 CIMD 进行客户端注册时,将其设置为 true。当 CIMD 可用时,ChatGPT 会优先使用 CIMD,但连接器创建者可以在 CIMD 和 DCR 同时可用时选择 DCR。
    • token_endpoint_auth_methods_supported:包含您的授权服务器接受的令牌端点身份验证方法。这适用于 CIMD、DCR 和预定义的 OAuth 客户端。对于 CIMD,ChatGPT 支持 none(用于公共客户端令牌交换)和 private_key_jwt(用于签名的客户端断言令牌交换)。其他 OAuth 客户端通常使用 noneclient_secret_postclient_secret_basic
    • registration_endpoint:当您支持动态客户端注册 (DCR) 时包含此项,它允许 ChatGPT 为该连接器实例创建并重用专用的 client_id
    • code_challenge_methods_supported:如果您的授权服务器通告支持 PKCE,请包含 S256
    • 可选字段遵循 RFC 8414 / OpenID Discovery;请包含任何有助于您的管理员配置策略的信息。

OIDC 作用域

  • 如果您的提供商在其 .well-known/oauth-authorization-server.well-known/openid-configuration 文档的 scopes_supported 中通告了 OIDC 作用域(例如 openidemailprofile),ChatGPT 将在 OAuth 流程中默认请求这些作用域。
  • 某些身份提供商可能不会默认启用已通告的 OIDC 作用域。请检查您提供商的配置设置,并确保无论使用的是 CIMD、手动创建还是通过 DCR 创建,每个已通告的作用域都已为该 OAuth 客户端启用。

重定向 URL

ChatGPT 通过重定向到 https://chatgpt.com/connector/oauth/{callback_id} 来完成 OAuth 流程,该 URL 将显示在应用管理页面中。请将该生产环境重定向 URI 添加到您授权服务器的允许列表中,以便成功返回授权码。

  • 对于已经发布的应用,旧版的重定向 URI https://chatgpt.com/connector_platform_oauth_redirect 将继续有效。

在整个 OAuth 流程中回传 resource 参数

  • 预计 ChatGPT 会在授权请求和令牌请求中附加 resource=https%3A%2F%2Fyour-mcp.example.com。这会将令牌与上面显示的受保护资源元数据关联起来。
  • 配置您的授权服务器,将该值复制到访问令牌中(通常是 aud 声明),以便您的 MCP 服务器可以验证该令牌是为它颁发的,而不是其他任何服务。
  • 如果收到的令牌缺少预期的受众或作用域,请拒绝它,并依靠 WWW-Authenticate 质询来提示 ChatGPT 使用正确的参数重新进行授权。

支持授权码流程

  • 作为 MCP 客户端,ChatGPT 使用 S256 代码质询执行带有 PKCE 的授权码流程,从而防止拦截的授权码被攻击者重用。
  • 如果您的授权服务器发布了 code_challenge_methods_supported,请包含 S256,以便客户端可以通过元数据确认对 PKCE 的支持。

OAuth 流程

只要您按照上述说明实现了 MCP 授权规范,OAuth 流程将如下进行:

  1. ChatGPT 向您的 MCP 服务器查询受保护的资源元数据。

  1. ChatGPT 将自己标识为 OAuth 客户端。当连接器使用 CIMD 时,ChatGPT 会跳过动态客户端注册,并发送一个 CIMD 文档 URL 作为 client_id,例如 https://chatgpt.com/oauth/.../client.json(确切的 URL 特定于 MCP 服务器,因为重定向 URI 是 MCP 特有的)。当连接器使用 DCR 时,ChatGPT 会为该连接器实例调用一次授权服务器的 registration_endpoint,接收生成的 client_id,并为该实例重用此客户端。

使用 CIMD 时,没有客户端注册步骤。以下屏幕显示的是 DCR 路径:

  1. 当用户首次调用工具时,ChatGPT 客户端启动 OAuth 授权码 + PKCE 流程。用户进行身份验证并同意所请求的作用域。

  1. ChatGPT 将授权码交换为访问令牌,并将其附加到后续的 MCP 请求中(Authorization: Bearer <token>)。

  1. 您的服务器在执行工具之前验证每个请求上的令牌(颁发者、受众、过期时间、作用域)。

客户端注册

当您的授权服务器支持且连接器创建者选择时,请将 客户端 ID 元数据文档 (CIMD) 作为首选客户端注册方法。使用 CIMD,ChatGPT 使用 HTTPS 元数据文档 URL 作为其 client_id。您的授权服务器获取该文档,验证已发布的客户端元数据和重定向 URI,并将该 URL 视为 ChatGPT 稳定的客户端身份。

如果您支持 CIMD,请在授权服务器元数据中设置 client_id_metadata_document_supported: true。这允许 ChatGPT 为选择 CIMD 的连接器使用一个稳定的客户端身份,您的授权服务器可以将其用于重定向 URI 允许列表、速率限制和其他策略。

ChatGPT 为 CIMD 支持的客户端支持两种令牌端点身份验证方法:

  • none:当您的令牌端点支持基于 PKCE 的授权码交换而无需客户端身份验证时,使用此公共客户端流程。ChatGPT 不会存储每个客户端的密钥。
  • private_key_jwt:当您的令牌端点需要客户端身份验证时,使用此签名的客户端断言流程。ChatGPT 发布带有 token_endpoint_auth_method: "private_key_jwt" 和公共 JWKS URL 的 CIMD 元数据。JWKS 从元数据源的 /oauth/jwks.json 提供。ChatGPT 在服务器端使用托管的私钥和 kid 对令牌请求进行签名;您的授权服务器对照公共 JWKS 验证断言。

仍然支持 DCR。如果您包含 registration_endpoint,当连接器创建者选择 DCR 或 CIMD 不可用时,ChatGPT 可以进行动态注册。ChatGPT 为每个连接器实例运行一次 DCR,然后为该实例保留并重用注册的 OAuth 客户端。DCR 仍然可以在许多单独的连接器实例中创建许多注册的客户端,因此 CIMD 在大规模管理时通常更容易。

客户端识别

一个常见的问题是您的 MCP 服务器如何确认请求确实来自 ChatGPT。ChatGPT 在连接到 MCP 服务器时会出示由 OpenAI 托管的客户端证书,因此您可以在传输层使用 mTLS 验证客户端。您也可以将 ChatGPT 的 已发布的出站 IP 范围 加入白名单。ChatGPT 支持机器对机器的 OAuth 授权,如客户端凭据、服务帐户或 JWT 承载断言,也无法出示自定义 API 密钥或客户提供的 mTLS 证书。

CIMD 通过为您的授权服务器提供一个稳定的、由 HTTPS 托管的 ChatGPT 身份声明,进一步加强了客户端识别。当您使用 private_key_jwt 时,请对照 CIMD 元数据中发布的公共 JWKS 验证 ChatGPT 的令牌端点客户端断言。

双向 TLS (mTLS)

当建立与 MCP 服务器的 TLS 连接时,ChatGPT 现在会出示由 OpenAI 托管的客户端证书。如果您的应用程序验证客户端证书,请将其配置为信任下面的 OpenAI 证书链。

要在建立与您的 MCP 服务器的 TLS 连接时验证客户端证书:

  • 验证存在叶证书,并且它链接到 OpenAI 连接器 mTLS 中间 CA。
  • 验证叶证书对客户端身份验证有效。
  • 验证叶证书的 SAN dnsNamemtls.prod.connectors.openai.com
  • 避免固定叶证书指纹;OpenAI 可能会轮换叶证书,同时保持其在已发布的 CA 链下。

使用 mTLS 验证 ChatGPT 作为 MCP 客户端。继续使用 OAuth 2.1 对最终用户进行身份验证并授权工具访问。

选择身份提供商

大多数 OAuth 2.1 身份提供商在公开发现文档、支持 noneprivate_key_jwt 的 CIMD、在需要时支持 DCR 以及将 resource 参数回传到颁发的令牌中后,都可以满足 MCP 授权要求。请优先选择支持 CIMD 的客户端注册的提供商。

我们强烈建议您使用现有的成熟身份提供商,而不是从头开始实现身份验证。

以下是一些常用身份提供商的操作说明。

Auth0

Auth0 通过提供元数据发现、CIMD 注册、API 安全性和针对第一方和第三方工具调用的令牌交换,使 MCP 客户端能够安全地连接到 MCP 服务器。

Stytch

实现令牌验证

当 OAuth 流程完成时,ChatGPT 只是将其收到的访问令牌附加到随后的 MCP 请求中(Authorization: Bearer …)。一旦请求到达您的 MCP 服务器,您必须假设令牌不可信,并亲自执行全套资源服务器检查——签名验证、颁发者和受众匹配、过期时间、重放考虑和作用域执行。此责任由您承担,而非 ChatGPT。

在实践中,您应该:

  • 获取您的授权服务器发布的签名密钥(通常通过 JWKS),并验证令牌的签名和 iss
  • 拒绝已过期或尚未生效的令牌(exp/nbf)。
  • 确认令牌是为您服务器颁发的(audresource 声明),并包含您标记为必需的作用域。
  • 运行任何特定于应用的策略检查,然后将解析后的身份附加到请求上下文,或者返回带有 WWW-Authenticate 质询的 401 错误。

如果验证失败,请返回 401 Unauthorized 和指向您的受保护资源元数据的 WWW-Authenticate 标头。这会告诉客户端再次运行 OAuth 流程。

SDK 令牌验证原语

Python 和 TypeScript 的 MCP SDK 都包含辅助程序,因此您不必从头开始编写。

测试和发布

  • 本地测试 – 从一个颁发短期令牌的开发租户开始,以便快速迭代。
  • 内部测试 (Dogfood) – 一旦身份验证工作正常,在广泛发布之前限制受信任测试人员的访问。您可以要求针对特定工具或整个连接器进行链接。
  • 轮换 – 规划令牌吊销、刷新和作用域更改。您的服务器应将丢失或陈旧的令牌视为未通过身份验证,并返回有用的错误消息。
  • OAuth 调试 – 使用 MCP Inspector Auth 设置来逐步调试每个 OAuth 步骤,并在发布前查明流程中断的位置。

在完成身份验证后,您可以放心地向 ChatGPT 用户公开用户特定的数据和写入操作。

触发身份验证 UI

ChatGPT 仅在您的 MCP 服务器发出 OAuth 可用或必要的信号时,才会弹出其 OAuth 链接 UI。

触发工具级 OAuth 流程需要元数据(securitySchemes 和资源元数据文档)以及携带 _meta["mcp/www_authenticate"] 的运行时错误。缺一不可,ChatGPT 将不会为该工具显示链接 UI。

  1. 发布资源元数据。 MCP 服务器必须在众所周知的 URL(如 https://your-mcp.example.com/.well-known/oauth-protected-resource)上公开其 OAuth 配置。

  2. 使用 securitySchemes 描述每个工具的身份验证策略。 为每个工具声明 securitySchemes 可以告诉 ChatGPT 哪些工具需要 OAuth,哪些可以匿名运行。即使整个服务器使用相同的策略,也要坚持使用工具级声明;服务器级默认值使得将来难以单独演进各个工具。

    目前有两种方案类型可用,您可以列出多个以表示可选的身份验证:

    • noauth — 工具可以匿名调用;ChatGPT 可以立即运行它。
    • oauth2 — 工具需要 OAuth 2.0 访问令牌;包含您将请求的作用域,以便同意屏幕准确无误。

    如果您完全省略了该数组,则工具会继承服务器通告的任何默认值。同时声明 noauthoauth2 会告诉 ChatGPT,它可以从匿名调用开始,但链接操作可以解锁特权行为。无论您向客户端发出什么信号,您的服务器在每次调用时仍必须验证令牌、作用域和受众。

    示例(公开 + 可选身份验证)– TypeScript SDK

    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
    import { z } from "zod";
    
    declare const server: McpServer;
    
    server.registerTool(
      "search",
      {
        title: "Public Search",
        description: "Search public documents.",
        inputSchema: {
          q: z.string(),
        },
        outputSchema: {},
        securitySchemes: [
          { type: "noauth" },
          { type: "oauth2", scopes: ["search.read"] },
        ],
      },
      async ({ q }) => {
        return {
          content: [{ type: "text", text: `Results for ${q}` }],
          structuredContent: {},
        };
      }
    );

    示例(需要身份验证)– TypeScript SDK

    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
    import { z } from "zod";
    
    declare const server: McpServer;
    
    server.registerTool(
      "create_doc",
      {
        title: "Create Document",
        description: "Make a new doc in your account.",
        inputSchema: {
          title: z.string(),
        },
        outputSchema: {},
        securitySchemes: [{ type: "oauth2", scopes: ["docs.write"] }],
      },
      async ({ title }) => {
        return {
          content: [{ type: "text", text: `Created doc: ${title}` }],
          structuredContent: {},
        };
      }
    );
  3. 在工具处理程序内部检查令牌并发出 _meta["mcp/www_authenticate"],当您希望 ChatGPT 触发身份验证 UI 时。检查令牌并验证颁发者、受众、过期时间和作用域。如果没有有效的令牌,请返回一个包含 _meta["mcp/www_authenticate"] 的错误结果,并确保该值包含 errorerror_description 参数。一旦步骤 1 和 2 到位,这个 WWW-Authenticate 载荷就会实际触发工具级 OAuth UI。

    示例

    {
      "jsonrpc": "2.0",
      "id": 4,
      "result": {
        "content": [
          {
            "type": "text",
            "text": "Authentication required: no access token provided."
          }
        ],
        "_meta": {
          "mcp/www_authenticate": [
            "'Bearer resource_metadata=\"https://your-mcp.example.com/.well-known/oauth-protected-resource\", error=\"insufficient_scope\", error_description=\"You need to login to continue\"'"
          ]
        },
        "isError": true
      }
    }
© . This website operates independently and is not affiliated with or endorsed by OpenAI, Inc. All brand names, logos, and trademarks are the property of their respective owners.