Install
$ agentstack add skill-metabase-agent-skills-metabase-embedding-sso-implementation ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Execution contract
Follow the workflow steps in order — do not skip any step. Create the checklist first, then execute each step and explicitly mark it done with evidence. Each step's output feeds into the next, so skipping steps produces incorrect implementations.
If you cannot complete a step due to missing info or tool failure, you must:
- record the step as ❌ blocked,
- explain exactly what is missing / what failed,
- stop (do not proceed to later steps).
Each workflow step must end with Status: ✅ complete or Status: ❌ blocked. Steps are sequential — do not start a step until the previous one is complete. Each step must include evidence (detected code patterns, file paths, diffs applied, pass/fail results).
Architectural conformance
Follow the app's existing architecture, template engine, layout/partial system, code style, and route patterns. Do not switch paradigms (e.g., templates to inline HTML or vice versa). If the app has middleware for shared template variables, prefer that over duplicating across route handlers.
The JWT SSO endpoint must integrate with the app's existing authentication system. The endpoint must only issue Metabase JWTs for users who are already authenticated in the host app. Never create an endpoint that issues tokens without verifying the user's session first.
SSO requests to the Metabase instance must be proxied through the app's backend (FE → BE → Metabase /auth/sso). This keeps the Metabase instance URL and JWT tokens off the client, avoids CORS issues, and ensures auth is always validated server-side. The JWT shared secret must never be exposed to the frontend.
Important performance notes
- Maximize parallelism within each step. Use parallel Grep/Glob/Read calls in a single message wherever possible.
- Do not use sub-agents for project scanning — results need to stay in the main context for cross-referencing in later steps.
- Do not parse repo branches, commits, PRs, or issues.
Scope
This skill implements JWT SSO authentication for Metabase embedding. It supports all three embedding types that use SSO:
| Embedding type | Delivery mechanism | Frontend auth config | |---|---|---| | Modular embedding (embed.js) | Web components (`, etc.) | window.metabaseConfig — see fetched docs for available auth fields | | **Modular embedding SDK** (@metabase/embedding-sdk-react) | React components (, etc.) | defineMetabaseAuthConfig() — see fetched docs for available auth fields | | **Full App embedding** | iframe with full Metabase UI | iframe src` points through SSO endpoint |
The SSO endpoint response format (JSON, redirect, proxy to Metabase /auth/sso, etc.) varies by embedding type and Metabase version — consult the fetched docs to determine the correct behavior.
The consumer's app may be written in any backend language (Node.js, Python, Ruby, PHP, Java, Go, .NET, etc.). Keep instructions language-agnostic unless a specific language is detected in Step 1.
What this skill handles
- Creating a JWT signing endpoint that maps the app's authenticated user to Metabase JWT fields (
email,first_name,last_name,groups,exp) - Configuring the frontend auth layer based on the detected embedding type
- Adding the
METABASE_JWT_SHARED_SECRETenvironment variable - Installing a JWT signing library if one is not already present
- Producing Metabase admin configuration instructions (JWT settings, group mappings, CORS)
What this skill does NOT handle
- Setting up the embedding itself (web components, SDK, or iframes) — use the migration skills for that
- Upgrading the embedding version — use the
metabase-modular-embedding-version-upgradeskill - Guest embeds auth (uses
METABASE_SECRET_KEYwith{resource, params}payloads, not SSO) - SAML or LDAP authentication — this skill covers JWT SSO only
JWT payload structure (SSO)
The JWT signed with METABASE_JWT_SHARED_SECRET must contain these fields:
| Field | Type | Required | Description | |---|---|---|---| | email | string | Yes | User's email — Metabase uses this as the unique identifier. Auto-provisions account on first login. | | first_name | string | Yes | User's first name — synced on every login. | | last_name | string | Yes | User's last name — synced on every login. | | groups | string[] | Yes | Array of group names — Metabase syncs group memberships on every login when group sync is enabled. | | exp | number | Yes | Token expiration as Unix timestamp. Recommend 10 minutes: e.g. Math.round(Date.now() / 1000) + 600. |
Additional user attributes can be included as extra key/value pairs in the JWT — Metabase will store them as user attributes for use in sandboxing and data permissions.
Allowed documentation sources
Fetch the version-specific llms-embedding-full.txt using this URL:
https://www.metabase.com/docs/v0.{VERSION}/llms-embedding-full.txt
The version in the URL uses the format v0.58 (normalize: strip leading v or 0., drop patch — e.g., 0.58.1 → 58 → URL uses v0.58). This single file contains all embedding documentation for that version, optimized for LLM consumption.
Other constraints:
- No GitHub PRs/issues or npm pages
- Do not follow changelog links to GitHub or guess URLs
AskUserQuestion triggers
Use AskUserQuestion and halt until answered if:
- No Metabase embedding code is detected in the project (ask which embedding type the user plans to use)
- The Metabase instance URL cannot be determined from project code or environment variables
- The Metabase instance version cannot be determined from the project code
- The app's user authentication mechanism cannot be determined (how do users log in? session? cookie? token?)
- The user model/schema cannot be identified (where are email, name, and group/role stored?)
- The user's group/role model does not clearly map to Metabase groups (ask the user how they want to map roles → Metabase groups)
- The backend language cannot be determined
Implementation Plan Checklist
Create a checklist to track progress. In Claude Code, use TaskCreate/TaskUpdate tools:
- Step 0: Detect embedding type and Metabase version
- Step 1: Scan project + fetch detected version docs
- Step 2: Design auth architecture
- Step 3: Plan implementation changes
- Step 4: Apply code changes
- Step 5: Validate changes
- Step 6: Final summary
Workflow
Step 0: Detect embedding type and Metabase version
0a: Detect embedding type
Grep the project for these patterns (in parallel) to determine which embedding type is in use:
Modular embedding (embed.js):
embed.jsor/app/embed.jsin HTML/template fileswindow.metabaseConfigordefineMetabaseConfig- e.g. ` Settings > Authentication > JWT > enable
- Set JWT signing key: Paste the same value as
METABASE_JWT_SHARED_SECRET - Set JWT Identity Provider URI: The full URL of the SSO endpoint (e.g.,
http://localhost:9090/sso/metabase) — check the fetched docs to determine whether this is required or optional for the detected version (it depends on whether the frontend config supports a JWT provider field). - Configure group sync:
- Enable "Synchronize Group Memberships"
- Create matching groups in Metabase, or set up group mappings if names differ
- Configure CORS (for modular embedding only): Admin > Embedding > Modular embedding > add the host app's domain
- SameSite cookie setting (for cross-domain deployments): Admin > Embedding > set to "None" (requires HTTPS)
Step 4: Apply code changes (only after Step 3 ✅)
Apply all changes from Step 3 in this order:
- Add environment variable (Step 3a)
- Install JWT library if needed (Step 3b)
- Create the SSO endpoint (Step 3c) — this is the core change
- Configure frontend auth (Step 3d)
- Remove dev-only auth if present (Step 3e)
Constraints:
- Use the Edit tool with precise
old_string/new_stringfor every change to existing files - Use the Write tool only for new files
- Do not change the app's existing authentication system — only add the Metabase SSO layer on top
- Do not change environment variable names that already exist in the project
- If a file requires multiple edits, apply them top-to-bottom to avoid offset issues
Step 5: Validate changes (only after Step 4 ✅)
Perform all of these checks. Each check should have an explicit pass/fail result.
5a: SSO endpoint exists and is auth-protected
Read the SSO endpoint file. Verify:
- The route exists with the planned path
- Auth middleware is applied (the endpoint is protected)
- The JWT payload includes all required fields:
email,first_name,last_name,groups,exp - The token is signed with
METABASE_JWT_SHARED_SECRETfrom environment - The response format matches what the fetched docs specify for the embedding type
Pass criteria: endpoint is complete and auth-protected.
5b: Frontend auth is configured
Verify the frontend auth config uses only fields listed in the detected version's docs. No deprecated or removed fields should remain.
- embed.js:
window.metabaseConfigauth fields match what the docs support - SDK:
defineMetabaseAuthConfigoptions andfetchRequestTokensignature match the docs - Full App: iframe src routes through SSO
Pass criteria: frontend auth matches the embedding type and detected version docs.
5c: No dev-only or deprecated auth remains
Use Grep to search for useExistingUserSession across all project files (excluding node_modules, .git). Also search for apiKey near metabaseConfig or defineMetabaseAuthConfig — a bare apiKey grep is too broad and will match unrelated code. Also verify no deprecated config fields remain (compare against the detected version docs).
Pass criteria: no Metabase-specific development-only or deprecated auth methods remain.
5d: JWT library is available
Check that the JWT library is in the project's dependencies (e.g., package.json, requirements.txt).
Pass criteria: library is listed or was already present.
5e: Spot-check the endpoint code
Verify the endpoint does NOT:
- Accept user identity from request body/query params
- Issue tokens without auth middleware
- Hardcode user information
- Use a signing key other than
METABASE_JWT_SHARED_SECRET
Pass criteria: all security checks pass.
If any check fails:
- Fix the issue immediately
- Re-run the specific check
- If unable to fix after 3 attempts, mark Step 5 ❌ blocked and report which check failed and why
Step 6: Output summary
Organize the final output into these sections:
- Changes applied: list every file modified/created and a one-line description of each change
- SSO endpoint: route path, HTTP method, response behavior, which auth middleware protects it
- JWT payload mapping: table showing how app user fields map to Metabase JWT fields:
`` | Metabase field | Source | Example value | |---|---|---| | email | req.user.email | "jane@example.com" | | first_name | req.user.firstName | "Jane" | | last_name | req.user.lastName | "Doe" | | groups | [req.user.role] | ["Analyst"] | | exp | Date.now()/1000 + 600 | 1700000600 | ``
- Group mapping: how app roles/groups map to Metabase groups
- Manual steps required (Metabase admin configuration from Step 3f):
- Enable JWT authentication and set signing key
- Set JWT Identity Provider URI
- Configure group sync and mappings
- Configure CORS (if modular embedding)
- Set SameSite cookie (if cross-domain)
- Security notes:
- The endpoint is protected by
{middleware}— only authenticated users can obtain a Metabase JWT METABASE_JWT_SHARED_SECRETmust be kept secret and match the value in Metabase admin- Each user gets their own Metabase account (auto-provisioned on first login)
- Token expiration is set to {N} minutes
Retry policy
Doc fetching:
- If fetching
llms-embedding-full.txtreturns 404, verify the Metabase version number and retry. If still failing, mark Step 1 ❌ blocked.
Validation:
- If AskUserQuestion is not answered, remain blocked on that step — do not guess or proceed with assumptions.
- If any validation check in Step 5 fails after 3 fix attempts, mark Step 5 ❌ blocked and report which check failed and why.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: metabase
- Source: metabase/agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.