A minimal template showing how to embed Databricks dashboards into external applications with user authentication and row-level security.
- Backend (Flask): Authenticate users and generate user-specific Databricks OAuth tokens
- Frontend (React): Use the Databricks embedding SDK to display dashboards
- Row-Level Security: Pass user context for data filtering with
__aibi_external_value
aibi-external-embedding/
β
βββ backend/ # Flask Backend
β βββ app.py # Main application - START HERE
β βββ requirements.txt # Python dependencies
β βββ .env.example # Configuration template
β
βββ frontend/ # React Frontend
βββ src/
β βββ components/
β β βββ DashboardEmbed.jsx # Dashboard embedding component
β βββ App.jsx # Main React component - START HERE
β βββ App.css # Styles
β βββ main.jsx # Entry point
β βββ index.css # Global styles
βββ index.html # HTML template
βββ package.json # Node dependencies
βββ vite.config.js # Dev server config
Frontend (frontend/src/App.jsx)
// User clicks login β sends username to backend
const handleLogin = async (username) => {
await axios.post('/api/auth/login', { username: username })
}Backend (backend/app.py)
@app.route('/api/auth/login', methods=['POST'])
def login():
# Validates username
# Creates user session
session['user_id'] = user['id']
return jsonify({'user': user})Frontend (frontend/src/App.jsx)
// After login, request dashboard config and token
const fetchDashboardConfig = async () => {
const response = await axios.get('/api/dashboard/embed-config')
// Response includes: workspace_url, dashboard_id, embed_token
setDashboardConfig(response.data)
}Backend (backend/app.py)
@app.route('/api/dashboard/embed-config')
@login_required
def get_embed_config():
# Gets current user from session
user = DUMMY_USERS.get(session['username'])
# Mints OAuth token for this specific user
token_data = mint_databricks_token(user)
# Returns dashboard config + token to frontend
return jsonify({
'workspace_url': os.environ.get('DATABRICKS_WORKSPACE_URL'),
'dashboard_id': os.environ.get('DATABRICKS_DASHBOARD_ID'),
'embed_token': token_data['access_token'],
'user_context': user
})Backend (backend/app.py)
# Create Basic Auth header
basic_auth = base64.b64encode(
f"{client_id}:{client_secret}".encode()
).decode()
def mint_databricks_token(user_data):
"""
Creates an OAuth token for the authenticated user using
the official Databricks 3-step token generation process.
Steps:
1. Get all-apis token from OIDC endpoint
2. Get token info for the dashboard with external user context
3. Generate scoped token with authorization details
The user context enables row-level security (external_value)
and access auditing (external_viewer_id) in your dashboards.
"""
# Step 1: Get all-apis token
oidc_response = requests.post(
f"{workspace_url}/oidc/v1/token",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": f"Basic {basic_auth}",
},
data=urllib.parse.urlencode({
"grant_type": "client_credentials",
"scope": "all-apis"
})
)
oidc_token = oidc_response.json()["access_token"]
# Step 2: Get token info for the dashboard with user context
# external_viewer_id: unique user identifier for access auditing
# external_value: user attributes (e.g., department) for row-level filtering
token_info_url = (
f"{workspace_url}/api/2.0/lakeview/dashboards/"
f"{dashboard_id}/published/tokeninfo"
f"?external_viewer_id={urllib.parse.quote(user_data['email'])}"
f"&external_value={urllib.parse.quote(user_data['department'])}"
)
token_info_response = requests.get(
token_info_url,
headers={"Authorization": f"Bearer {oidc_token}"}
)
token_info = token_info_response.json()
# Step 3: Generate scoped token with authorization details
params = token_info.copy()
authorization_details = params.pop("authorization_details", None)
params.update({
"grant_type": "client_credentials",
"authorization_details": json.dumps(authorization_details)
})
scoped_response = requests.post(
f"{workspace_url}/oidc/v1/token",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": f"Basic {basic_auth}",
},
data=urllib.parse.urlencode(params)
)
scoped_token_data = scoped_response.json()
return {
'access_token': scoped_token_data['access_token'],
'token_type': 'Bearer',
'expires_in': scoped_token_data.get('expires_in', 3600),
'created_at': int(time.time())
}Frontend (frontend/src/components/DashboardEmbed.jsx)
import { DatabricksDashboard } from '@databricks/aibi-client'
// Initialize dashboard with automatic token refresh
const dashboard = new DatabricksDashboard({
instanceUrl: config.workspace_url,
workspaceId: config.workspace_id,
dashboardId: config.dashboard_id,
token: config.embed_token,
container: containerRef.current,
// SDK calls this callback ~5 min before token expires
getNewToken: async () => {
const response = await axios.get('/api/dashboard/embed-config')
return response.data.embed_token
}
})
await dashboard.initialize()| Username | Department | Purpose |
|---|---|---|
alice |
Sales | See sales data |
bob |
Engineering | See engineering data |
Prerequisites: Python 3.9+, Node.js 16+, npm
git clone https://github.com/databricks-solutions/aibi-dashboards-external-embedding.git
cd aibi-dashboards-external-embeddingBackend:
cd backend
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
cd ..Frontend:
cd frontend
npm install
cd ..Create the file:
cd backend
cp .env.example .env # Windows: copy .env.example .envGet the required values:
-
DATABRICKS_WORKSPACE_URL: Your workspace URL (e.g.,
https://your-workspace.cloud.databricks.com)- Copy from your browser address bar when logged into Databricks
-
DATABRICKS_CLIENT_ID & DATABRICKS_CLIENT_SECRET: Service Principal credentials
- Go to Settings β Identity and Access β Service Principals
- Click Add Service Principal β Name it (e.g., "Dashboard Embedding SP")
- Click Generate Secret β Save the Client ID and Client Secret (you won't see the secret again)
-
DATABRICKS_DASHBOARD_ID: Your dashboard's unique ID
- Open your dashboard in Databricks (make sure it is published)
- Copy the ID from the URL:
https://your-workspace.cloud.databricks.com/sql/dashboards/{DASHBOARD_ID}
-
DATABRICKS_WORKSPACE_ID: Your workspace's numeric ID
Edit backend/.env:
DATABRICKS_WORKSPACE_URL=https://your-workspace.cloud.databricks.com
DATABRICKS_CLIENT_ID=your-service-principal-client-id
DATABRICKS_CLIENT_SECRET=your-service-principal-secret
DATABRICKS_DASHBOARD_ID=your-dashboard-id
DATABRICKS_WORKSPACE_ID=your-workspace-idGrant permissions to Service Principal:
-
Dashboard Access:
- Open your dashboard β Click Share β Add Service Principal with Can View permission
-
SQL Warehouse Access:
- Go to SQL Warehouses β Select your warehouse β Click Permissions
- Add Service Principal with Can Use permission
-
Unity Catalog Data Access:
- Go to Data Explorer β Navigate to your catalog, schema, and tables used in the dashboard
- For each level (catalog β schema β table), click Permissions tab β Add Service Principal with appropriate access
This demo uses department for row-level security. Customize it for your data:
A. Update the backend (backend/app.py):
# Change the DUMMY_USERS to match your data attribute
DUMMY_USERS = {
'alice': {
'id': 'user_alice',
'name': 'Alice Johnson',
'email': 'alice@example.com',
'region': 'US-West' # Changed from 'department' to 'region'
},
'bob': {
'id': 'user_bob',
'name': 'Bob Smith',
'email': 'bob@example.com',
'region': 'US-East' # Changed from 'department' to 'region'
}
}Find this line in mint_databricks_token() function (~line 94):
f"&external_value={user_data['department']}"Change to:
f"&external_value={user_data['region']}" # Or whatever attribute you're usingB. Update your dashboard SQL:
Add a WHERE clause using __aibi_external_value:
SELECT * FROM your_table
WHERE region = __aibi_external_value -- Filters based on user's attributeTerminal 1 - Backend:
cd backend
source venv/bin/activate # Windows: venv\Scripts\activate
python app.py
# Should show: Running on http://127.0.0.1:5000Terminal 2 - Frontend:
cd frontend
npm run dev
# Should show: Local: http://localhost:3000/Access: Open http://localhost:3000 and login as alice or bob
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β Browser β β Flask β β Databricks β
β (React) β β Backend β β β
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β β β
β 1. Login (alice) β β
βββββββββββββββββββββββ>β β
β β β
β 2. Session created β β
β<βββββββββββββββββββββββ β
β β β
β 3. Get dashboard β β
β config & token β β
βββββββββββββββββββββββ>β β
β β β
β β 4. Mint OAuth token β
β β for Alice β
β βββββββββββββββββββββββ>β
β β β
β β 5. Return token β
β β<βββββββββββββββββββββββ
β β β
β 6. Config + token β β
β<βββββββββββββββββββββββ β
β β β
β 7. Embed dashboard β β
β with Alice's β β
β token in iframe β β
βββββββββββββββββββββββββββββββββββββββββββββββ>β
β β β
β 8. Dashboard renders β β
β with Alice's data β β
β<βββββββββββββββββββββββββββββββββββββββββββββββ
Q: Where is the OAuth token generated?
A: In backend/app.py, function mint_databricks_token() (line 55) uses the 3-step OAuth flow to generate user-specific tokens.
Q: How does the frontend get the token?
A: The frontend calls /api/dashboard/embed-config which returns the dashboard configuration and a fresh OAuth token (line 216 in app.py).
Q: How is the dashboard embedded?
A: Using the @databricks/aibi-client SDK in frontend/src/components/DashboardEmbed.jsx. The SDK handles embedding, rendering, and automatic token refresh.
Q: How does row-level security (RLS) work?
A: The backend passes user context (external_viewer_id and external_value) when minting tokens:
# In mint_databricks_token() - passes department as external_value
token_info_url = (
f"{workspace_url}/api/2.0/lakeview/dashboards/{dashboard_id}/published/tokeninfo"
f"?external_viewer_id={user_data['email']}"
f"&external_value={user_data['department']}" # e.g., "Sales"
)In your Databricks dashboard SQL queries, use __aibi_external_value to filter data:
SELECT * FROM sales_data
WHERE department = __aibi_external_value -- Returns "Sales" for Alice, "Engineering" for BobQ: How do I customize this for my own dashboard?
A: See Step 4: Customize for Your Dashboard. You'll need to:
- Update
DUMMY_USERSinbackend/app.pywith your data attribute (e.g., changedepartmenttoregion) - Update the
external_valueparameter inmint_databricks_token() - Add
WHERE your_column = __aibi_external_valueto your dashboard SQL queries
Q: What files should I read first?
A: Start with:
backend/app.py- Token minting, authentication, RLSfrontend/src/App.jsx- Login flow and user managementfrontend/src/components/DashboardEmbed.jsx- Dashboard SDK integrationbackend/.env.example- Required configuration
- @databricks/aibi-client (v1.0.0) - Official Databricks SDK for dashboard embedding
- React (v18.2.0) - UI framework
- Axios (v1.6.0) - HTTP client for API calls
- Vite (v5.0.0) - Build tool and dev server
- Flask (v3.0.0) - Python web framework
- Flask-CORS (v4.0.0) - CORS support for frontend communication
- python-dotenv (v1.0.0) - Environment variable management
- requests (v2.31.0) - HTTP library for Databricks OAuth API calls
- Examine the code to understand the flow
- Customize for your use case
- Check the Databricks AI/BI External Embedding and Databricks SDK documentation for more details.
Databricks support doesn't cover this content. For questions or bugs, please open a GitHub issue and the team will help on a best effort basis.
Β© 2025 Databricks, Inc. All rights reserved. The source in this notebook is provided subject to the Databricks License [https://databricks.com/db-license-source]. All included or referenced third party libraries are subject to the licenses set forth below.
| Library | Description | License | Source |
|---|---|---|---|
| @databricks/aibi-client | Official Databricks SDK for dashboard embedding | Proprietary | https://www.npmjs.com/package/@databricks/aibi-client |
| React | JavaScript library for building user interfaces | MIT | https://github.com/facebook/react |
| Axios | Promise-based HTTP client | MIT | https://github.com/axios/axios |
| Vite | Frontend build tool and dev server | MIT | https://github.com/vitejs/vite |
| Flask | Python web framework | BSD-3-Clause | https://github.com/pallets/flask |
| Flask-CORS | Cross-origin resource sharing for Flask | MIT | https://github.com/corydolphin/flask-cors |
| python-dotenv | Environment variable management | BSD-3-Clause | https://github.com/theskumar/python-dotenv |
| requests | HTTP library for Python | Apache-2.0 | https://github.com/psf/requests |

