A sample app that shows two ways to embed Metabase dashboards:
- SSO Embed - User is authenticated via your app. Metabase knows who they are. You get drill-through, permissions, the works.
- Guest Embed - Anonymous access via signed JWT. No user identity passed to Metabase. You can do simple filtering.
- Metabase Pro or Enterprise (free trial)
- Bun
cp .env-template .env
# Edit .env with your keys (see below)
bun install
bun run index.tsOpen http://localhost:8080 to view the app. Use the tabs to switch between SSO and Guest embeds.
Admin > Settings > Embedding > Enable Modular embedding
| Embed Type | Where to find the key |
|---|---|
| SSO | Admin > Settings > Authentication > JWT > JWT signing key |
| Guest | Admin > Settings > Embedding > Embedding secret key |
METABASE_SECRET_KEY="your-jwt-signing-key"
METABASE_EMBEDDING_SECRET_KEY="your-embedding-secret-key"
METABASE_SITE_URL="http://localhost:3000"
In Metabase (Admin > Settings > Authentication > JWT):
- Set JWT Identity Provider URI to
http://localhost:8080/sso/metabase - Ensure the signing key matches
METABASE_SECRET_KEY
Admin > Settings > Embedding > Authorized origins: add http://localhost:8080
Browser → embed.js → /api/metabase/auth → JWT with user claims → Metabase
The <metabase-dashboard> component uses authProviderUri to fetch a JWT containing user info (email, name). Metabase auto-provisions the user on first login.
<metabase-dashboard dashboard-id="1" with-title="false"></metabase-dashboard>window.metabaseConfig = {
instanceUrl: "http://localhost:3000",
authProviderUri: "http://localhost:8080/api/metabase/auth",
};Browser → /api/metabase/guest-token → JWT with resource access → embed.js → Metabase
The token grants access to a specific dashboard without user identity. Your server signs tokens with the embedding secret key.
<metabase-dashboard
with-title="true"
with-downloads="true"
></metabase-dashboard>window.metabaseConfig = {
isGuest: true,
instanceUrl: "http://localhost:3000",
};
// Fetch token from your backend, then:
dashboard.setAttribute("token", token);| Endpoint | Purpose |
|---|---|
/api/metabase/auth |
Returns JWT with user claims (SSO) |
/api/metabase/guest-token |
Returns JWT with resource access (Guest) |
/sso/metabase |
SSO endpoint for Metabase redirects |
Edit the dashboard-id attribute (SSO) or resource.dashboard in the token payload (Guest).
Edit the JWT claims in index.ts:
const token = jwt.sign(
{
email: "user@example.com",
first_name: "First",
last_name: "Last",
exp: Math.floor(Date.now() / 1000) + 60 * 10,
},
METABASE_SECRET_KEY,
);Click the Theme button to customize colors. Click Export theme to download as JSON.
| Issue | Solution |
|---|---|
| CORS errors | Add http://localhost:8080 to allowed origins in Metabase |
| "Message corrupted" (Guest) | Check METABASE_EMBEDDING_SECRET_KEY matches Metabase |
| JWT errors (SSO) | Check METABASE_SECRET_KEY matches Metabase |
| Dashboard not found | Verify dashboard ID exists and user has access |
