TREK 管理员实战:MCP Access 面板全面管理 OAuth 会话与 API 令牌
【免费下载链接】TREKA self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.项目地址: https://gitcode.com/GitHub_Trending/nomad22/TREK
MCP Access 是 TREK 管理后台(Admin Panel)中用于集中管理全实例 MCP 接入凭证的核心面板:管理员可以查看任意用户的 MCP OAuth 会话与长期 API 令牌,并一键撤销、删除。本文以 wiki/Admin-MCP-Tokens.md 为骨架,结合 server/src/nest/admin/admin.controller.ts、server/src/services/adminService.ts、server/src/services/authService.ts 等源码,讲清两类凭证的差异、表格字段含义、操作背后的失效机制,以及trekoa_/trek_两种令牌前缀的来源。
MCP Access 面板:全实例 MCP 凭证总览
MCP Access面板(对应前端组件 client/src/components/Admin/AdminMcpTokensPanel.tsx)展示的是全实例范围内所有用户的活跃 MCP OAuth 会话与 API 令牌,而普通用户在个人设置中只能看到自己的令牌。作为管理员,你可以对它们执行撤销(revoke)和删除(delete)操作。
该面板有两个重要的前置条件:
- 仅当 MCP addon 启用时才可见:MCP 作为集成型 addon 存放在
addons表中(id 为mcp,默认enabled = 0,见 server/src/db/migrations.ts),需先在 Admin-Addons 面板中启用它; - 需要管理员权限:面板的读取与操作 API 都挂在 admin 命名空间下,受管理后台鉴权保护。
从源码看,面板背后是 4 个管理端接口(见 server/src/nest/admin/admin.controller.ts):
| HTTP 接口 | 方法 | 作用 |
|---|---|---|
/admin/mcp-tokens | GET | 列出所有用户的 MCP API 令牌 |
/admin/mcp-tokens/:id | DELETE | 删除指定 MCP API 令牌 |
/admin/oauth-sessions | GET | 列出所有活跃的 OAuth 会话 |
/admin/oauth-sessions/:id | DELETE | 撤销指定 OAuth 会话 |
前端通过 client/src/api/client.ts 中的adminApi.mcpTokens()、adminApi.deleteMcpToken()、adminApi.oauthSessions()、adminApi.revokeOAuthSession()调用这些接口。
面板下方分为两个区块:OAuth Sessions与API Tokens,二者代表 TREK 支持的两类 MCP 接入凭证。
OAuth Sessions:推荐的标准授权方式
OAuth 会话产生于用户通过OAuth 2.1 流程授权某个 MCP 客户端接入 TREK 之时,这是官方推荐、也是唯一支持细粒度权限的接入方式。
授权流程速览
从 server/src/mcp/oauthProvider.ts 的实现可以看到完整链路:
- MCP 客户端通过动态客户端注册(DCR,RFC 7591)注册自身,注册时校验
redirect_uris:仅允许 HTTPS、loopback HTTP(localhost / 127.0.0.1 / ::1)或带点号的自定义私有 scheme,并拦截javascript:、data:等危险 scheme(见 oauthProvider.ts); - 用户被重定向到 TREK 的
/oauth/consent授权同意页,携带client_id、redirect_uri、scope、code_challenge(S256 PKCE)等参数; - 用户批准后,客户端用授权码 + code verifier 换取访问令牌与刷新令牌;TREK 在换取令牌时写入审计日志
oauth.token.issue(见 oauthProvider.ts); - 后续每次 MCP 请求携带访问令牌,由
verifyAccessToken解析出对应用户、客户端与授权 scope 集合(见 oauthProvider.ts)。
表格列说明
OAuth Sessions 表格各列含义如下(与原文档一致):
| 列 | 说明 |
|---|---|
| Client name | 已注册 OAuth 客户端的名称;其下方以徽章(badge)形式展示已授予的 scope,最多显示 6 个,超出部分显示 "+N more",点击可展开/收起(前端常量SCOPES_PREVIEW = 6,见 AdminMcpTokensPanel.tsx) |
| Owner | 授权该会话的用户名 |
| Created | 会话建立日期 |
| (操作) | 垃圾桶图标按钮,点击后弹出确认框,用于撤销会话 |
撤销会话及其底层联动
撤销操作流程:点击行内垃圾桶图标 → 确认弹窗 → 调用DELETE /admin/oauth-sessions/:id。服务端 adminService.ts 的处理是:
- 将
oauth_tokens表中该记录的revoked_at置为当前时间; - 调用
revokeUserSessionsForClient(user_id, client_id),只关闭该用户与该客户端的 MCP 会话,不影响该用户其他客户端的会话; - 写入审计日志,action 为
admin.oauth_session.revoke(见 admin.controller.ts)。
会话被撤销后立即失效,该 MCP 客户端必须重新走一遍授权流程才能继续发请求。
前缀trekoa_与令牌存储安全
OAuth 访问令牌使用前缀trekoa_,刷新令牌使用trekrf_前缀。生成逻辑在 server/src/services/oauthService.ts:trekoa_+ 32 字节加密随机数(randomBytes(32))的 hex 编码。
安全存储方面,TREK 不落库明文令牌,而是只存 SHA-256 哈希(hashToken),并在校验时使用常量时间比较timingSafeEqualHex防时序攻击(见 oauthService.ts)。oauth_tokens表对access_token_hash与refresh_token_hash均建有唯一索引(见 migrations.ts)。
API Tokens:用户的长期静态令牌
API 令牌(MCP tokens)是用户在个人设置中自行创建的长期令牌,适合脚本化或 CLI 类客户端直接以 Bearer 方式携带,不需要走浏览器授权流程。它们以trek_前缀标识。
表格列说明
| 列 | 说明 |
|---|---|
| Token name | 用户为令牌起的标签,下方以等宽字体显示其截断前缀(如trek_ab12c...) |
| Owner | 创建该令牌的用户名 |
| Created | 令牌创建日期 |
| Last used | 最近一次使用该令牌发起 API 调用的日期;从未使用过则显示 "Never" |
| (操作) | 垃圾桶图标按钮,点击后弹出确认框,用于删除令牌 |
令牌的创建与限制(源码级)
用户在个人设置(Integrations 页)创建令牌时,服务端 authService.ts 施加以下规则:
- 命名必填且不超过 100 字符;
- 每用户最多 10 个令牌,超出返回 400 错误;
- 原始令牌为
trek_+ 24 字节加密随机的 hex 编码; - 数据库仅保存 SHA-256 哈希与前 13 个字符的前缀(
rawToken.slice(0, 13)),用于界面展示与识别; - 原始令牌仅在创建响应中返回一次(
raw_token字段),之后无法再查看。
表结构与唯一索引见 migrations.ts:mcp_tokens表含user_id(级联删除)、name、token_hash、token_prefix、created_at、last_used_at。
删除令牌
删除操作:点击垃圾桶图标 → 确认 → 调用DELETE /admin/mcp-tokens/:id。服务端 adminService.ts 的处理是:
- 校验令牌存在,不存在返回 404;
- 从
mcp_tokens表物理删除该记录; - 调用
revokeUserSessions(token.user_id)终止该用户的全部活跃 MCP 会话(注意:这里比 OAuth 撤销更彻底,是所有会话,而不限于某个客户端)。
令牌一旦删除立即失效。如果用户仍需访问,必须回到个人设置中重新创建一个新令牌。
令牌验证与 Last used 更新
每当一个trek_令牌被用于 MCP 请求,服务端通过 verifyMcpToken 完成校验:对传入令牌做 SHA-256 哈希后查mcp_tokens表并 JOIN 用户表,命中即同步更新last_used_at,失败返回 null。这正是 "Last used" 列的数据来源。
撤销背后的会话联动机制
MCP 会话(即活跃的流式 HTTP 连接)与令牌一一对应,因此令牌撤销必须同步清理内存中的会话。这一联动由 server/src/mcp/sessionManager.ts 承担:
McpSession结构包含userId、scopes、clientId、isStaticToken、lastActivity等字段,其中scopes为 null 表示静态trek_令牌(或 JWT),即全权限访问;为字符串数组则表示 OAuth 2.1 授权的作用域(见 sessionManager.ts);revokeUserSessions(userId):遍历并关闭该用户的所有MCP 会话——删除trek_令牌时调用;revokeUserSessionsForClient(userId, clientId):只关闭特定用户 × 特定 OAuth 客户端组合的会话——撤销 OAuth 会话时调用,避免误伤该用户其他客户端;evictOldestSessionForUser:按lastActivity淘汰最久未使用的会话,保证单用户会话数有上限时不会因客户端丢失Mcp-Session-Id而彻底锁死集成(见 sessionManager.ts)。
同时值得注意的是,静态trek_令牌在 scopes.ts 中被明确定义为null scopes = static trek_ token = full access,即拥有完整权限;而 OAuth 会话的权限则受用户在同意页授予的 scope 约束。这也是文档推荐优先使用 OAuth 方式接入的原因——它支持最小权限授权,而静态令牌是"一刀切"的全权限凭证。
相关页面
- MCP-Overview:MCP 整体架构与接入概念
- MCP-Setup:客户端接入配置(OAuth / 静态令牌两种方式)
- Admin-Panel-Overview:管理后台各面板总览
- Admin-Addons:启用 MCP addon 的入口(面板可见前提)
【免费下载链接】TREKA self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.项目地址: https://gitcode.com/GitHub_Trending/nomad22/TREK
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考