Mastra mastra/server 认证与路由保护架构:从 checkRouteAuth 到 RBAC 权限推导全解析【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra本文基于packages/server/CLAUDE.md的设计文档,结合 Mastra 仓库中mastra/server的认证中间件、路由注册与权限推导源码,完整拆解 Mastra 服务器哪些请求需要登录、如何认证、如何授权、如何校验 RBAC 权限的四层防线。读完后,你将能够理解server.auth配置项如何作用于内置路由与自定义 API 路由,能读懂checkRouteAuth→coreAuthMiddleware→checkRoutePermission的调用链,并掌握{resource}:{action}权限推导约定在真实路由(如POST /agents/:id/generate)上的落地方式。一、三类路由,三种认证行为packages/server/CLAUDE.md开篇将 Mastra 服务器中的路由划分为三个类别,这是理解整个认证架构的前提:内置 SERVER_ROUTES(agents、workflows、memory、auth 等)——通过registerRoutes()注册在/api前缀下,每个路由在自己的处理逻辑中内联调用checkRouteAuth(route, ...)。认证相关路由(登录、注册等)使用createPublicRoute()创建,即在路由对象上设置requiresAuth: false。自定义 API 路由(用户通过server.apiRoutes[]定义)——在服务启动时被逐条记录进customRouteAuthConfigMap,默认requiresAuth true,除非路由显式声明requiresAuth: false。它们在registerCustomApiRoutes()中各自获得按路由粒度的认证中间件。非 API 路径(/、/agents、/assets/*)——Studio UI 与静态资源。这两类都不进入上述任何路由系统,而是由 Hono 的 catch-allapp.get(*)处理器返回index.html。它们故意不做保护,目的是保证 Studio 登录页本身可以加载。从源码可以印证第 2、3 类的设计意图:自定义路由的注册会先做路径冲突校验——自定义路由不得以服务器apiPrefix(默认/api)开头,否则直接抛错,见 validateCustomRoutePaths:if (route.path.startsWith(${prefix}/) || route.path prefix) { throw new Error( Custom API route ${route.path} must not start with ${prefix} — that path is reserved for built-in Mastra routes. ..., ); }这意味着内置路由与自定义路由在路径空间上被硬性隔离,各自走独立的认证通道,不会互相泄漏保护范围。二、默认认证配置:defaults.ts框架内置了一份可以被用户server.auth扩展的默认配置,位于 packages/server/src/server/auth/defaults.ts:export const defaultAuthConfig: MastraAuthConfig { protected: [/api/*], public: [/api, /api/auth/*], // Simple rule system rules: [ // Admin users can do anything { condition: user { if (typeof user object user ! null) { if (isAdmin in user) return !!user.isAdmin; if (role in user) return user.role admin; } return false; }, allow: true, }, ], };三项配置的语义:配置项默认值含义protected[/api/*]命中这些模式的路径必须经过认证public[/api, /api/auth/*]即使受保护也可匿名访问的路径(登录端点、API 根路径)rulesadmin 用户放行授权阶段的兜底规则:用户对象上isAdmin: true或role admin即获得全部访问权用户配置与默认配置是合并关系而非替换:isProtectedPath与canAccessPublicly内部都会先展开默认模式再叠加用户模式([...defaultAuthConfig.protected, ...authConfig.protected]),见 helpers.ts。三、请求认证主流程:checkRouteAuth → coreAuthMiddleware3.1 入口:checkRouteAuth 的三道前置判断每个内置路由在处理请求前都会调用适配器的checkRouteAuth(),见 server-adapter/index.ts。它按顺序执行:未配置server.auth→ 直接放行(if (!effectiveAuth) return null),即整个服务处于无认证模式;路由级豁免:route.requiresAuth false时直接返回 null——这是createPublicRoute()生效的位置,文档中称为per-route opt-out;提取 Token:Authorization: Bearer token头优先,其次是apiKey查询参数;Cookie 场景(如 SimpleAuth 写入的mastra-token)则在coreAuthMiddleware内部补齐:const authHeader context.getHeader(authorization); let token: string | null authHeader ? authHeader.replace(Bearer , ) : null; if (!token) { token context.getQuery(apiKey) || null; }Token 提取后,checkRouteAuth会把requiresAuth标志连同customRouteAuthConfig一起传入核心的coreAuthMiddleware,认证与授权逻辑全部收敛在这一处,各 Web 框架适配器(Hono/Fastify/Express 等)只是薄封装。3.2 coreAuthMiddleware:四段式判定coreAuthMiddleware 是框架无关的核心中间件,判定顺序与文档中的流程图一致:几个值得注意的源码细节,均比文档描述更精确:Playground 旁路有一个隐含前提:源码中该旁路仅在hasAuthProvider为 false 时生效——即没有配置真正的authenticateToken时才允许开发环境匿名访问;一旦配置了认证,开发请求也必须走完整认证流程,这样才能把 user/roles/permissions 注入 requestContext(见 helpers.ts 的注释与判断)。isDevPlaygroundRequest本身的判定条件是MASTRA_DEV true且(路径既不在 protected 模式内、也不是受保护的自定义路由)或请求带x-mastra-dev-playground: true头,见 isDevPlaygroundRequest。认证失败前会尝试透明会话刷新:若认证返回空用户且认证配置实现了getSessionIdFromRequest/refreshSession/getSessionHeaders(Cookie 会话类 provider),中间件会尝试刷新会话、用新的Set-Cookie重建请求并二次认证,成功则把刷新头透传给客户端;失败才回落到 401(见 helpers.ts)。认证成功后写入 requestContext:用户对象写入MASTRA_USER_KEY(同时兼容旧键user),原始 token 写入MASTRa_AUTH_TOKEN_KEY供下游(如 MCP 客户端转发)使用,若配置了mapUserToResourceId还会写入资源 ID;配置了 RBAC provider 时,getPermissions/getRoles的加载结果也写入 context,供下一阶段权限检查使用。授权方式的优先级:authorizeUser()authorize()rules[] (无显式授权时)RBAC 兜底。rules[]的求值语义是首条命中即返回,规则可按path/methods限定作用范围,condition支持异步函数,求值抛错按拒绝处理,见 checkRules。仅认证模式(auth-only):未配置任何显式授权、也未配置 RBAC 时,通过认证即视为放行——这就是文档中 authenticated full access 的含义;一旦配置了 RBAC 却没有显式授权,则改用默认 admin 规则做兜底判定,不命中即 403。四、isProtectedPath 的行为边界isProtectedPath在以下任一条件成立时返回 true(见 isProtectedPath):路径命中protected[]模式(默认/api/*);或路径以METHOD:/path为键注册在customRouteAuthConfig中且值为true。因此/、/agents、/assets/*这类路径不受保护——这是设计使然而非漏洞:这些路径背后没有敏感处理函数,只会返回 Studio UI 或静态文件。源码注释特别记录了一段演进历史:早期实现采用默认拒绝逻辑,曾错误拦截非 API 路径,导致生产环境中 Studio 登录页无法加载;现行实现刻意采用未知路径默认放行 已注册路由逐一校验的组合,并靠/api/*这个 protected 模式作为面向用户可覆盖的安全基线。模式匹配由 path-pattern.ts 提供,基于 regexparam 语法,支持精确路径、*通配、:id路径参数与:id?可选参数;protected/public还接受RegExp与[pattern, method]二元组形式,匹配逻辑见 isAnyMatch。五、权限推导约定:{resource}:{action}当服务配置了 RBAC provider,每个路由还需要通过checkRoutePermission的权限校验。权限的来源优先级由 getEffectivePermission 定义:route.requiresAuth false→ 不需要任何权限(公共路由);路由显式声明requiresPermission(字符串或字符串数组)→ 直接采用;否则从路径与方法自动推导;无法推导时为null(仅应出现在公共路由上)。5.1 推导规则资源(resource)取自路径首段,动作(action)由 HTTP 方法映射,见 permissions.ts:HTTP 方法动作GETreadPOSTwrite(命中操作型路径段时为execute,见下)PUT/PATCHwriteDELETEdelete文档中的推导示例与 permissions.test.ts 中的断言一一对应:路由推导出的权限GET /agents/:idagents:readPOST /agents/:id/generateagents:executePUT /workflows/:idworkflows:writeDELETE /memory/threads/:idmemory:deletePOST 的动作判定比文档的含操作段则为 execute更细致,源码中维护了两张名单:EXECUTE_PATTERNS(/generate、/stream、/execute、/start、/resume、/restart、/cancel、/approve、/decline、/speak、/listen、/query、/search、/observe、/time-travel、/enhance、/clone)——路径包含其中任一片段时判为execute,见 deriveAction;PUBLISH_PATTERNS(/publish、/activate、/restore)——且仅限/stored/*路径下生效,判为publish,避免无关路由因后缀巧合被误分类。资源侧也有特殊映射:/stored/agents→stored-agents、/stored/skills→stored-skills等(见STORED_RESOURCE_SEGMENTS),/.well-known→a2a。5.2 checkRoutePermission 的执行checkRoutePermission 的关键行为:未配置 RBAC 时整体跳过:requiresPermission在纯认证模式下会被静默忽略——文档流程图中的 requiresPermission on routes is silently ignored without RBAC 在此得到证实;权限为数组时是逻辑或语义:用户只需持有其中任意一项即通过(适配服务多种资源类型的路由);匹配支持精确匹配或*通配;拒绝时返回 403,消息形如Missing required permission: agents:read or agents:admin。六、自定义 API 路由的认证注册用户通过server.apiRoutes[]定义的路由,其认证状态在初始化阶段被固化为一张MapMETHOD:/path, boolean,见 initializeCustomRouteAuthConfig:private initializeCustomRouteAuthConfig(routes: ApiRoute[]): void { this.customRouteAuthConfig ?? new Map(); for (const route of routes) { const routeKey ${route.method}:${route.path}; if (!this.customRouteAuthConfig.has(routeKey)) { // 未显式声明 requiresAuth: false 的一律默认受保护 this.customRouteAuthConfig.set(routeKey, route.requiresAuth ! false); } } }这张 Map 同时服务于三处判定:isProtectedPath通过 isProtectedCustomRoute 查询该路由是否受保护(支持精确键、ALL:通配方法与:id动态路径模式匹配);开发环境旁路通过它判断某条自定义路由是否属于受保护集合;getFrameworkPublicMatcher 用它配合内置SERVER_ROUTES构建一个该路由是否为框架声明的公共路由匹配器,用于保证用户自己的认证中间件不能拦截框架已声明为 public 的路由(如 Studio 登录端点),避免自定义中间件误伤公共路由产生 401。七、小结:一条请求要闯过的四道门综合packages/server/CLAUDE.md与源码,一次针对GET /api/agents/123的受保护请求依次经过:路由级门(checkRouteAuth):没配server.auth就全放行;路由标记requiresAuth: false就放行;路径门(coreAuthMiddleware):playground 旁路 →isProtectedPath→canAccessPublicly三道筛子;认证门:authenticateToken验证 token(含透明会话刷新),失败 401,成功则把 user/permissions/roles 注入 requestContext;授权 权限门:显式authorizeUser/authorize/rules三选一,或回落到 RBAC 默认规则;最后checkRoutePermission用{resource}:{action}推导出的权限(或显式requiresPermission)做最终裁决,失败 403。核心实现与测试均可在当前仓库中直接核对:认证判定逻辑在 packages/server/src/server/auth/helpers.ts(配套 helpers.test.ts),默认配置在 packages/server/src/server/auth/defaults.ts,路由注册与权限检查在 packages/server/src/server/server-adapter/index.ts,权限推导在 packages/server/src/server/server-adapter/routes/permissions.ts(配套 permissions.test.ts)。如果你正在为 Mastra 应用接入自有认证(自定义server.auth.authenticateToken、requiresPermission或 RBAC provider),这套默认保护/api/*、公共白名单、按路由豁免、约定式权限的四层结构就是你需要对齐的完整参照系。【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考