认证与身份
了解如何保护应用中的 API 端点、Studio 和 agent 调用。以编程方式限制访问至关重要,因为 LLM 可能很快绕过你添加到系统提示词中的表面检查。
认证验证谁在调用你的 Mastra 服务器。身份是与该请求关联的、经过验证的用户或服务账号。Mastra 在运行时代码中传递该身份,使每个组件都能做出可信的访问决策。
你可以用四个阶段来设计这条链路:
| 阶段 | 问题 | Mastra 机制 |
|---|---|---|
| 认证 | 谁在调用? | server.auth |
| 身份传递 | 经过验证的身份如何到达运行时代码? | RequestContext |
| 授权 | 该调用方可以访问或执行什么? | 应用检查、基于角色的访问控制(RBAC)或细粒度授权(FGA) |
| 出站认证 | Mastra 应使用哪个凭据访问另一个服务? | 可信工具代码或 Model Context Protocol(MCP)连接配置 |
设计身份流程设计身份流程的直接链接
请将身份决策保持在应用的可信一侧。调用方可以提供凭据,但只有认证提供方才能决定该凭据代表哪个用户。运行时组件使用服务器拥有的上下文来授权数据访问或选择出站凭据。
该流程建立了四个边界:
- 入站数据: 在认证成功之前,将所有请求数据视为不可信。
- 身份上下文: 让服务器拥有
RequestContext中的保留身份值。 - 工具输入: 防止模型控制的输入选择所有权作用域。
- 外部凭据: 将凭据保留在可信运行时代码中,置于模型输入或工具输出之外。
建立可信身份建立可信身份的直接链接
使用认证提供方或 authenticateToken 函数配置 server.auth。配置认证后,内置路由和自定义路由都要求有效用户,除非路由明确选择退出。
请从验证器中选择稳定的身份,并将其映射到运行时所有权边界。
import { Mastra } from '@mastra/core'
import { verifyAccessToken } from './lib/auth'
type AuthUser = {
id: string
workspaceId: string
}
export const mastra = new Mastra({
server: {
auth: {
authenticateToken: async token => verifyAccessToken(token),
mapUserToResourceId: (user: AuthUser) => `${user.workspaceId}:${user.id}`,
},
},
})
请使用经过验证的提供方提供的规范标识符,因为显示名称和其他可变资料字段不适合作为所有权键。添加足够的作用域,以防跨租户和身份系统发生冲突。
如果某个自定义 API 路由必须公开,请设置 requiresAuth: false。公开 webhook 在信任请求体之前仍必须验证提供方签名。
未配置 server.auth 时,Mastra 路由是公开的。生产部署必须为所有不应匿名的服务器面配置认证。如果你设置了自定义 server.apiPrefix,请更新认证路径配置以使用该前缀。
选择资源边界选择资源边界的直接链接
Mastra 验证调用方之后,需要决定谁应拥有其 memory 和 thread。资源 ID 是该所有者的稳定标识符,Mastra 用它来限定状态作用域。凭据用于验证身份,而授权规则用于授予权限。
例如,假设两个用户都请求 ID 为 support 的 thread。如果每个用户的 ID 就是其资源 ID,Mastra 会保持这些 thread 相互隔离,因为它们拥有不同的所有者。如果两个用户都映射到同一个组织 ID,他们就会共享该组织的 memory 和 thread。
使用 mapUserToResourceId 从已验证用户推导该值。认证成功后,Mastra 会:
- 使用已认证用户调用
mapUserToResourceId。 - 将结果作为服务器拥有的值存入
RequestContext。 - 使用该值限定 memory 和 thread 操作的作用域。
- 忽略客户端提供的、与之冲突的资源 ID。
如果配置了 server.auth 但未配置 mapUserToResourceId,就不存在由服务器推导的资源 ID。此时内置路由会使用请求本身的资源 ID(例如请求体中的 memory.resource),因此任何已认证调用方都可以读写任意资源 ID 下的 thread。在这种状态下,Mastra 会在启动时记录一条警告。当部署中有多个用户共享时,请始终配置 mapUserToResourceId。
请选择与人们共享状态方式相匹配的最窄边界:
| 期望边界 | 映射示例 |
|---|---|
| 每个用户的私有 memory | user.id |
| 组织共享 memory | user.organizationId |
| 工作区和项目内的用户 memory | ${workspaceId}:${projectId}:${userId} |
Mastra 会自动将该边界应用于 memory 和 thread 操作。应用代码在为数据库记录或外部服务授权时,可以继续使用 RequestContext 中的已认证用户。
在数据边界强制执行身份在数据边界强制执行身份的直接链接
授权应放在可信代码中,尽可能靠近受保护的操作。可信运行时代码可以从 RequestContext 中的已认证用户推导所有权,而不是从模型生成的工具输入中推导:
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
import { orders } from '../lib/orders'
type AuthenticatedUser = {
id: string
}
export const listOrders = createTool({
id: 'list-orders',
description: 'List orders owned by the authenticated user',
inputSchema: z.object({ status: z.enum(['open', 'shipped']).optional() }),
execute: async ({ status }, { requestContext }) => {
const user = requestContext.get('user') as AuthenticatedUser | undefined
if (!user?.id) {
throw new Error('Authenticated user is missing')
}
return orders.list({ ownerId: user.id, status })
},
})
模型可以选择 status,但不能选择 ownerId。
请对自定义路由和 workflow 步骤使用同样的规则,从已认证上下文推导所有权,然后在返回数据之前约束数据库查询或服务调用。
在运行时代码中传递身份在运行时代码中传递身份的直接链接
认证中间件在处理程序运行之前将可信身份写入 RequestContext。随后 Mastra 会将同一上下文传递给:
- Agent 选项: 解析请求特定的 agent 配置。
- 工具执行: 在
execute回调中读取身份。 - Workflow 步骤: 在步骤的
execute回调中读取身份。 - 服务器处理程序: 在中间件和自定义 API 路由中读取身份。
使用应用键调用 requestContext.get()。RequestContext 指南记录了可用的组件 API、中间件模式以及高级集成使用的保留运行时键。
RequestContext 不是环境全局状态。在 Mastra 服务器请求路径之外启动 agent 或 workflow 时,请显式传递它。
身份可以用于个性化指令和选择可用工具,但这些选择不是授权检查。请在触及受保护资源的组件或 FGA 策略中强制执行访问控制。
从可信身份选择出站凭据从可信身份选择出站凭据的直接链接
入站认证令牌与下游服务凭据解决不同的问题:
- 令牌转发: 外部服务信任出示给 Mastra 的同一个 bearer token。
- 凭据选择: 已认证用户的租户选择一个由应用单独存储的凭据。
仅当接收服务本就应当信任该令牌时,才使用令牌转发。这适用于向接受调用方自身令牌的服务发起出站调用。它不允许 MCP 服务器将其收到的 MCP 令牌转发给另一个 API。对于多租户服务,应改为在可信工具代码中解析租户作用域的凭据:
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
import { billing, tenantCredentials } from '../lib/billing'
type AuthenticatedUser = {
organizationId: string
}
export const listInvoices = createTool({
id: 'list-invoices',
description: 'List invoices for the authenticated tenant',
inputSchema: z.object({ limit: z.number().int().min(1).max(100).default(20) }),
execute: async ({ limit }, { requestContext }) => {
const user = requestContext.get('user') as AuthenticatedUser | undefined
if (!user?.organizationId) {
throw new Error('Authenticated organization is missing')
}
const credential = await tenantCredentials.getBillingToken(user.organizationId)
return billing.listInvoices({ limit, token: credential.token })
},
})
请将凭据提供方配置为对未知资源失败关闭(fail closed),并返回最小权限的凭据。让密钥不出现在其错误信息中。模型只提供 limit 等业务输入。由可信代码选择租户和凭据。
当你的应用连接到外部 MCP 服务器时,也适用同样的模式。使用已认证用户的租户选择一个已存储的凭据。当该 MCP 服务器接受调用方自己的令牌时,从 MASTRA_AUTH_TOKEN_KEY 读取面向它的令牌并发送该令牌,而不是发送已存储的凭据。只向 agent 暴露最终生成的工具。这是出站凭据:MCP 服务器不得将其收到的 MCP 令牌转发给另一个 API。客户端配置请参阅 MCP 连接,受众边界请参阅安全访问上游服务。
将身份转发给 MCP 工具将身份转发给 MCP 工具的直接链接
关于 MCP 凭据、委托访问和工具权限之间的区别,请参阅使用 MCP 进行认证与授权。
默认情况下,当 MCPServer 运行在 server.auth 之后时,Mastra 会将已认证调用方放入 MCP 的 authInfo 对象。服务器工具可以直接使用该身份,而无需再次解析请求:
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'
import { customers } from '../lib/customers'
export const getCustomerRecord = createTool({
id: 'get-customer-record',
description: 'Get the authenticated customer record',
inputSchema: z.object({}),
execute: async (_input, { mcp }) => {
const user = mcp?.extra.authInfo?.extra?.user as { id?: string } | undefined
if (!user?.id) throw new Error('Authenticated MCP caller is missing')
return customers.getByUserId(user.id)
},
})
此示例使用默认桥接。Mastra 首先保留中间件设置的任何 req.auth 值。否则,在已配置时由 server.mcpOptions.setRequestAuth 定义 authInfo 的结构。无论哪个来源提供 req.auth,都必须提供你的 MCP 工具所读取的字段。
当 MCP 传输身份必须成为细粒度授权所消费的用户时,MCPServer.mapAuthInfoToUser 执行反向映射。请将映射保留在传输边界,而不是接受用户 ID 作为工具参数。
将密钥置于模型边界之外将密钥置于模型边界之外的直接链接
RequestContext 和工具执行运行在可信应用代码中。模型不会自动接收它们的内容。
| 信息 | 可信运行时代码 | 模型 |
|---|---|---|
RequestContext 中的已认证用户 | 可用 | 仅当应用代码将其加入指令或结果时可用 |
MASTRA_AUTH_TOKEN_KEY 中的原始令牌 | 可用 | 使其不出现在提示词和工具结果中 |
| 租户 API 密钥和服务凭据 | 对解析它们的代码可用 | 使其处于模型可见数据之外 |
| 工具参数 | 视为不可信输入 | 通常由模型选择 |
| 工具结果 | 可用 | 通常会返回给模型 |
只返回模型所需的数据。在返回工具结果之前,移除凭据和敏感的上游字段。
当请求上下文附加到追踪 span 或保存在评分记录上时,Mastra 的保留键(以 mastra__ 或 __mastra_ 开头,包括 MASTRA_AUTH_TOKEN_KEY)会被省略。应用日志和自定义遥测必须对其他凭据和敏感身份字段应用同样的原则。
在认证之后添加授权在认证之后添加授权的直接链接
请使用与资源相匹配的授权层:
| 需求 | 使用 |
|---|---|
| admin、member、viewer 等角色的宽泛能力 | server.rbac |
| 依赖于用户-资源关系的权限 | server.fga |
| 应用数据库行的所有权 | 作用域查询或应用授权检查 |
身份缺失或无效会产生 401。身份已知但无权限会产生 403。FGA 指南涵盖详细配置。
观看 FGA 生产环境演练,了解 FGA 在已部署应用中的工作方式。
生产前验证生产前验证的直接链接
- 拒绝匿名请求: 确认每个受保护的服务器面在没有凭据时返回
401。 - 拒绝无效凭据: 确认认证失败不会填充可信的
RequestContext值。 - 验证公开 webhook: 在信任请求体之前检查提供方签名。
- 保护 thread 所有权: 确认一个用户无法访问另一个用户的 thread。
- 忽略所有权输入: 防止工具输入替换服务器拥有的身份或作用域。
- 拒绝禁止的访问: 确认执行层对已知但无权限的用户返回
403。 - 安全选择凭据: 从可信身份而不是模型控制的输入解析下游凭据。
- 保护密钥隐私: 确认密钥处于模型可见数据和可观测性数据之外。
- 显式传递上下文: 在直接测试 agent 和 workflow 时提供可信的
RequestContext值。
在 Studio 中进行手动测试时,请使用请求上下文预设。在本地开发之外请使用生产认证。