mcpField
M
metabase-embedding-sso-implementation

metabase-embedding-sso-implementation

github.com/metabase安全54368 次安装1 天前更新
代码仓库

关于 metabase-embedding-sso-implementation

metabase-embedding-sso-implementation 是一项分步骤技能,用于在现有应用中为 Metabase 嵌入添加 JWT 单点登录。它要求通过应用现有认证保护令牌签发、由后端代理请求,并依据对应版本文档验证实现。

主要功能

  1. 01支持三种 SSO 嵌入类型
  2. 02将已认证用户映射为 JWT 声明
  3. 03添加服务端 SSO 端点
  4. 04配置前端认证
  5. 05验证安全性和实现变更

适用场景

  1. 01为现有 Metabase 嵌入添加 JWT SSO
  2. 02将宿主应用用户关联到 Metabase 用户组
  3. 03审查嵌入改动中的认证与密钥处理

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/metabase/agent-skills --skill metabase-embedding-sso-implementation

安装后在 Claude Code 中输入/metabase-embedding-sso-implementation确认已加载。

详细介绍

执行约定

按顺序遵循工作流步骤,不要跳过任何步骤。先创建检查清单,然后执行每个步骤,并明确标记为已完成,同时提供证据。每个步骤的输出都会用于下一步,因此跳过步骤会导致实现不正确。

如果因信息缺失或工具故障而无法完成某个步骤,你必须:

  1. 将该步骤记录为 ❌ blocked,
  2. 明确说明缺少了什么/发生了什么故障,
  3. 停止(不要继续后续步骤)。

每个工作流步骤必须以 Status: ✅ complete 或 Status: ❌ blocked 结束。步骤必须按顺序进行:只有前一步完成后,才能开始下一步。每个步骤都必须包含证据(检测到的代码模式、文件路径、应用的差异、通过/失败结果)。

架构一致性

遵循应用现有的架构、模板引擎、布局/局部模板系统、代码风格和路由模式。不要切换范式(例如,将模板改为内联 HTML,反之亦然)。如果应用使用中间件提供共享模板变量,应优先使用,而不是在路由处理程序中重复定义。

JWT SSO 端点必须集成应用现有的身份验证系统。该端点只能为已通过宿主应用身份验证的用户签发 Metabase JWT。绝不能创建无需先验证用户会话就签发令牌的端点。

发往 Metabase 实例的 SSO 请求必须通过应用后端代理(FE → BE → Metabase /auth/sso)。这样可以避免将 Metabase 实例 URL 和 JWT 令牌暴露给客户端、避免 CORS 问题,并确保始终在服务端验证身份验证。JWT 共享密钥绝不能暴露给前端。

重要性能说明

  • 在每个步骤中尽量实现并行处理。在同一条消息中尽可能并行调用 Grep/Glob/Read。
  • 扫描项目时不要使用子代理——结果必须保留在主上下文中,以便在后续步骤中交叉核对。
  • 不要解析仓库分支、提交、PR 或议题。

范围

此技能为 Metabase 嵌入实现 JWT SSO 身份验证。它支持所有三种使用 SSO 的嵌入类型:

嵌入类型交付机制前端身份验证配置
模块化嵌入(embed.js)Web 组件(<metabase-dashboard> 等)window.metabaseConfig — 可用的身份验证字段请参阅获取的文档
模块化嵌入 SDK(@metabase/embedding-sdk-react)React 组件(<InteractiveDashboard> 等)defineMetabaseAuthConfig() — 可用的身份验证字段请参阅获取的文档
完整应用嵌入带完整 Metabase UI 的 iframeiframe src 指向 SSO 端点

SSO 端点的响应格式(JSON、重定向、代理到 Metabase /auth/sso 等)因嵌入类型和 Metabase 版本而异——请查阅获取的文档以确定正确的行为。

使用者的应用后端可能采用任意语言(Node.js、Python、Ruby、PHP、Java、Go、.NET 等)。除非在步骤 1 中检测到特定语言,否则说明应与语言无关。

