跳到内容

认证与访问控制

LangGraph Platform 提供了一个灵活的认证和授权系统,可以与大多数认证方案集成。

核心概念

认证 vs 授权

这些术语虽然经常互换使用,但代表着不同的安全概念。

  • 认证 ("AuthN") 验证您的身份。它作为中间件运行,处理每个请求。
  • 授权 ("AuthZ") 确定您可以做什么。它根据每个资源验证用户的权限和角色。

在 LangGraph Platform 中,认证由您的 authenticate() 处理器处理,而授权由您的 on() 处理器处理。

默认安全模型

LangGraph Platform 提供不同的默认安全设置

  • LangGraph Cloud
  • 自我托管

LangGraph Cloud

  • 默认使用 LangSmith API 密钥
  • 要求在 x-api-key 请求头中提供有效的 API 密钥
  • 可以使用您的认证处理器进行自定义

自定义认证

LangGraph Cloud 的所有计划都支持自定义认证。

自我托管

  • 没有默认认证
  • 完全灵活地实现您的安全模型
  • 您控制认证和授权的所有方面

自定义认证

企业版自我托管计划支持自定义认证。自我托管轻量版计划本身不支持自定义认证。

系统架构

典型的认证设置包括三个主要组件

  1. 认证提供商 (身份提供商/IdP)

  2. 一个专门管理用户身份和凭据的服务

  3. 处理用户注册、登录、密码重置等
  4. 认证成功后颁发令牌(JWT、会话令牌等)
  5. 示例:Auth0、Supabase Auth、Okta 或您自己的认证服务器

  6. LangGraph 后端 (资源服务器)

  7. 您的 LangGraph 应用程序,包含业务逻辑和受保护资源

  8. 与认证提供商一起验证令牌
  9. 基于用户身份和权限强制执行访问控制
  10. 不直接存储用户凭据

  11. 客户端应用程序 (前端)

  12. Web 应用、移动应用或 API 客户端

  13. 收集时效性强的用户凭据并发送给认证提供商
  14. 从认证提供商接收令牌
  15. 在发送到 LangGraph 后端的请求中包含这些令牌

这些组件通常以如下方式交互

sequenceDiagram
    participant Client as Client App
    participant Auth as Auth Provider
    participant LG as LangGraph Backend

    Client->>Auth: 1. Login (username/password)
    Auth-->>Client: 2. Return token
    Client->>LG: 3. Request with token
    Note over LG: 4. Validate token (authenticate())
    LG-->>Auth:  5. Fetch user info
    Auth-->>LG: 6. Confirm validity
    Note over LG: 7. Apply access control (on())
    LG-->>Client: 8. Return resources

您的 LangGraph 中的 authenticate() 处理器处理步骤 4-6,而您的 on() 处理器实现步骤 7。

认证

LangGraph 中的认证作为中间件运行,处理每个请求。您的 authenticate() 处理器接收请求信息,应执行以下操作:

  1. 验证凭据
  2. 如果凭据有效,返回包含用户身份和用户信息的详情
  3. 如果凭据无效,抛出 HTTPException 或其他错误
import { Auth, HTTPException } from "@langchain/langgraph-sdk/auth";

// (1) Validate the credentials
const isValidKey = (key: string) => {
  return true;
};

export const auth = new Auth().authenticate(async (request: Request) => {
  const apiKey = request.headers.get("x-api-key");

  if (!apiKey || !isValidKey(apiKey)) {
    // (3) Raise an HTTPException
    throw new HTTPException(401, { message: "Invalid API key" });
  }

  // (2) Return user information containing the user's identity and user information if valid
  return {
    // required, unique user identifier
    identity: "user-123",
    // required, list of permissions
    permissions: [],
    // optional, assumed `true` by default
    is_authenticated: true,

    // You can add more custom fields if you want to implement other auth patterns
    role: "admin",
    org_id: "org-123",
  };
});

返回的用户信息可通过以下方式获取:

  • 通过 on() 回调中的 user 属性提供给您的授权处理器。
  • 在您的应用程序中通过 config["configuration"]["langgraph_auth_user"] 获取
Request 输入参数

authenticate() 处理器接受 Request 实例作为参数,但 Request 对象可能不包含请求体。

您仍然可以使用 Request 实例来提取其他字段,例如请求头、查询参数等。

授权

认证后,LangGraph 调用您的 on() 处理器来控制对特定资源(例如,线程、助手、定时任务)的访问。这些处理器可以:

  1. 通过直接修改 value["metadata"] 对象来添加在资源创建时要保存的元数据。有关每个操作可接受的值类型列表,请参阅支持的操作表
  2. 在搜索/列表或读取操作期间,通过返回一个过滤器对象来按元数据过滤资源。
  3. 如果拒绝访问,抛出 HTTP 错误。

