认证与访问控制¶
LangGraph Platform 提供了一个灵活的认证和授权系统,可以与大多数认证方案集成。
核心概念¶
认证 vs 授权¶
这些术语虽然经常互换使用,但代表着不同的安全概念。
在 LangGraph Platform 中,认证由您的 authenticate()
处理器处理,而授权由您的 on()
处理器处理。
默认安全模型¶
LangGraph Platform 提供不同的默认安全设置
- LangGraph Cloud
- 自我托管
LangGraph Cloud¶
- 默认使用 LangSmith API 密钥
- 要求在
x-api-key
请求头中提供有效的 API 密钥 - 可以使用您的认证处理器进行自定义
自定义认证
LangGraph Cloud 的所有计划都支持自定义认证。
自我托管¶
- 没有默认认证
- 完全灵活地实现您的安全模型
- 您控制认证和授权的所有方面
自定义认证
企业版自我托管计划支持自定义认证。自我托管轻量版计划本身不支持自定义认证。
系统架构¶
典型的认证设置包括三个主要组件
-
认证提供商 (身份提供商/IdP)
-
一个专门管理用户身份和凭据的服务
- 处理用户注册、登录、密码重置等
- 认证成功后颁发令牌(JWT、会话令牌等)
-
示例:Auth0、Supabase Auth、Okta 或您自己的认证服务器
-
LangGraph 后端 (资源服务器)
-
您的 LangGraph 应用程序,包含业务逻辑和受保护资源
- 与认证提供商一起验证令牌
- 基于用户身份和权限强制执行访问控制
-
不直接存储用户凭据
-
客户端应用程序 (前端)
-
Web 应用、移动应用或 API 客户端
- 收集时效性强的用户凭据并发送给认证提供商
- 从认证提供商接收令牌
- 在发送到 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()
处理器接收请求信息,应执行以下操作:
- 验证凭据
- 如果凭据有效,返回包含用户身份和用户信息的详情
- 如果凭据无效,抛出
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()
处理器来控制对特定资源(例如,线程、助手、定时任务)的访问。这些处理器可以:
- 通过直接修改
value["metadata"]
对象来添加在资源创建时要保存的元数据。有关每个操作可接受的值类型列表,请参阅支持的操作表。 - 在搜索/列表或读取操作期间,通过返回一个过滤器对象来按元数据过滤资源。
- 如果拒绝访问,抛出 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()
方法链式调用,来注册特定资源和操作的处理器。当发起请求时,将调用与该资源和操作匹配的最具体的处理器。下面是如何为特定资源和操作注册处理器的示例。对于以下设置:
- 认证用户能够创建线程、读取线程、在线程上创建运行(runs)
- 只有拥有 "assistants:create" 权限的用户才能创建新的助手
- 所有其他端点(例如,删除助手、定时任务、存储)对所有用户禁用。
支持的处理器
有关支持的资源和操作的完整列表,请参阅下面的支持的资源部分。
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
、布尔值或过滤器对象。
null
、void
和true
表示“授权访问所有底层资源”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 提供三种级别的授权处理器,从最通用到最具体:
- 全局处理器 (
on("*")
):匹配所有资源和操作 - 资源处理器 (例如,
on("threads")
,on("assistants")
,on("crons")
):匹配特定资源的所有操作 - 操作处理器 (例如,
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
事件用于创建新的运行,因为它包含更多可以在处理器中查看的参数。
下一步¶
有关实现详情
- 请参阅关于实现自定义认证处理器的操作指南