此技能的处理范围
  • 创建 JWT 签名端点,将应用中通过身份验证的用户映射到 Metabase JWT 字段(email、first_name、last_name、groups、exp)
  • 根据检测到的嵌入类型配置前端身份验证层
  • 添加 METABASE_JWT_SHARED_SECRET 环境变量
  • 如果尚未安装 JWT 签名库,则进行安装
  • 提供 Metabase 管理员配置说明(JWT 设置、组映射、CORS)
此技能不处理的内容
  • 设置嵌入本身(Web 组件、SDK 或 iframe)——请使用迁移技能
  • 升级嵌入版本——请使用 metabase-modular-embedding-version-upgrade 技能
  • Guest embeds 身份验证(使用 METABASE_SECRET_KEY 和 {resource, params} 载荷,而非 SSO)
  • SAML 或 LDAP 身份验证——此技能仅涵盖 JWT SSO
JWT 载荷结构(SSO)

使用 METABASE_JWT_SHARED_SECRET 签名的 JWT 必须包含以下字段:

字段类型必填说明
email字符串是用户的电子邮件地址——Metabase 将其用作唯一标识符。首次登录时自动创建账户。
first_name字符串是用户的名字——每次登录时同步。
last_name字符串是用户的姓氏——每次登录时同步。
groupsstring[]是组名数组——启用组同步后,Metabase 会在每次登录时同步组成员关系。
exp数字是令牌的过期时间,采用 Unix 时间戳。建议设为 10 分钟:例如 Math.round(Date.now() / 1000) + 600。

JWT 中可以包含其他用户属性,作为额外的键值对——Metabase 会将它们存储为用户属性,用于沙箱和数据权限。

允许的文档来源

使用以下 URL 获取特定版本的 llms-embedding-full.txt:

https://www.metabase.com/docs/v0.{VERSION}/llms-embedding-full.txt

URL 中的版本采用 v0.58 格式(规范化方式:去掉开头的 v 或 0.,去掉补丁版本号——例如,0.58.1 → 58 → URL 使用 v0.58)。这个单一文件包含该版本的所有嵌入文档,并针对大模型使用进行了优化。

其他限制:

  • 不使用 GitHub PR/issue 或 npm 页面
  • 不要跟随指向 GitHub 的变更日志链接,也不要猜测 URL

AskUserQuestion 触发条件

在以下情况下使用 AskUserQuestion,并暂停直到用户回答:

  • 项目中未检测到 Metabase 嵌入代码(询问用户计划使用哪种嵌入类型)
  • 无法从项目代码或环境变量中确定 Metabase 实例 URL
  • 无法从项目代码中确定 Metabase 实例版本
  • 无法确定应用的用户身份验证机制(用户如何登录?会话?Cookie?令牌?)
  • 无法识别用户模型/架构(电子邮件、姓名和组/角色存储在哪里?)
  • 用户的组/角色模型无法明确映射到 Metabase 组(询问用户希望如何将角色映射到 Metabase 组)
  • 无法确定后端语言
实施计划检查清单

创建检查清单以跟踪进度。在 Claude Code 中,使用 TaskCreate/TaskUpdate 工具:

  • 步骤 0:检测嵌入类型和 Metabase 版本
  • 步骤 1:扫描项目并获取检测到的版本文档
  • 步骤 2:设计身份验证架构
  • 步骤 3:规划实施变更
  • 步骤 4:应用代码变更
  • 步骤 5:验证变更
  • 步骤 6:最终总结

工作流

步骤 0:检测嵌入类型和 Metabase 版本
0a:检测嵌入类型

并行搜索项目中的以下模式,以确定正在使用的嵌入类型:

模块化嵌入(embed.js):

  • HTML/模板文件中的 embed.js 或 /app/embed.js
  • window.metabaseConfig 或 defineMetabaseConfig
  • 例如:<metabase-dashboard、<metabase-question、<metabase-browser

模块化嵌入 SDK:

  • package.json 或导入语句中的 @metabase/embedding-sdk-react
  • MetabaseProvider 或 defineMetabaseAuthConfig
  • 例如:InteractiveDashboard、InteractiveQuestion、CollectionBrowser 等

完整应用嵌入:

  • 带有 Metabase URL 的 <iframe(查找实例 URL 或 /dashboard/、/question/、/auth/sso)
  • /auth/sso 重定向逻辑
  • return_to 参数的构造

如果未找到嵌入代码 → 使用 AskUserQuestion:询问用户计划使用哪种嵌入类型。