如果您只想实现简单的用户范围访问控制,您可以对所有资源和操作使用一个 on() 处理器。如果您想根据资源和操作进行不同的控制,可以使用资源特定处理器。有关支持访问控制的资源完整列表,请参阅支持的资源部分。

import { Auth, HTTPException } from "@langchain/langgraph-sdk/auth";

export const auth = new Auth()
  .authenticate(async (request: Request) => ({
    identity: "user-123",
    permissions: [],
  }))
  .on("*", ({ value, user }) => {
    // Create filter to restrict access to just this user's resources
    const filters = { owner: user.identity };

    // If the operation supports metadata, add the user identity
    // as metadata to the resource.
    if ("metadata" in value) {
      value.metadata ??= {};
      value.metadata.owner = user.identity;
    }

    // Return filters to restrict access
    // These filters are applied to ALL operations (create, read, update, search, etc.)
    // to ensure users can only access their own resources
    return filters;
  });

资源特定处理器

您可以通过将资源和操作名称与 on() 方法链式调用,来注册特定资源和操作的处理器。当发起请求时,将调用与该资源和操作匹配的最具体的处理器。下面是如何为特定资源和操作注册处理器的示例。对于以下设置:

  1. 认证用户能够创建线程、读取线程、在线程上创建运行(runs)
  2. 只有拥有 "assistants:create" 权限的用户才能创建新的助手
  3. 所有其他端点(例如,删除助手、定时任务、存储)对所有用户禁用。

支持的处理器

有关支持的资源和操作的完整列表,请参阅下面的支持的资源部分。

import { Auth, HTTPException } from "@langchain/langgraph-sdk/auth";

export const auth = new Auth()
  .authenticate(async (request: Request) => ({
    identity: "user-123",
    permissions: ["threads:write", "threads:read"],
  }))
  .on("*", ({ event, user }) => {
    console.log(`Request for ${event} by ${user.identity}`);
    throw new HTTPException(403, { message: "Forbidden" });
  })

  // Matches the "threads" resource and all actions - create, read, update, delete, search
  // Since this is **more specific** than the generic `on("*")` handler, it will take precedence over the generic handler for all actions on the "threads" resource
  .on("threads", ({ permissions, value, user }) => {
    if (!permissions.includes("write")) {
      throw new HTTPException(403, {
        message: "User lacks the required permissions.",
      });
    }

    // Not all events do include `metadata` property in `value`.
    // So we need to add this type guard.
    if ("metadata" in value) {
      value.metadata ??= {};
      value.metadata.owner = user.identity;
    }

    return { owner: user.identity };
  })

  // Thread creation. This will match only on thread create actions.
  // Since this is **more specific** than both the generic `on("*")` handler and the `on("threads")` handler, it will take precedence for any "create" actions on the "threads" resources
  .on("threads:create", ({ value, user, permissions }) => {
    if (!permissions.includes("write")) {
      throw new HTTPException(403, {
        message: "User lacks the required permissions.",
      });
    }

    // Setting metadata on the thread being created will ensure that the resource contains an "owner" field
    // Then any time a user tries to access this thread or runs within the thread,
    // we can filter by owner
    value.metadata ??= {};
    value.metadata.owner = user.identity;

    return { owner: user.identity };
  })

  // Reading a thread. Since this is also more specific than the generic `on("*")` handler, and the `on("threads")` handler,
  .on("threads:read", ({ user }) => {
    // Since we are reading (and not creating) a thread,
    // we don't need to set metadata. We just need to
    // return a filter to ensure users can only see their own threads.
    return { owner: user.identity };
  })

  // Run creation, streaming, updates, etc.
  // This takes precedence over the generic `on("*")` handler and the `on("threads")` handler
  .on("threads:create_run", ({ value, user }) => {
    value.metadata ??= {};
    value.metadata.owner = user.identity;

    return { owner: user.identity };
  })

  // Assistant creation. This will match only on assistant create actions.
  // Since this is **more specific** than both the generic `on("*")` handler and the `on("assistants")` handler, it will take precedence for any "create" actions on the "assistants" resources
  .on("assistants:create", ({ value, user, permissions }) => {
    if (!permissions.includes("assistants:create")) {
      throw new HTTPException(403, {
        message: "User lacks the required permissions.",
      });
    }

    // Setting metadata on the assistant being created will ensure that the resource contains an "owner" field.
    // Then any time a user tries to access this assistant, we can filter by owner
    value.metadata ??= {};
    value.metadata.owner = user.identity;

    return { owner: user.identity };
  });

