Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions .env.docker.example
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
PREMIUM_EMBEDDING_TOKEN="<your_enterprise_token>"
METABASE_JWT_SHARED_SECRET="ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
METABASE_STATIC_EMBEDDING_SECRET="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
METABASE_ADMIN_EMAIL="rene@example.com"
METABASE_ADMIN_FIRST_NAME="Rene"
METABASE_ADMIN_LAST_NAME="Descartes"
METABASE_ADMIN_PASSWORD="foobarbaz"
METABASE_DASHBOARD_ID_TO_EMBED=1
MB_PREMIUM_EMBEDDING_TOKEN="<your_enterprise_token>"
MB_JWT_SHARED_SECRET="ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
MB_EMBEDDING_SECRET_KEY="eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
MB_ADMIN_EMAIL="rene@example.com"
MB_ADMIN_FIRST_NAME="Rene"
MB_ADMIN_LAST_NAME="Descartes"
MB_ADMIN_PASSWORD="foobarbaz"
MB_DASHBOARD_ID_TO_EMBED=1

MB_PORT=4300
SERVER_PORT=4400

# for development
# MB_RUN_MODE="dev"
# METASTORE_DEV_SERVER_URL=""
16 changes: 8 additions & 8 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
PORT=3100
METABASE_INSTANCE_URL="http://localhost:3000"
METABASE_JWT_SHARED_SECRET="<shared_secret>"
METABASE_STATIC_EMBEDDING_SECRET="<static_embedding_secret>"
METABASE_ADMIN_EMAIL="rene@example.com"
METABASE_ADMIN_FIRST_NAME="Rene"
METABASE_ADMIN_LAST_NAME="Descartes"
METABASE_ADMIN_PASSWORD="foobarbaz"
METABASE_DASHBOARD_ID_TO_EMBED=1
MB_INSTANCE_URL="http://localhost:3000"
MB_JWT_SHARED_SECRET="<shared_secret>"
MB_EMBEDDING_SECRET_KEY="<static_embedding_secret>"
MB_ADMIN_EMAIL="rene@example.com"
MB_ADMIN_FIRST_NAME="Rene"
MB_ADMIN_LAST_NAME="Descartes"
MB_ADMIN_PASSWORD="foobarbaz"
MB_DASHBOARD_ID_TO_EMBED=1
2 changes: 1 addition & 1 deletion .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
timeout-minutes: 10
name: e2e-tests
env:
PREMIUM_EMBEDDING_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
MB_PREMIUM_EMBEDDING_TOKEN: ${{ secrets.STAGING_MB_ALL_FEATURES_TOKEN }}
MB_RUN_MODE: dev
METASTORE_DEV_SERVER_URL: ${{ secrets.METASTORE_DEV_SERVER_URL }}
permissions:
Expand Down
8 changes: 8 additions & 0 deletions DEV.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Development info (for Metabase developers only)

### Dev env variables
To test with Docker locally, define additional env variables in the `.env.docker` env file:
```
MB_RUN_MODE="dev"
METASTORE_DEV_SERVER_URL="https://token-check.staging.metabase.com"
```
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ A minimal sample demonstrating Metabase embedding using **embed.js** with Node.j

```bash
cp .env.docker.example .env.docker
# Edit .env.docker and add your PREMIUM_EMBEDDING_TOKEN
# Edit .env.docker and add your MB_PREMIUM_EMBEDDING_TOKEN
```

2. Start the containers:
Expand Down Expand Up @@ -79,7 +79,7 @@ const payload = {
params: {},
exp: Math.round(Date.now() / 1000) + 10 * 60,
};
const token = jwt.sign(payload, METABASE_STATIC_EMBEDDING_SECRET);
const token = jwt.sign(payload, MB_EMBEDDING_SECRET_KEY);
```

```html
Expand Down Expand Up @@ -107,7 +107,7 @@ const ssoPayload = {
groups: ["Customer"],
exp: Math.round(Date.now() / 1000) + 10 * 60,
};
const ssoToken = jwt.sign(ssoPayload, METABASE_JWT_SHARED_SECRET);
const ssoToken = jwt.sign(ssoPayload, MB_JWT_SHARED_SECRET);
res.json({ jwt: ssoToken });
```

Expand All @@ -131,11 +131,11 @@ res.json({ jwt: ssoToken });
| Variable | Description | Default |
|----------|-------------|---------|
| `PORT` | Server port | `3100` |
| `METABASE_INSTANCE_URL` | Metabase URL | `http://localhost:3000` |
| `METABASE_JWT_SHARED_SECRET` | JWT signing secret for SSO authentication | - |
| `METABASE_STATIC_EMBEDDING_SECRET` | JWT signing secret for guest embeds | - |
| `METABASE_DASHBOARD_ID_TO_EMBED` | ID of the dashboard to embed | `1` |
| `PREMIUM_EMBEDDING_TOKEN` | Metabase Enterprise license | - |
| `MB_INSTANCE_URL` | Metabase URL | `http://localhost:3000` |
| `MB_JWT_SHARED_SECRET` | JWT signing secret for SSO authentication | - |
| `MB_EMBEDDING_SECRET_KEY` | JWT signing secret for guest embeds | - |
| `MB_DASHBOARD_ID_TO_EMBED` | ID of the dashboard to embed | `1` |
| `MB_PREMIUM_EMBEDDING_TOKEN` | Metabase Enterprise license | - |