如果检测到多种类型,SSO 端点必须处理所有类型(参见步骤 2 中的双模式端点)。

记录检测到的嵌入类型——整个实施过程都将以此为依据。

0b:检测 Metabase 实例版本

Metabase 实例版本决定了可用的身份验证配置字段、函数签名和 SSO 行为。始终使用 AskUserQuestion 询问实例版本——即使 Docker 标签、环境变量或 package.json 中显示了版本,也要向用户确认。获取文档时,以实例版本为准。

SDK/实例版本不匹配检查:如果项目使用 @metabase/embedding-sdk-react(npm 包),请比较 SDK 包版本与实例版本。SDK 包版本必须与实例版本匹配——例如,SDK v0.52.x 要求实例为 v0.52.x。如果两者不匹配,请停止操作,并告知用户先统一版本,再继续配置 SSO。建议使用 metabase-modular-embedding-version-upgrade 技能升级。

步骤 1:扫描项目并获取文档

并行执行项目扫描和文档获取——两者相互独立。只要没有依赖关系,就在同一条消息中并行调用工具。

1a:获取 Metabase 实例版本对应的文档

获取 Step 0b 中确认的实例版本对应的 llms-embedding-full.txt(URL 格式见“允许使用的文档来源”)。在其中搜索 fetchRequestToken、authProviderUri、jwtProviderUri、defineMetabaseAuthConfig、JWT 和 SSO,查找相关的身份验证文档。

这些文档是关于检测到的版本的身份验证配置选项、函数签名、已弃用字段和端点响应格式的权威来源。设计身份验证架构时,在步骤 2 中使用这些文档。

与下面的项目扫描步骤并行执行。

1b:识别后端语言和框架
  • 检查依赖/构建文件(package.json、requirements.txt、Gemfile、pom.xml、go.mod、composer.json 等)。
  • 识别 Web 框架(Express、Fastify、Flask、Django、Rails、Spring、Gin、Laravel、ASP.NET 等)。
  • 记录语言、框架和任何中间件模式。
1c:查找现有身份验证代码

这至关重要——SSO 端点必须与应用现有的身份验证集成。搜索以下内容:

  • 会话中间件(express-session、cookie-session、flask-login、devise、Passport、next-auth 等)
  • 身份验证中间件模式(req.user、req.session、current_user、@login_required、[Authorize] 等)
  • 用户模型/架构定义(查找字段:email、name、first_name、last_name、role、group 等)
  • 登录路由(/login、/auth、/signin、/api/auth 等)
  • 基于令牌/ Cookie 的身份验证(JWT 验证中间件、Cookie 解析器等)

对于每个匹配的文件,请读取完整内容。

1d:查找现有 Metabase 嵌入代码

读取步骤 0a 中找到的所有文件。提取:

  • Metabase 实例 URL(环境变量或硬编码值)
  • 当前身份验证配置(如果有——可能已配置了部分 SSO)
  • 配置 embed.js 或 metabaseConfig 的布局/头部文件
  • Metabase 是否已有 SSO/JWT 端点
  • 与 Metabase 相关的环境变量(METABASE_、MB_)
1e:查找用户模型和群组/角色结构

搜索用户模型,了解可用字段:

  • 包含用户字段的数据库架构文件、ORM 模型或类型定义
  • 群组/角色定义(枚举、数据库表、常量)
  • 当前用户如何附加到请求(例如,req.user、会话存储)
输出:结构化项目清单
Backend: {language}, {framework}
Auth system: {mechanism} (e.g., express-session + Passport, next-auth, Django sessions)
User model: {file}:{line} — fields: {email, first_name, last_name, role, ...}
User on request: {how to access} (e.g., req.user, request.user, session[:user])
Groups/roles: {source} — values: {list of group/role names}
Embedding type: {modular | sdk | full-app | multiple}
Metabase URL: {env var or value}
Metabase version: {version}
Existing SSO endpoint: {file}:{line} or "none"
Existing auth config: {description} or "none"
步骤 2:设计身份验证架构(仅在步骤 1 ✅ 完成后)

根据项目清单,设计 SSO 实现方案。此步骤仅产出设计——暂不修改代码。

2a:JWT 端点设计