请注意,在上面的示例中,我们混合使用了全局处理器和资源特定处理器。由于每个请求都由最具体的处理器处理,因此创建 thread 的请求将匹配 thread:create 处理器,而不是 * 处理器。然而,更新线程的请求将由全局处理器处理,因为我们没有针对该资源和操作的更具体处理器。创建、更新、

过滤操作

授权处理器可以返回 None、布尔值或过滤器对象。

  • nullvoidtrue 表示“授权访问所有底层资源”
  • False 表示“拒绝访问所有底层资源(抛出 403 错误)”
  • 元数据过滤器对象将限制对资源的访问。支持精确匹配和运算符。
过滤器对象语法

支持以下运算符

  • 精确匹配简写:{"field": "value"}
  • 精确匹配:{"field": {"$eq": "value"}}
  • 包含:{"field": {"$contains": "value"}}

包含多个键的元数据过滤器对象将使用逻辑 AND 过滤器进行处理。例如,{"owner": org_id, "allowed_users": {"$contains": user_id}} 只会匹配元数据中“owner”为 org_id 且“allowed_users”列表包含 user_id 的资源。

常见访问模式

以下是一些典型的授权模式

单所有者资源

这种常见模式允许您将所有线程、助手、定时任务和运行限定到单个用户。这对于常见的单用户用例(如常规聊天机器人应用)非常有用。

import { Auth, HTTPException } from "@langchain/langgraph-sdk/auth";

export const auth = new Auth()
  .authenticate(async (request: Request) => ({
    identity: "user-123",
    permissions: ["threads:write", "threads:read"],
  }))
  .on("*", ({ value, user }) => {
    if ("metadata" in value) {
      value.metadata ??= {};
      value.metadata.owner = user.identity;
    }
    return { owner: user.identity };
  });

基于权限的访问

这种模式允许您基于权限控制访问。如果您希望某些角色对资源拥有更广泛或更严格的访问权限,这会很有用。

import { Auth, HTTPException } from "@langchain/langgraph-sdk/auth";

export const auth = new Auth()
  .authenticate(async (request: Request) => ({
    identity: "user-123",
    // Define permissions in auth
    permissions: ["threads:write", "threads:read"],
  }))
  .on("threads:create", ({ value, user, permissions }) => {
    if (!permissions.includes("threads:write")) {
      throw new HTTPException(403, { message: "Unauthorized" });
    }

    if ("metadata" in value) {
      value.metadata ??= {};
      value.metadata.owner = user.identity;
    }
    return { owner: user.identity };
  })
  .on("threads:read", ({ user, permissions }) => {
    if (
      !permissions.includes("threads:read") &&
      !permissions.includes("threads:write")
    ) {
      throw new HTTPException(403, { message: "Unauthorized" });
    }

    return { owner: user.identity };
  });

支持的资源

LangGraph 提供三种级别的授权处理器,从最通用到最具体:

  1. 全局处理器 (on("*")):匹配所有资源和操作
  2. 资源处理器 (例如,on("threads"), on("assistants"), on("crons")):匹配特定资源的所有操作
  3. 操作处理器 (例如,on("threads:create"), on("threads:read")):匹配特定资源上的特定操作

将使用最具体的匹配处理器。例如,对于线程创建,on("threads:create") 优先于 on("threads")。如果注册了更具体的处理器,则更通用的处理器不会针对该资源和操作被调用。

支持的操作事件

资源 事件 描述 值类型
线程 threads:create 创建线程 ThreadsCreate
threads:read 检索线程 ThreadsRead
threads:update 更新线程 ThreadsUpdate
threads:delete 删除线程 ThreadsDelete
threads:search 列出线程 ThreadsSearch
threads:create_run 创建或更新运行 RunsCreate
助手 assistants:create 创建助手 AssistantsCreate
assistants:read 检索助手 AssistantsRead
assistants:update 更新助手 AssistantsUpdate
assistants:delete 删除助手 AssistantsDelete
assistants:search 列出助手 AssistantsSearch
定时任务 crons:create 创建定时任务 CronsCreate
crons:read 检索定时任务 CronsRead
crons:update 更新定时任务 CronsUpdate
crons:delete 删除定时任务 CronsDelete
crons:search 列出定时任务 CronsSearch
关于运行

运行(Runs)的访问控制范围限定在其父线程内。这意味着权限通常继承自线程,反映了数据模型的会话性质。除创建之外的所有运行操作(读取、列出)都由线程的处理器控制。有一个特定的 threads:create_run 事件用于创建新的运行,因为它包含更多可以在处理器中查看的参数。

下一步

有关实现详情