跳到内容

认证与访问控制

LangGraph 平台提供灵活的认证和授权系统,可与大多数认证方案集成。

核心概念

认证与授权

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

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

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

默认安全模型

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

  • LangGraph 云
  • 自托管

LangGraph 云

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

自定义认证

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

自托管

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

自定义认证

企业版自托管计划支持自定义认证。自托管精简版计划本身不支持自定义认证。

系统架构

典型的认证设置涉及三个主要组件

  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. 认证用户能够创建线程、读取线程、在线程上创建运行
  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
关于运行

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

下一步

有关实现细节