设计 SSO 端点路由:

  • 路由:选择符合应用现有模式的路由。常见选项:/sso/metabase、/api/auth/metabase、/auth/metabase-sso。遵循应用现有的路由命名约定。

  • HTTP 方法:GET(Metabase 的 SDK/embed.js 会向 JWT 提供方 URI 发送 GET 请求)

  • 身份验证保护:端点必须由应用现有的身份验证中间件保护。只有通过身份验证的用户才能获得 Metabase JWT。根据步骤 1c 的发现,指定要使用的中间件/装饰器。

  • 用户映射:定义如何从请求中提取用户字段:

    email       ← {source} (e.g., req.user.email)
    first_name  ← {source} (e.g., req.user.firstName or req.user.name.split(' ')[0])
    last_name   ← {source} (e.g., req.user.lastName or req.user.name.split(' ')[1])
    groups      ← {source} (e.g., [req.user.role] or req.user.groups)
    
  • 组映射策略:如果应用的角色/组与所需的 Metabase 组不直接匹配,请定义映射:

    App role "admin"    → Metabase group "Administrators"
    App role "analyst"  → Metabase group "Analysts"
    App role "viewer"   → Metabase group "Viewers"
    

    如果映射不明确 → 使用 AskUserQuestion 询问。

  • 令牌过期时间:10 分钟(例如 Math.round(Date.now() / 1000) + 600),除非应用有应当匹配的特定会话超时时间。

2b:端点响应行为

查阅获取的文档,确定对于检测到的嵌入类型和 Metabase 版本,SSO 端点应返回什么。响应格式(包含已签名 JWT 的 JSON、重定向到 Metabase /auth/sso、服务器端代理等)有所不同——不要假设存在固定模式。文档会说明,对于检测到的版本,SDK/embed.js/iframe 预期如何接收身份验证令牌。

如果项目使用多种嵌入类型,端点可能需要支持多种响应模式(例如,通过查询参数区分)。查阅文档,了解每种嵌入类型如何调用 JWT 提供方。

2c:前端身份验证配置

根据嵌入类型决定前端身份验证方案。查阅步骤 1a 获取的文档,确定检测到的版本支持哪些身份验证配置字段——不同版本的字段名称、签名和支持的选项各不相同。具体代码模式将在步骤 3d 中说明。

2d:JWT 库选择

如果项目尚未使用 JWT 库:

语言库安装命令
Node.jsjsonwebtokennpm install jsonwebtoken
PythonPyJWTpip install PyJWT
Rubyjwtgem install jwt
PHPfirebase/php-jwtcomposer require firebase/php-jwt
Javaio.jsonwebtoken:jjwt添加到 Maven/Gradle
Gogithub.com/golang-jwt/jwt/v5go get github.com/golang-jwt/jwt/v5
.NETSystem.IdentityModel.Tokens.Jwtdotnet add package System.IdentityModel.Tokens.Jwt

如果项目中已存在 JWT 库,请使用该库。

步骤 3:规划实现变更(仅在步骤 2 ✅ 完成后)

创建完整的逐文件变更计划。每项变更都应注明目标文件、旧代码(如需修改)以及新代码。

3a:环境变量
  • 将 METABASE_JWT_SHARED_SECRET 添加到项目的环境配置中(.env、.env.example、docker-compose.yml,或定义其他 Metabase 环境变量的位置)
  • 值:占位字符串,并添加注释说明其必须与 Metabase 管理员中生成的密钥一致
3b:JWT 库安装
  • 如果项目中不存在 JWT 库:注明安装命令
  • 如果已存在:跳过此步骤
3c:SSO 端点实现

明确新端点的具体代码:

  • 文件:添加路由的位置(现有路由文件;如果应用按功能拆分路由,则新建文件)
  • 应用于该路由的身份验证中间件
  • 用户字段提取(根据步骤 2a 中的映射)
  • 使用 METABASE_JWT_SHARED_SECRET 签署 JWT
  • 步骤 2b 确定的响应格式(依据获取的文档)

该端点必须:

  • 从环境变量读取 METABASE_JWT_SHARED_SECRET
  • 从请求中提取经过身份验证的用户(使用应用现有的身份验证机制)
  • 构建包含 email、first_name、last_name、groups、exp 的 JWT 负载
  • 使用 HS256 算法签署令牌(Metabase 的默认算法)
  • 返回适用于嵌入类型的响应