## Running E2E Tests

Expand Down
42 changes: 21 additions & 21 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ services:
MB_JETTY_PORT: "${MB_PORT}"
MB_EDITION: "ee"
MB_SITE_URL: "http://localhost:${MB_PORT}/"
MB_JWT_SHARED_SECRET: "${METABASE_JWT_SHARED_SECRET}"
MB_EMBEDDING_SECRET_KEY: "${METABASE_STATIC_EMBEDDING_SECRET}"
MB_SETUP_TOKEN: "${PREMIUM_EMBEDDING_TOKEN}"
MB_PREMIUM_EMBEDDING_TOKEN: "${PREMIUM_EMBEDDING_TOKEN}"
MB_RUN_MODE: "${MB_RUN_MODE}"
METASTORE_DEV_SERVER_URL: "${METASTORE_DEV_SERVER_URL}"
MB_JWT_SHARED_SECRET: "${MB_JWT_SHARED_SECRET}"
MB_EMBEDDING_SECRET_KEY: "${MB_EMBEDDING_SECRET_KEY}"
MB_SETUP_TOKEN: "${MB_PREMIUM_EMBEDDING_TOKEN}"
MB_PREMIUM_EMBEDDING_TOKEN: "${MB_PREMIUM_EMBEDDING_TOKEN}"
MB_RUN_MODE: "${MB_RUN_MODE:-}"
METASTORE_DEV_SERVER_URL: "${METASTORE_DEV_SERVER_URL:-}"
MB_JWT_IDENTITY_PROVIDER_URI: "http://localhost:${SERVER_PORT}/auth/sso"
METABASE_ADMIN_EMAIL: "${METABASE_ADMIN_EMAIL}"
METABASE_ADMIN_FIRST_NAME: "${METABASE_ADMIN_FIRST_NAME}"
METABASE_ADMIN_LAST_NAME: "${METABASE_ADMIN_LAST_NAME}"
METABASE_ADMIN_PASSWORD: "${METABASE_ADMIN_PASSWORD}"
MB_ADMIN_EMAIL: "${MB_ADMIN_EMAIL}"
MB_ADMIN_FIRST_NAME: "${MB_ADMIN_FIRST_NAME}"
MB_ADMIN_LAST_NAME: "${MB_ADMIN_LAST_NAME}"
MB_ADMIN_PASSWORD: "${MB_ADMIN_PASSWORD}"
healthcheck:
test: curl --fail -X GET -I "http://localhost:${MB_PORT}/api/health" || exit 1
interval: 15s
Expand All @@ -33,10 +33,10 @@ services:
metabase:
condition: service_healthy
environment:
METABASE_INSTANCE_URL: "http://metabase:${MB_PORT}"
METABASE_ADMIN_EMAIL: "${METABASE_ADMIN_EMAIL}"
METABASE_ADMIN_PASSWORD: "${METABASE_ADMIN_PASSWORD}"
METABASE_DASHBOARD_ID_TO_EMBED: "${METABASE_DASHBOARD_ID_TO_EMBED}"
MB_INSTANCE_URL: "http://metabase:${MB_PORT}"
MB_ADMIN_EMAIL: "${MB_ADMIN_EMAIL}"
MB_ADMIN_PASSWORD: "${MB_ADMIN_PASSWORD}"
MB_DASHBOARD_ID_TO_EMBED: "${MB_DASHBOARD_ID_TO_EMBED}"
volumes:
- ./metabase/setup.sh:/setup.sh:ro
entrypoint: ["/bin/sh", "/setup.sh"]
Expand All @@ -52,13 +52,13 @@ services:
dockerfile: ./Dockerfile
environment:
PORT: "${SERVER_PORT}"
METABASE_INSTANCE_URL: "http://localhost:${MB_PORT}"
METABASE_JWT_SHARED_SECRET: "${METABASE_JWT_SHARED_SECRET}"
METABASE_STATIC_EMBEDDING_SECRET: "${METABASE_STATIC_EMBEDDING_SECRET}"
METABASE_DASHBOARD_ID_TO_EMBED: "${METABASE_DASHBOARD_ID_TO_EMBED}"
METABASE_ADMIN_EMAIL: "${METABASE_ADMIN_EMAIL}"
METABASE_ADMIN_FIRST_NAME: "${METABASE_ADMIN_FIRST_NAME}"
METABASE_ADMIN_LAST_NAME: "${METABASE_ADMIN_LAST_NAME}"
MB_INSTANCE_URL: "http://localhost:${MB_PORT}"
MB_JWT_SHARED_SECRET: "${MB_JWT_SHARED_SECRET}"
MB_EMBEDDING_SECRET_KEY: "${MB_EMBEDDING_SECRET_KEY}"
MB_DASHBOARD_ID_TO_EMBED: "${MB_DASHBOARD_ID_TO_EMBED}"
MB_ADMIN_EMAIL: "${MB_ADMIN_EMAIL}"
MB_ADMIN_FIRST_NAME: "${MB_ADMIN_FIRST_NAME}"
MB_ADMIN_LAST_NAME: "${MB_ADMIN_LAST_NAME}"
healthcheck:
test: curl --fail -X GET -I "http://localhost:${SERVER_PORT}/" || exit 1
interval: 2s
Expand Down
8 changes: 4 additions & 4 deletions metabase/config.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
version: 1
config:
users:
- first_name: "{{ env METABASE_ADMIN_FIRST_NAME }}"
last_name: "{{ env METABASE_ADMIN_LAST_NAME }}"
password: "{{ env METABASE_ADMIN_PASSWORD }}"
email: "{{ env METABASE_ADMIN_EMAIL }}"
- first_name: "{{ env MB_ADMIN_FIRST_NAME }}"
last_name: "{{ env MB_ADMIN_LAST_NAME }}"
password: "{{ env MB_ADMIN_PASSWORD }}"
email: "{{ env MB_ADMIN_EMAIL }}"
settings:
enable-embedding-simple: true
enable-embedding-static: true
Expand Down
12 changes: 6 additions & 6 deletions metabase/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
set -e

