{"id":39698094,"url":"https://github.com/databricks-solutions/aibi-dashboards-external-embedding","last_synced_at":"2026-01-18T10:18:55.231Z","repository":{"id":322323676,"uuid":"1086054074","full_name":"databricks-solutions/aibi-dashboards-external-embedding","owner":"databricks-solutions","description":null,"archived":false,"fork":false,"pushed_at":"2025-11-11T19:18:56.000Z","size":230,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-11T20:29:29.533Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/databricks-solutions.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS.txt","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE.md","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-29T22:05:19.000Z","updated_at":"2025-11-11T19:19:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/databricks-solutions/aibi-dashboards-external-embedding","commit_stats":null,"previous_names":["databricks-solutions/aibi-dashboards-external-embedding"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/databricks-solutions/aibi-dashboards-external-embedding","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/databricks-solutions%2Faibi-dashboards-external-embedding","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/databricks-solutions%2Faibi-dashboards-external-embedding/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/databricks-solutions%2Faibi-dashboards-external-embedding/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/databricks-solutions%2Faibi-dashboards-external-embedding/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/databricks-solutions","download_url":"https://codeload.github.com/databricks-solutions/aibi-dashboards-external-embedding/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/databricks-solutions%2Faibi-dashboards-external-embedding/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28534316,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T10:13:46.436Z","status":"ssl_error","status_checked_at":"2026-01-18T10:13:11.045Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-18T10:18:52.597Z","updated_at":"2026-01-18T10:18:55.122Z","avatar_url":"https://github.com/databricks-solutions.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AI/BI External Embedding Template\n\nA minimal template showing how to embed Databricks dashboards into external applications with user authentication and row-level security.\n\n\u003cimg src=\"img/Dashboard Page.png\" width=\"600\"\u003e\n\n\n## 📋 What This Template Shows\n\n1. **Backend (Flask)**: Authenticate users and [generate user-specific Databricks OAuth tokens](https://docs.databricks.com/aws/en/dashboards/embedding/external-embed#gsc.tab=0)\n2. **Frontend (React)**: Use the [Databricks embedding SDK](https://www.npmjs.com/package/@databricks/aibi-client) to display dashboards\n3. **Row-Level Security**: Pass user context for data filtering with `__aibi_external_value`\n\n## Project Structure\n\n```\naibi-external-embedding/\n│\n├── backend/                          # Flask Backend\n│   ├── app.py                       # Main application - START HERE\n│   ├── requirements.txt             # Python dependencies\n│   └── .env.example                 # Configuration template\n│\n└── frontend/                         # React Frontend\n    ├── src/\n    │   ├── components/\n    │   │   └── DashboardEmbed.jsx  # Dashboard embedding component\n    │   ├── App.jsx                  # Main React component - START HERE\n    │   ├── App.css                  # Styles\n    │   ├── main.jsx                 # Entry point\n    │   └── index.css                # Global styles\n    ├── index.html                   # HTML template\n    ├── package.json                 # Node dependencies\n    └── vite.config.js               # Dev server config\n```\n\n## How It Works: File Relationships\n\n### 1. User Logs In\n\n\u003cimg src=\"img/Login Page.png\" width=\"600\"\u003e\n\n\n**Frontend** (`frontend/src/App.jsx`)\n```javascript\n// User clicks login → sends username to backend\nconst handleLogin = async (username) =\u003e {\n  await axios.post('/api/auth/login', { username: username })\n}\n```\n\n**Backend** (`backend/app.py`)\n```python\n@app.route('/api/auth/login', methods=['POST'])\ndef login():\n    # Validates username\n    # Creates user session\n    session['user_id'] = user['id']\n    return jsonify({'user': user})\n```\n\n### 2. Frontend Requests Dashboard Configuration\n\n**Frontend** (`frontend/src/App.jsx`)\n```javascript\n// After login, request dashboard config and token\nconst fetchDashboardConfig = async () =\u003e {\n  const response = await axios.get('/api/dashboard/embed-config')\n  // Response includes: workspace_url, dashboard_id, embed_token\n  setDashboardConfig(response.data)\n}\n```\n\n**Backend** (`backend/app.py`)\n```python\n@app.route('/api/dashboard/embed-config')\n@login_required\ndef get_embed_config():\n    # Gets current user from session\n    user = DUMMY_USERS.get(session['username'])\n    \n    # Mints OAuth token for this specific user\n    token_data = mint_databricks_token(user)\n    \n    # Returns dashboard config + token to frontend\n    return jsonify({\n        'workspace_url': os.environ.get('DATABRICKS_WORKSPACE_URL'),\n        'dashboard_id': os.environ.get('DATABRICKS_DASHBOARD_ID'),\n        'embed_token': token_data['access_token'],\n        'user_context': user\n    })\n```\n\n### 3. Token Minting (Key Function)\n\n**Backend** (`backend/app.py`)\n```python\n\n# Create Basic Auth header\nbasic_auth = base64.b64encode(\n    f\"{client_id}:{client_secret}\".encode()\n).decode()\n\ndef mint_databricks_token(user_data):\n    \"\"\"\n    Creates an OAuth token for the authenticated user using \n    the official Databricks 3-step token generation process.\n    \n    Steps:\n    1. Get all-apis token from OIDC endpoint\n    2. Get token info for the dashboard with external user context\n    3. Generate scoped token with authorization details\n\n    The user context enables row-level security (external_value) \n    and access auditing (external_viewer_id) in your dashboards.\n    \"\"\"\n    \n    # Step 1: Get all-apis token\n    oidc_response = requests.post(\n        f\"{workspace_url}/oidc/v1/token\",\n        headers={\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\n            \"Authorization\": f\"Basic {basic_auth}\",\n        },\n        data=urllib.parse.urlencode({\n            \"grant_type\": \"client_credentials\",\n            \"scope\": \"all-apis\"\n        })\n    )\n    oidc_token = oidc_response.json()[\"access_token\"]\n    \n    # Step 2: Get token info for the dashboard with user context\n    # external_viewer_id: unique user identifier for access auditing\n    # external_value: user attributes (e.g., department) for row-level filtering\n    token_info_url = (\n        f\"{workspace_url}/api/2.0/lakeview/dashboards/\"\n        f\"{dashboard_id}/published/tokeninfo\"\n        f\"?external_viewer_id={urllib.parse.quote(user_data['email'])}\"\n        f\"\u0026external_value={urllib.parse.quote(user_data['department'])}\"\n    )\n    \n    token_info_response = requests.get(\n        token_info_url,\n        headers={\"Authorization\": f\"Bearer {oidc_token}\"}\n    )\n    token_info = token_info_response.json()\n    \n    # Step 3: Generate scoped token with authorization details\n    params = token_info.copy()\n    authorization_details = params.pop(\"authorization_details\", None)\n    params.update({\n        \"grant_type\": \"client_credentials\",\n        \"authorization_details\": json.dumps(authorization_details)\n    })\n    \n    scoped_response = requests.post(\n        f\"{workspace_url}/oidc/v1/token\",\n        headers={\n            \"Content-Type\": \"application/x-www-form-urlencoded\",\n            \"Authorization\": f\"Basic {basic_auth}\",\n        },\n        data=urllib.parse.urlencode(params)\n    )\n    scoped_token_data = scoped_response.json()\n    \n    return {\n        'access_token': scoped_token_data['access_token'],\n        'token_type': 'Bearer',\n        'expires_in': scoped_token_data.get('expires_in', 3600),\n        'created_at': int(time.time())\n    }\n```\n\n### 4. Dashboard Embedding\n\n**Frontend** (`frontend/src/components/DashboardEmbed.jsx`)\n```javascript\nimport { DatabricksDashboard } from '@databricks/aibi-client'\n\n// Initialize dashboard with automatic token refresh\nconst dashboard = new DatabricksDashboard({\n  instanceUrl: config.workspace_url,\n  workspaceId: config.workspace_id,\n  dashboardId: config.dashboard_id,\n  token: config.embed_token,\n  container: containerRef.current,\n  \n  // SDK calls this callback ~5 min before token expires\n  getNewToken: async () =\u003e {\n    const response = await axios.get('/api/dashboard/embed-config')\n    return response.data.embed_token\n  }\n})\n\nawait dashboard.initialize()\n```\n\n## 👥 Demo Users\n\n| Username | Department | Purpose |\n|----------|-----------|---------|\n| `alice` | Sales | See sales data |\n| `bob` | Engineering | See engineering data |\n\n## 🚀 Getting Started\n\n**Prerequisites:** Python 3.9+, Node.js 16+, npm\n\n### Step 1: Clone the Repository\n\n```bash\ngit clone https://github.com/databricks-solutions/aibi-dashboards-external-embedding.git\ncd aibi-dashboards-external-embedding\n```\n\n### Step 2: Install Requirements in Virtual Environment\n\n**Backend:**\n```bash\ncd backend\npython3 -m venv venv\nsource venv/bin/activate  # Windows: venv\\Scripts\\activate\npip install -r requirements.txt\ncd ..\n```\n\n**Frontend:**\n```bash\ncd frontend\nnpm install\ncd ..\n```\n\n### Step 3: Create and Configure `.env` File\n\n**Create the file:**\n```bash\ncd backend\ncp .env.example .env  # Windows: copy .env.example .env\n```\n\n**Get the required values:**\n\n1. **DATABRICKS_WORKSPACE_URL**: Your workspace URL (e.g., `https://your-workspace.cloud.databricks.com`)\n   - Copy from your browser address bar when logged into Databricks\n\n2. **DATABRICKS_CLIENT_ID \u0026 DATABRICKS_CLIENT_SECRET**: Service Principal credentials\n   - Go to **Settings** → **Identity and Access** → **Service Principals**\n   - Click **Add Service Principal** → Name it (e.g., \"Dashboard Embedding SP\")\n   - Click **Generate Secret** → Save the Client ID and Client Secret (you won't see the secret again)\n\n3. **DATABRICKS_DASHBOARD_ID**: Your dashboard's unique ID\n   - Open your dashboard in Databricks (make sure it is published)\n   - Copy the ID from the URL: `https://your-workspace.cloud.databricks.com/sql/dashboards/{DASHBOARD_ID}`\n\n4. **DATABRICKS_WORKSPACE_ID**: Your workspace's numeric ID\n   - See official docs: [AWS](https://docs.databricks.com/aws/en/workspace/workspace-details) | [Azure](https://learn.microsoft.com/en-us/azure/databricks/workspace/workspace-details) | [GCP](https://docs.databricks.com/gcp/en/workspace/workspace-details)\n\n**Edit `backend/.env`:**\n```env\nDATABRICKS_WORKSPACE_URL=https://your-workspace.cloud.databricks.com\nDATABRICKS_CLIENT_ID=your-service-principal-client-id\nDATABRICKS_CLIENT_SECRET=your-service-principal-secret\nDATABRICKS_DASHBOARD_ID=your-dashboard-id\nDATABRICKS_WORKSPACE_ID=your-workspace-id\n```\n\n**Grant permissions to Service Principal:**\n\n1. **Dashboard Access:**\n   - Open your dashboard → Click **Share** → Add Service Principal with **Can View** permission\n\n2. **SQL Warehouse Access:**\n   - Go to **SQL Warehouses** → Select your warehouse → Click **Permissions**\n   - Add Service Principal with **Can Use** permission\n\n3. **Unity Catalog Data Access:**\n   - Go to **Data Explorer** → Navigate to your catalog, schema, and tables used in the dashboard\n   - For each level (catalog → schema → table), click **Permissions** tab → Add Service Principal with appropriate access\n\n### Step 4: Customize for Your Dashboard\n\nThis demo uses `department` for row-level security. Customize it for your data:\n\n**A. Update the backend** (`backend/app.py`):\n\n```python\n# Change the DUMMY_USERS to match your data attribute\nDUMMY_USERS = {\n    'alice': {\n        'id': 'user_alice',\n        'name': 'Alice Johnson',\n        'email': 'alice@example.com',\n        'region': 'US-West'  # Changed from 'department' to 'region'\n    },\n    'bob': {\n        'id': 'user_bob',\n        'name': 'Bob Smith',\n        'email': 'bob@example.com',\n        'region': 'US-East'  # Changed from 'department' to 'region'\n    }\n}\n```\n\nFind this line in `mint_databricks_token()` function (~line 94):\n```python\nf\"\u0026external_value={user_data['department']}\"\n```\n\nChange to:\n```python\nf\"\u0026external_value={user_data['region']}\"  # Or whatever attribute you're using\n```\n\n**B. Update your dashboard SQL:**\n\nAdd a `WHERE` clause using `__aibi_external_value`:\n\n```sql\nSELECT * FROM your_table\nWHERE region = __aibi_external_value  -- Filters based on user's attribute\n```\n\n### Step 5: Start the Servers\n\n**Terminal 1 - Backend:**\n```bash\ncd backend\nsource venv/bin/activate  # Windows: venv\\Scripts\\activate\npython app.py\n# Should show: Running on http://127.0.0.1:5000\n```\n\n**Terminal 2 - Frontend:**\n```bash\ncd frontend\nnpm run dev\n# Should show: Local: http://localhost:3000/\n```\n\n**Access:** Open http://localhost:3000 and login as `alice` or `bob`\n\n\n\n## Understanding the Flow\n\n```\n┌─────────────┐         ┌─────────────┐         ┌─────────────┐\n│   Browser   │         │    Flask    │         │ Databricks  │\n│  (React)    │         │   Backend   │         │             │\n└─────────────┘         └─────────────┘         └─────────────┘\n      │                       │                       │\n      │  1. Login (alice)     │                       │\n      │──────────────────────\u003e│                       │\n      │                       │                       │\n      │  2. Session created   │                       │\n      │\u003c──────────────────────│                       │\n      │                       │                       │\n      │  3. Get dashboard     │                       │\n      │     config \u0026 token    │                       │\n      │──────────────────────\u003e│                       │\n      │                       │                       │\n      │                       │  4. Mint OAuth token  │\n      │                       │     for Alice         │\n      │                       │──────────────────────\u003e│\n      │                       │                       │\n      │                       │  5. Return token      │\n      │                       │\u003c──────────────────────│\n      │                       │                       │\n      │  6. Config + token    │                       │\n      │\u003c──────────────────────│                       │\n      │                       │                       │\n      │  7. Embed dashboard   │                       │\n      │     with Alice's      │                       │\n      │     token in iframe   │                       │\n      │──────────────────────────────────────────────\u003e│\n      │                       │                       │\n      │  8. Dashboard renders │                       │\n      │     with Alice's data │                       │\n      │\u003c──────────────────────────────────────────────│\n```\n\n## ❓ Common Questions\n\n**Q: Where is the OAuth token generated?**  \nA: In `backend/app.py`, function `mint_databricks_token()` (line 55) uses the 3-step OAuth flow to generate user-specific tokens.\n\n**Q: How does the frontend get the token?**  \nA: The frontend calls `/api/dashboard/embed-config` which returns the dashboard configuration and a fresh OAuth token (line 216 in `app.py`).\n\n**Q: How is the dashboard embedded?**  \nA: Using the `@databricks/aibi-client` SDK in `frontend/src/components/DashboardEmbed.jsx`. The SDK handles embedding, rendering, and automatic token refresh.\n\n**Q: How does row-level security (RLS) work?**  \nA: The backend passes user context (`external_viewer_id` and `external_value`) when minting tokens:\n```python\n# In mint_databricks_token() - passes department as external_value\ntoken_info_url = (\n    f\"{workspace_url}/api/2.0/lakeview/dashboards/{dashboard_id}/published/tokeninfo\"\n    f\"?external_viewer_id={user_data['email']}\"\n    f\"\u0026external_value={user_data['department']}\"  # e.g., \"Sales\"\n)\n```\n\nIn your Databricks dashboard SQL queries, use `__aibi_external_value` to filter data:\n```sql\nSELECT * FROM sales_data\nWHERE department = __aibi_external_value  -- Returns \"Sales\" for Alice, \"Engineering\" for Bob\n```\n\n**Q: How do I customize this for my own dashboard?**  \nA: See [Step 4: Customize for Your Dashboard](#step-4-customize-for-your-dashboard). You'll need to:\n1. Update `DUMMY_USERS` in `backend/app.py` with your data attribute (e.g., change `department` to `region`)\n2. Update the `external_value` parameter in `mint_databricks_token()`\n3. Add `WHERE your_column = __aibi_external_value` to your dashboard SQL queries\n\n**Q: What files should I read first?**  \nA: Start with:\n- `backend/app.py` - Token minting, authentication, RLS\n- `frontend/src/App.jsx` - Login flow and user management\n- `frontend/src/components/DashboardEmbed.jsx` - Dashboard SDK integration\n- `backend/.env.example` - Required configuration\n\n## Key Dependencies\n\n### Frontend\n- **[@databricks/aibi-client](https://www.npmjs.com/package/@databricks/aibi-client)** (v1.0.0) - Official Databricks SDK for dashboard embedding\n- **React** (v18.2.0) - UI framework\n- **Axios** (v1.6.0) - HTTP client for API calls\n- **Vite** (v5.0.0) - Build tool and dev server\n\n### Backend\n- **Flask** (v3.0.0) - Python web framework\n- **Flask-CORS** (v4.0.0) - CORS support for frontend communication\n- **python-dotenv** (v1.0.0) - Environment variable management\n- **requests** (v2.31.0) - HTTP library for Databricks OAuth API calls\n\n## Next Steps\n\n1. Examine the code to understand the flow\n2. Customize for your use case\n3. Check the [Databricks AI/BI External Embedding](https://docs.databricks.com/aws/en/dashboards/embedding/external-embed#gsc.tab=0) and [Databricks SDK](https://www.npmjs.com/package/@databricks/aibi-client) documentation for more details.\n\n## How to get help\n\nDatabricks 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.\n\n\n## License\n\n\u0026copy; 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.\n\n| Library | Description | License | Source |\n|---------|-------------|---------|--------|\n| [@databricks/aibi-client](https://www.npmjs.com/package/@databricks/aibi-client) | Official Databricks SDK for dashboard embedding | Proprietary | https://www.npmjs.com/package/@databricks/aibi-client |\n| [React](https://reactjs.org/) | JavaScript library for building user interfaces | MIT | https://github.com/facebook/react |\n| [Axios](https://axios-http.com/) | Promise-based HTTP client | MIT | https://github.com/axios/axios |\n| [Vite](https://vitejs.dev/) | Frontend build tool and dev server | MIT | https://github.com/vitejs/vite |\n| [Flask](https://flask.palletsprojects.com/) | Python web framework | BSD-3-Clause | https://github.com/pallets/flask |\n| [Flask-CORS](https://flask-cors.readthedocs.io/) | Cross-origin resource sharing for Flask | MIT | https://github.com/corydolphin/flask-cors |\n| [python-dotenv](https://github.com/theskumar/python-dotenv) | Environment variable management | BSD-3-Clause | https://github.com/theskumar/python-dotenv |\n| [requests](https://requests.readthedocs.io/) | HTTP library for Python | Apache-2.0 | https://github.com/psf/requests |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatabricks-solutions%2Faibi-dashboards-external-embedding","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatabricks-solutions%2Faibi-dashboards-external-embedding","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatabricks-solutions%2Faibi-dashboards-external-embedding/lists"}