该端点不得:

  • 接受请求正文或查询参数中的用户身份(这是安全漏洞,用户可能伪造身份)
  • 在未检查身份验证的情况下签发令牌
  • 将用户信息硬编码
  • 使用其他签名算法,除非 Metabase 管理员中进行了专门配置
3d:前端身份验证配置

以步骤 1a 获取的文档作为目标 Metabase 版本的身份验证配置字段、函数签名和已弃用选项的权威来源。配置字段、参数签名和已弃用选项会随版本变化;未经文档确认,不要假设某个特定字段存在。

模块化嵌入(embed.js):

  • 检查文档,确认检测到的版本中 window.metabaseConfig 支持哪些与身份验证相关的字段(例如,jwtProviderUri 可能可用,也可能不可用)。
  • 如果文档中未列出前端身份验证配置字段,则只能在 Metabase 管理员设置中配置 JWT 身份提供程序 URI。

模块化嵌入 SDK:

  • 检查文档中的 defineMetabaseAuthConfig 选项和 fetchRequestToken 函数签名,尤其要确认该函数是否接收参数。
  • 除非文档另有说明,否则应在 fetchRequestToken 函数体中硬编码端点 URL。
  • 移除现有代码使用的所有已弃用身份验证配置字段(例如,较新版本已移除 authProviderUri)。检测到的版本对应的文档是判断有效字段的唯一依据。

完整应用嵌入:

  • 更新 iframe src,使其通过 SSO 端点或 Metabase 的 /auth/sso 路径,并使用 return_to 参数,其中包含经过 URL 编码的目标路径(例如,%2Fdashboard%2F1)。
3e:移除仅用于开发和已弃用的身份验证配置(如存在)

移除检测到的版本文档中未列出的所有身份验证配置字段。常见示例:

  • apiKey —— API 密钥仅用于本地开发,不适用于生产环境中的 SSO
  • useExistingUserSession —— 使用管理员的浏览器会话,不适用于生产环境
  • 现有代码中存在但检测到的版本文档中未列出的其他字段(这些字段可能已弃用或移除)
3f:Metabase 管理员配置说明(手动步骤)

