wikijs-mcp-google-auth
wikijs-mcp-google-auth
在现有 Wiki.js 2.5.x 之上包一层 MCP:企业用户用 Google Workspace 登录,然后通过 LLM(claude.ai、Claude Desktop 或任意 MCP 客户端)操作 wiki —— 严格遵守该用户自己的 Wiki.js 权限范围。
核心原则:授权判断以 Wiki.js 为唯一事实来源。 MCP 服务器本身没有任何用户、组或权限,也没有全局 API 密钥。每个操作都以该用户自己的原生 Wiki.js JWT 执行,是允许还是拒绝(Groups / Permissions / Page Rules)完全由 Wiki.js 决定。
Google Workspace ──OAuth/OIDC──▶ MCP Server ──signed assertion──▶ Wiki.js
│ auth module "mcpdelegation"
│ → refreshToken() → native JWT
│
MCP client (claude.ai / Desktop) ◀──┴── tools: search / get / list /
create / update / delete / whoami
(all via GraphQL with the user's JWT)组件
目录 | 说明 |
| 为 Wiki.js 2.5.x 定制的认证模块——验证 MCP 服务器签发的 RS256 断言,并返回原生 Wiki.js JWT(详情) |
| 远程 MCP 服务器(Streamable HTTP):面向 MCP 客户端的 OAuth 2.1 授权服务器,底层基于 Google OIDC + 令牌代理 + 工具 |
| 仅测试:浏览器 UI 端到端(Playwright)和一个独立的自研假 Google IdP 模拟器 |
| 隔离的测试环境(Wiki.js 2.5.303 + Postgres + ACL 种子数据)——仅用于开发/CI |
| 完整 UI 端到端测试栈(假 IdP + Wiki.js + MCP + Playwright)——仅测试用 |
| 生产部署:只包含 MCP 服务器,指向你现有的 Wiki.js |
| 测试台种子数据入口(底层库是 |
Related MCP server: wikijs-mcp
工作原理
MCP 客户端连接到
https://mcp.company.com/mcp,并执行 OAuth 2.1(动态客户端注册 + PKCE)。Google 不支持 DCR,因此 MCP 服务器本身充当客户端侧的授权服务器,而 Google 仅用于认证“人”。Google 的令牌永远不会离开服务器;客户端收到的是 MCP 服务器自己的不透明令牌。Google 登录完成后,用户会看到一个同意屏幕(consent screen),其中标明应用程序名称及其重定向 URI——这是针对“混淆代理”(confused deputy)问题的防御:第三方注册的客户端无法在用户不知情的情况下获取用户令牌;该批准按“用户 × 客户端”分别记忆。验证 Google 的
id_token(验签、iss、aud、email_verified,以及hd= 你的 Workspace 域名)。MCP 服务器的令牌代理(token broker)把 Google 身份交换成原生 Wiki.js JWT:它对一段短时有效的 RS256 断言(TTL 60 秒、
jti唯一)签名,然后以mcpdelegation策略调用标准 GraphQL mutationauthentication.login。Wiki.js 模块验签断言、按邮箱解析用户,并通过标准refreshToken()流程返回 JWT。该 JWT 会被缓存,并在过期前续签。每次工具调用都会携带
Authorization: Bearer <用户的 JWT>访问 Wiki.js GraphQL。无权限的页面无法读取或修改,不会出现在搜索结果或列表中——已由端到端测试验证(两个分属不同组的用户构成的 allow / forbidden 矩阵)。
工具
工具 | 说明 |
| 当前用户的身份 + 其在 Wiki.js 中的组与权限(访问诊断) |
| 全文搜索;结果按当前用户权限过滤 |
| 按 id 或路径获取页面(元数据 + 完整 markdown) |
| 列出当前用户可见的页面(支持按路径前缀过滤) |
| 创建页面(markdown) |
| 更新页面:读-合并-写,未指定的字段保持不变 |
| 删除页面(破坏性操作;Wiki.js 会强制检查 |
集成到你的 Wiki.js:分步指南
你需要:Wiki.js 2.5.x(已在 2.5.303 上验证)、能访问其文件系统或 Docker 配置;一台具备公网 HTTPS 端点的 MCP 服务器主机;以及你的 Workspace 的 Google Cloud Console 管理员权限。
第 1 步:在 Wiki.js 中安装认证模块
Docker: 给 wiki 服务添加一个 volume,然后重启容器:
services:
wiki:
image: ghcr.io/requarks/wiki:2.5.303
volumes:
- /opt/wikijs-mcp/wikijs-auth-module:/wiki/server/modules/authentication/mcpdelegation:ro(本仓库 packages/wikijs-auth-module/ 的内容需要放进 /opt/wikijs-mcp/wikijs-auth-module;注意:目标目录名必须恰好是 mcpdelegation)
裸机部署: 把 packages/wikijs-auth-module/ 复制到 <wiki>/server/modules/authentication/mcpdelegation/,然后重启 Wiki.js。
配置完成(第 3 步)后,Wiki.js 日志中会出现:
Authentication Strategy MCP Delegation: [ OK ]。
第 2 步:生成断言密钥
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out mcp-assertion-key.pem
openssl pkey -in mcp-assertion-key.pem -pubout -out mcp-assertion-key.pub.pem私钥(mcp-assertion-key.pem)只能保留在 MCP 服务器主机上;公钥则在下一步配置到 Wiki.js 中。
第 3 步:在 Wiki.js 管理后台配置该策略
管理后台 → Auth → 添加策略 → MCP Delegation:
断言公钥(PEM) —— 填写
mcp-assertion-key.pub.pem中的全部内容;期望的 Audience / Issuer —— 保持默认值(
urn:wikijs:mcp-delegation/urn:wikijs-mcp-google-auth);User Lookup Provider Priority —— 按邮箱查找用户时各个 Provider 的顺序。如果你的同事是通过 Google/OIDC 登录 wiki 的,把它放到最前面(也接受模块键名:
google、oidc、local);(可选)自助注册 + 域名白名单 + 自动加组 —— 这样新 Workspace 用户在第一次通过 MCP 发起请求时会被自动创建;
保存。
列表中会显示该策略实例的 key(也就是 MCP 服务器要用到的 WIKIJS_STRATEGY_KEY;如果你是通过界面手动创建的,Wiki.js 会生成一个 uuid —— 复制它即可)。
已开启 TFA 的账号无法通过委托方式使用 —— MCP 服务器会返回一个明确的错误。
第 4 步:创建 Google OAuth 客户端
Google Cloud Console → APIs & Services → Credentials → 创建凭据 → OAuth 客户端 ID:
应用类型:Web 应用;
授权重定向 URI:
https://mcp.company.com/oauth/google/callback(即你的PUBLIC_URL再拼接/oauth/google/callback);OAuth 同意屏幕:类型选择 Internal(仅限你的 Workspace 内部)。
保存得到的 Client ID 和 Client Secret。
第 5 步:部署 MCP 服务器
cd deploy
cp .env.example .env # fill in the values
mkdir -p keys && cp /path/to/mcp-assertion-key.pem keys/
chmod 644 keys/mcp-assertion-key.pem # the container runs as non-root node (uid 1000)
docker compose -f docker-compose.prod.yml up -d容器以非 root 的
node用户运行——挂载进去的密钥文件必须对它可读(chmod 644);私钥本身的保护仍由宿主机keys/目录的权限来保证。
.env 变量:
变量 | 值 |
| 带标签的镜像(当版本更新合并到 |
| MCP 服务器的公网 HTTPS 地址 |
| 你的 Wiki.js 地址(建议使用内网地址) |
| 第 3 步得到的策略实例密钥(如果你把它命名为 |
| 来自第 4 步 |
| 你的 Workspace 域名,例如 |
在 8000 端口前放一个 TLS 反向代理。最简 nginx 配置:
server {
listen 443 ssl http2;
server_name mcp.company.com;
# ssl_certificate ...; ssl_certificate_key ...;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $host;
proxy_buffering off; # streamable HTTP
}
}验证:运行 curl https://mcp.company.com/healthz 应返回 {"ok":true};再运行 curl https://mcp.company.com/.well-known/oauth-authorization-server 应返回 OAuth 元数据。
第 6 步:接入客户端
claude.ai(Team / Enterprise): Settings → Connectors → Add custom connector → URL 填 https://mcp.company.com/mcp。首次使用时客户端会自动执行 OAuth:客户端注册 → Google 登录 → 完成。
Claude Desktop: Settings → Connectors → 添加自定义连接器并填同一个 URL(旧版本也可以用 mcp-remote)。
MCP Inspector(诊断工具): 运行 npx @modelcontextprotocol/inspector → Transport 选 Streamable HTTP → URL 填 https://mcp.company.com/mcp → 点 Open Auth,完整走一遍流程。
第 7 步:验证
在 LLM 对话中:
“我在 wiki 里能做什么?” →
whoami工具应显示你的邮箱、所属组以及 Wiki.js 中的权限。让它查找/打开你有权限的页面 → 正常。
让它打开一个你无权限的页面 → 会得到明确的拒绝(“Wiki.js denied this operation…”);并且该页面也不会出现在搜索/列表结果中。
本地开发
npm ci
npm run stand:up # Wiki.js 2.5.303 + Postgres (docker)
npm run stand:seed # finalize + groups/users/pages + strategy + dev keys
npm test # unit tests (auth module + OAuth provider)
npm run build && npm run e2e # in-process e2e: delegation, OAuth, tools — against a live stand
npm run stand:down测试台账号:admin@example.com/admin1234!、john@example.com(Engineering 组,无 /management/* 权限)、kate@example.com(Management 组)。每个 PR 都会运行快速检查(CI:lint + 单元测试 + 构建);重量级的 docker e2e(e2e)和浏览器 ui-e2e 只在推送到 dev/main 时(即合并前)运行,这样不会拖慢 PR 的迭代速度。
按角色运行浏览器 UI 端到端(Playwright)
另一套 docker compose 栈 deploy/docker-compose.e2e.yml 会拉起一个假 Google IdP 模拟器(packages/e2e-ui/idp/ —— 一个带角色选择器的登录页,而不是真实 Google IdP)、Wiki.js、MCP 服务器以及一个Playwright 运行器,用来在不同角色(John/Kate/域外用户)下完整驱动浏览器的 OAuth + 授权流程。该模拟器和 Playwright 只会在 e2e 这套栈中启动——它们永远不会被打进 prod/dev 镜像。
C=deploy/docker-compose.e2e.yml
docker compose -f $C build mcp
docker compose -f $C up -d db wiki idp # no --wait on wiki: the seed script is the readiness gate
docker compose -f $C run --rm seed
docker compose -f $C up -d --wait mcp
docker compose -f $C run --rm playwright # exit code = test result
docker compose -f $C down -v它会检查:以某个角色登录 → 同意屏幕显示客户端名称 → 批准 → 返回 whoami 及该角色权限范围内的页面(John 看不到 management/*,Kate 可以);拒绝 → access_denied;域外账号在进入同意步骤之前就会被拒绝。一个独立的 CI 工作流(ui-e2e)会在推送到 dev/main 时执行这些检查。
手动针对测试实例运行 MCP 服务器:
PUBLIC_URL=http://localhost:8000 \
WIKIJS_URL=http://127.0.0.1:3000 \
MCP_ASSERTION_PRIVATE_KEY_FILE=deploy/keys/mcp-assertion-key.pem \
GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=... GOOGLE_ALLOWED_DOMAIN=example.com \
npm run dev -w @wikijs-mcp/server发布
发布是自动的。在 dev 分支上修改根目录 package.json 中的 version,创建一个 dev → main 的 PR 并合并即可。随后,Release on main 工作流会在推送到 main 时构建并推送 ghcr.io/<owner>/wikijs-mcp-server:vX.Y.Z(以及 :latest),同时创建 git 标签 vX.Y.Z 和 GitHub Release——全部在这一次运行中完成,只需使用内置的 GITHUB_TOKEN(无需配置 PAT/secret)。如果版本号没有变化,该次运行就是 no-op,因此普通合并到 main 不会产生 release。
要让这生效,需要一次性仓库设置:Settings → Actions → General → 工作流权限 = 读取和写入权限;如果你用规则集(ruleset)保护标签,还要允许 GitHub Actions 创建
v*标签。
安全说明
断言(Assertion):使用 RS256、TTL 60 秒、唯一的
jti,并提供防重放保护;私钥只存在于 MCP 服务器上。私钥一旦泄露,就意味着能冒充任意 wiki 用户登录——请将其视为根密钥(root secret)并轮换(生成新的密钥对,并更新对应 strategy 中的公钥)。Google 身份:规范标识符是
iss+sub;邮箱只用于查找。hd域名是从签名的 id_token 中验证的,而不是从参数里取。混淆代理(confused deputy)防御:在释放授权码之前,用户必须经过一个针对该客户端的同意屏幕(
requireConsent只有在可信的第一方场景中才可以关闭)。这可以防止攻击者通过 DCR 注册自己的 OAuth 客户端后静默获取受害者的令牌。Wiki.js 速率限制:
authentication.login为每个 IP 每分钟 5 次,而所有委托登录都来自 MCP 服务器的 IP。broker 会缓存 JWT(默认 30 分钟),并在限流时“尝试等待并重试”,因此在正常运行中无感知;但在大规模接入期间,最多可能出现大约一分钟的延迟。撤销:标准 OAuth
/revoke(按 token 撤销);在 Wiki.js 中停用用户后,下次 JWT 刷新时(≤30 分钟)委托会被中断;删除SESSION_STORE_FILE并重启 MCP 服务器,即可一次性清除所有会话。审计:工具调用会记录为结构化日志(谁、调用了哪个工具、ok/denied),且不包含页面内容。
MCP 端点:仅 Bearer 认证,每个 token 120 次请求/分钟,带安全响应头;OAuth 端点受 SDK 内置的速率限制保护。
限制与计划
RAG/语义搜索是一个独立的未来服务。接入点已就绪:
search_wiki通过SearchBackend接口工作(src/search/— v1 是原生 Wiki.js 搜索;RAG 服务将接收用户的 Wiki.js JWT 并保留 ACL 模型)。参见docs/rag-integration.md。当前是单实例 MCP 服务器(FileStore + 内存重放缓存)。高可用需要共享存储(Redis)——对应接口
KVStore已经拆好。Wiki.js 3.x 的认证机制不同——本模块以 2.5.x 为目标。
许可证
Apache-2.0
This server cannot be deployed
Maintenance
Related MCP Connectors
Shared project wiki for AI agents: read and write pages, next actions, and activity logs over MCP.
Hosted MCP for Google Workspace, Atlassian and Notion: one OAuth, per-user accounts, write access.
Read and write your team's shared, AI-readable wiki from any MCP client.
Read-only MCP for the Eco game wiki: search, Markdown pages, and wiki_* lookups. No keys, no writes.
Related MCP Servers
- AlicenseAqualityFmaintenanceEnables reading, creating, updating, and appending content to Yandex Wiki pages via MCP. Supports both read-write and read-only modes.79MIT
- AlicenseAqualityAmaintenanceEnables MCP clients to search, read, and edit Wiki.js pages, and manage assets, comments, users, groups, and maintenance jobs through the Wiki.js GraphQL API.62105 npm3MIT
- FlicenseNot gradedqualityBmaintenanceEnables MCP clients to interact with a Wiki.js 2.x instance via its GraphQL API, allowing page listing, reading, full-text search, creation, updating, and deletion, with a read-only mode and robust path handling.-
- AlicenseNot gradedqualityBmaintenanceEnables MCP clients to maintain a persistent Markdown knowledge base by ingesting sources, searching and reading notes, and creating or updating wiki pages with optimistic locking.12AGPL 3.0