echo "Waiting for Metabase to be ready..."
until curl -sf "${METABASE_INSTANCE_URL}/api/health" > /dev/null 2>&1; do
until curl -sf "${MB_INSTANCE_URL}/api/health" > /dev/null 2>&1; do
sleep 2
done
echo "Metabase is ready!"

echo "Getting session token..."
SESSION_RESPONSE=$(curl -s -X POST \
-H "Content-Type: application/json" \
-d "{\"username\": \"${METABASE_ADMIN_EMAIL}\", \"password\": \"${METABASE_ADMIN_PASSWORD}\"}" \
"${METABASE_INSTANCE_URL}/api/session")
-d "{\"username\": \"${MB_ADMIN_EMAIL}\", \"password\": \"${MB_ADMIN_PASSWORD}\"}" \
"${MB_INSTANCE_URL}/api/session")

SESSION_ID=$(echo "$SESSION_RESPONSE" | sed -n 's/.*"id":"\([^"]*\)".*/\1/p')

Expand All @@ -20,8 +20,8 @@ if [ -z "$SESSION_ID" ]; then
exit 1
fi

echo "Enabling static embedding for dashboard ${METABASE_DASHBOARD_ID_TO_EMBED}..."
RESPONSE=$(curl -s -X PUT "${METABASE_INSTANCE_URL}/api/dashboard/${METABASE_DASHBOARD_ID_TO_EMBED}" \
echo "Enabling static embedding for dashboard ${MB_DASHBOARD_ID_TO_EMBED}..."
RESPONSE=$(curl -s -X PUT "${MB_INSTANCE_URL}/api/dashboard/${MB_DASHBOARD_ID_TO_EMBED}" \
-H "Content-Type: application/json" \
-H "X-Metabase-Session: ${SESSION_ID}" \
-d '{"enable_embedding": true}')
Expand All @@ -31,4 +31,4 @@ if echo "$RESPONSE" | grep -q '"error"'; then
exit 1
fi

echo "Static embedding enabled for dashboard ${METABASE_DASHBOARD_ID_TO_EMBED}!"
echo "Static embedding enabled for dashboard ${MB_DASHBOARD_ID_TO_EMBED}!"
39 changes: 19 additions & 20 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,17 @@ const port = process.env.PORT;

app.use(cors({ credentials: true, origin: true }));

const METABASE_JWT_SHARED_SECRET = process.env.METABASE_JWT_SHARED_SECRET;
const METABASE_STATIC_EMBEDDING_SECRET =
process.env.METABASE_STATIC_EMBEDDING_SECRET;
const MB_JWT_SHARED_SECRET = process.env.MB_JWT_SHARED_SECRET;
const MB_EMBEDDING_SECRET_KEY = process.env.MB_EMBEDDING_SECRET_KEY;

const METABASE_SITE_URL = process.env.METABASE_INSTANCE_URL;
const METABASE_DASHBOARD_ID_TO_EMBED = process.env.METABASE_DASHBOARD_ID_TO_EMBED
? parseInt(process.env.METABASE_DASHBOARD_ID_TO_EMBED)
const MB_SITE_URL = process.env.MB_INSTANCE_URL;
const MB_DASHBOARD_ID_TO_EMBED = process.env.MB_DASHBOARD_ID_TO_EMBED
? parseInt(process.env.MB_DASHBOARD_ID_TO_EMBED)
: 1;

const METABASE_ADMIN_EMAIL = process.env.METABASE_ADMIN_EMAIL || "rene@example.com";
const METABASE_ADMIN_FIRST_NAME = process.env.METABASE_ADMIN_FIRST_NAME || "Rene";
const METABASE_ADMIN_LAST_NAME = process.env.METABASE_ADMIN_LAST_NAME || "Descartes";
const MB_ADMIN_EMAIL = process.env.MB_ADMIN_EMAIL || "rene@example.com";
const MB_ADMIN_FIRST_NAME = process.env.MB_ADMIN_FIRST_NAME || "Rene";
const MB_ADMIN_LAST_NAME = process.env.MB_ADMIN_LAST_NAME || "Descartes";

const JWT_PROVIDER_URI = `http://localhost:${port}/auth/sso`;

Expand Down Expand Up @@ -48,7 +47,7 @@ const getPageLayout = (config, component, currentPath) => {
};
defineMetabaseConfig(${JSON.stringify(config)});
</script>
<script defer src="${METABASE_SITE_URL}/app/embed.js"></script>
<script defer src="${MB_SITE_URL}/app/embed.js"></script>
<nav>
${navLink("/", "Guest Embed")}
${navLink("/sso-embed", "SSO Embed")}
Expand All @@ -68,10 +67,10 @@ app.get("/auth/sso", (req, res) => {
// Example:
// const { user } = req.session;
const user = {
email: METABASE_ADMIN_EMAIL,
firstName: METABASE_ADMIN_FIRST_NAME,
lastName: METABASE_ADMIN_LAST_NAME,
group: 'Customer',
email: MB_ADMIN_EMAIL,
firstName: MB_ADMIN_FIRST_NAME,
lastName: MB_ADMIN_LAST_NAME,
group: "Customer",
};

if (!user) {
Expand All @@ -89,23 +88,23 @@ app.get("/auth/sso", (req, res) => {
exp: Math.round(Date.now() / 1000) + 10 * 60, // 10 minutes
};

const ssoToken = jwt.sign(ssoPayload, METABASE_JWT_SHARED_SECRET);
const ssoToken = jwt.sign(ssoPayload, MB_JWT_SHARED_SECRET);

res.json({ jwt: ssoToken });
});

// Guest Embed - uses signed JWT tokens for anonymous access
app.get(["/", "/guest-embed"], (req, res) => {
const payload = {
resource: { dashboard: METABASE_DASHBOARD_ID_TO_EMBED },
resource: { dashboard: MB_DASHBOARD_ID_TO_EMBED },
params: {},
exp: Math.round(Date.now() / 1000) + 10 * 60, // 10 minutes
};
const token = jwt.sign(payload, METABASE_STATIC_EMBEDDING_SECRET);
const token = jwt.sign(payload, MB_EMBEDDING_SECRET_KEY);

res.send(
getPageLayout(
{ isGuest: true, instanceUrl: METABASE_SITE_URL },
{ isGuest: true, instanceUrl: MB_SITE_URL },
`<metabase-dashboard token="${token}" with-title="true"></metabase-dashboard>`,
"/",
),
Expand All @@ -117,11 +116,11 @@ app.get("/sso-embed", (req, res) => {
res.send(
getPageLayout(
{
instanceUrl: METABASE_SITE_URL,
instanceUrl: MB_SITE_URL,
jwtProviderUri: JWT_PROVIDER_URI,
enableInternalNavigation: true,
},
`<metabase-dashboard dashboard-id="${METABASE_DASHBOARD_ID_TO_EMBED}" with-title="true" with-downloads="false"></metabase-dashboard>`,
`<metabase-dashboard dashboard-id="${MB_DASHBOARD_ID_TO_EMBED}" with-title="true" with-downloads="false"></metabase-dashboard>`,
"/sso-embed",
),
);
Expand Down