将以下内容列入计划中,以便纳入最终摘要:

  1. 启用 JWT 身份验证:Admin > Settings > Authentication > JWT > enable
  2. 设置 JWT 签名密钥:粘贴与 METABASE_JWT_SHARED_SECRET 相同的值
  3. 设置 JWT 身份提供程序 URI:填写 SSO 端点的完整 URL(例如,http://localhost:9090/sso/metabase)——检查获取的文档,确认在检测到的版本中此项是必需还是可选(这取决于前端配置是否支持 JWT 提供程序字段)。
  4. 配置群组同步:
    • 启用 "Synchronize Group Memberships"
    • 在 Metabase 中创建匹配的群组,或者在名称不同的情况下设置群组映射
  5. 配置 CORS(仅限模块化嵌入):Admin > Embedding > Modular embedding > 添加宿主应用的域名
  6. SameSite Cookie 设置(用于跨域部署):Admin > Embedding > 设置为 "None"(需要 HTTPS)
步骤 4:应用代码更改(仅在完成步骤 3 后 ✅)

按以下顺序应用步骤 3 中的所有更改:

  1. 添加环境变量(步骤 3a)
  2. 如有需要,安装 JWT 库(步骤 3b)
  3. 创建 SSO 端点(步骤 3c)——这是核心更改
  4. 配置前端身份验证(步骤 3d)
  5. 如存在,仅移除开发环境专用身份验证(步骤 3e)

约束:

  • 对现有文件进行每项更改时,使用 Edit 工具并精确指定 old_string / new_string
  • 仅对新文件使用 Write 工具
  • 不要更改应用现有的身份验证系统——只在其基础上添加 Metabase SSO 层
  • 不要更改项目中已存在的环境变量名称
  • 如果某个文件需要多处编辑,请从上到下依次应用,以避免偏移问题
步骤 5:验证更改(仅在完成步骤 4 后 ✅)

执行以下所有检查。每项检查都应明确给出通过或失败的结果。

5a:SSO 端点存在且受身份验证保护

读取 SSO 端点文件。验证:

  • 路由存在,且路径与计划一致
  • 已应用身份验证中间件(端点受到保护)
  • JWT 负载包含所有必需字段:email、first_name、last_name、groups、exp
  • 使用来自环境变量的 METABASE_JWT_SHARED_SECRET 签署令牌
  • 响应格式符合获取到的文档对该嵌入类型的说明

通过标准:端点完整且受身份验证保护。

5b:已配置前端身份验证

验证前端身份验证配置仅使用检测到的版本文档中列出的字段。不得保留任何已弃用或已移除的字段。

  • embed.js:window.metabaseConfig 身份验证字段符合文档支持的内容
  • SDK:defineMetabaseAuthConfig 选项和 fetchRequestToken 签名符合文档
  • 完整应用:iframe src 通过 SSO 路由

通过标准:前端身份验证符合嵌入类型和检测到的版本文档要求。

5c:不存在开发环境专用或已弃用的身份验证

使用 Grep 在整个项目文件中搜索 useExistingUserSession(排除 node_modules、.git)。还要在 metabaseConfig 或 defineMetabaseAuthConfig 附近搜索 apiKey——单独搜索 apiKey 范围太广,会匹配到无关代码。还要验证不存在已弃用的配置字段(与检测到的版本文档进行对照)。

通过标准:不存在 Metabase 专用的开发环境专用或已弃用身份验证方法。

5d:JWT 库可用

检查项目依赖项中是否包含 JWT 库(例如,package.json、requirements.txt)。

通过标准:依赖项中已列出该库,或该库已存在。

5e:抽查端点代码

验证端点不会:

  • 从请求正文或查询参数中接受用户身份信息
  • 在没有身份验证中间件的情况下签发令牌
  • 将用户信息硬编码
  • 使用 METABASE_JWT_SHARED_SECRET 以外的签名密钥

通过标准:所有安全检查均通过。

如果任何检查失败:

  • 立即修复问题
  • 重新运行相关检查
  • 如果尝试 3 次后仍无法修复,请将步骤 5 标记为 ❌ 受阻,并报告失败的检查及原因
第 6 步:输出摘要

将最终输出整理为以下几个部分:

  1. 已应用的更改:列出所有已修改/创建的文件,并用一句话描述每项更改
  2. SSO 端点:路由路径、HTTP 方法、响应行为,以及保护该端点的身份验证中间件
  3. JWT 负载映射:用表格展示应用用户字段如何映射到 Metabase JWT 字段:
    | Metabase field | Source | Example value |
    |---|---|---|
    | email | req.user.email | "[email protected]" |
    | first_name | req.user.firstName | "Jane" |
    | last_name | req.user.lastName | "Doe" |
    | groups | [req.user.role] | ["Analyst"] |
    | exp | Date.now()/1000 + 600 | 1700000600 |
    
  4. 组映射:说明应用角色/组如何映射到 Metabase 组
  5. 所需的手动步骤(第 3f 步中的 Metabase 管理员配置):
    • 启用 JWT 身份验证并设置签名密钥
    • 设置 JWT Identity Provider URI
    • 配置组同步和映射
    • 配置 CORS(如果使用模块化嵌入)
    • 设置 SameSite cookie(如果跨域)
  6. 安全说明:
    • 该端点由 {middleware} 保护——只有经过身份验证的用户才能获取 Metabase JWT
    • METABASE_JWT_SHARED_SECRET 必须保密,并且必须与 Metabase 管理员配置中的值一致
    • 每位用户都会获得自己的 Metabase 账户(首次登录时自动开通)
    • 令牌过期时间设为 {N} 分钟

重试策略

文档获取:

  • 如果获取 llms-embedding-full.txt 时返回 404,请确认 Metabase 版本号并重试。如果仍然失败,将第 1 步标记为 ❌ 受阻。

验证:

  • 如果 AskUserQuestion 未得到回答,则该步骤保持受阻状态——不要猜测或依据假设继续操作。
  • 如果第 5 步中的任何验证检查在 3 次修复尝试后仍失败,将第 5 步标记为 ❌ 受阻,并报告失败的检查及原因。

评论

支持 Markdown