O que é incorporação para usuários externos?
Visualização
Este recurso está em Pré-visualização Pública.
Esta página descreve como funciona a incorporação para usuários externos, como configurar seu workspace Databricks para o compartilhamento seguro de painéis incorporados e como usar aplicativos de exemplo para começar. A incorporação para usuários externos utiliza uma entidade de serviço e access tokens com escopo definido para autenticar e autorizar o acesso aos painéis incorporados. Essa abordagem permite compartilhar dashboards com visualizadores externos à sua organização, como parceiros e clientes, sem a necessidade de provisionar uma conta Databricks para esses usuários.
Para saber mais sobre outras opções de incorporação, incluindo a incorporação de painéis para usuários dentro da sua organização, consulte Incorporar um painel.
Como funciona a incorporação para usuários externos
O diagrama e os passos numerados a seguir explicam como os usuários são autenticados e os painéis são preenchidos com resultados específicos para cada usuário quando você incorpora um painel para usuários externos.

- Autenticação e solicitação do usuário: O usuário faz login no seu aplicativo. O frontend da sua aplicação envia uma solicitação autenticada ao seu servidor para obter um access token ao painel de controle.
- Autenticação de entidade de serviço: seu servidor usa o segredo de entidade de serviço para solicitar e receber tokens OAuth do servidor Databricks . Trata-se de um token de amplo escopo que pode acessar todas APIs do painel de controle às quais Databricks tem acesso em nome da entidade de serviço. Seu servidor chama o endpoint
/tokeninfousando esses tokens, passando informações básicas do usuário, comoexternal_viewer_ideexternal_value. Consulte Apresentar painéis de controle de forma segura para usuários individuais. - Geração de tokens com escopo de usuário: Usando a resposta do endpoint
/tokeninfoe do endpoint OpenID Connect (OIDC) do Databricks , seu servidor gera novos tokens com escopo restrito que codificam as informações do usuário que você passou. - Renderização do painel e filtragem de dados: A página do aplicativo instancia
DatabricksDashboarda partir de@databricks/aibi-cliente passa os tokens de escopo do usuário durante a construção. O painel de controle é renderizado de acordo com o contexto do usuário. Este token autoriza o acesso, suporta auditoria comexternal_viewer_ide carregaexternal_valuepara filtragem de dados. As consultas no conjunto de dados do painel podem referenciar__aibi_external_valuepara aplicar filtros por usuário, garantindo que cada visualizador veja apenas os dados que ele tem permissão para view.
O Ask Genie não está disponível em incorporação externa.
O botão "Pergunte Genie não é compatível com incorporação para usuários externos. Se você deseja fornecer recursos de consulta de dados em linguagem natural para usuários externos, use a API Genie Conversation . A API de Conversas permite que você integre a funcionalidade do Genie ao seu aplicativo programaticamente, independentemente da incorporação do painel.
Para dashboards com incorporação básica, o Ask Genie está disponível. Consulte o Ask Genie nos painéis incorporados.
Apresente dashboards de forma segura para usuários individuais.
Configure seu servidor de aplicativos para gerar tokens exclusivos com escopo de usuário para cada usuário com base em seu external_viewer_id. Isso permite rastrear a visualização do painel e o uso por meio logs de auditoria. O external_viewer_id é emparelhado com um external_value, que atua como uma variável global que pode ser inserida em consultas SQL usadas no conjunto de dados do painel. Isso permite filtrar os dados exibidos no painel para cada usuário.
external_viewer_id é enviado para logs de auditoria do seu painel de controle e não deve incluir informações de identificação pessoal. Esse valor também deve ser único para cada usuário.
external_value É utilizado no processamento de consultas e pode incluir informações de identificação pessoal.
O exemplo a seguir demonstra como usar o valor externo como filtro em consultas dataset :
SELECT *
FROM sales
WHERE region = __aibi_external_value
Visão geral da configuração
Esta seção inclui uma visão geral conceitual de alto nível dos passos que você precisa executar para configurar a incorporação de um painel em um local externo.
Para incorporar um painel de controle em um aplicativo externo, primeiro você cria uma entidade de serviço no Databricks e gera um segredo. A entidade de serviço deve ter acesso de leitura ao painel de controle e aos dados subjacentes. Seu servidor usa o segredo da entidade de serviço para recuperar tokens que podem acessar APIs do painel em nome da entidade de serviço. Com esses tokens, o servidor chama o endpoint API /tokeninfo , um endpoint OpenID Connect (OIDC) que retorna informações básicas do perfil do usuário, incluindo os valores external_value e external_viewer_id . Esses valores permitem associar solicitações a usuários individuais.
Utilizando os tokens obtidos da entidade de serviço, seu servidor gera novos tokens específicos para o usuário que está acessando o painel de controle. Esses tokens de escopo de usuário são passados para a página do aplicativo, onde o aplicativo instancia o objeto DatabricksDashboard da biblioteca @databricks/aibi-client . Os tokens contêm informações específicas do usuário que permitem a auditoria e aplicam filtros, de forma que cada usuário veja apenas os dados aos quais está autorizado a acessar. Do ponto de vista do usuário, fazer login no aplicativo fornece acesso automático ao painel integrado com a visibilidade correta dos dados.
Limites de taxa e considerações de desempenho
A incorporação externa tem um limite de 20 carregamentos de painel por segundo. Você pode abrir mais de 20 painéis ao mesmo tempo, mas não mais do que 20 podem começar a carregar simultaneamente.
Pré-requisitos
Para implementar a incorporação externa, certifique-se de atender aos seguintes pré-requisitos:
- Você precisa ter, no mínimo, permissões CAN MANAGE em um painel publicado. Consulte o tutorial: Use dashboards de exemplo para criar e publicar rapidamente um dashboard de exemplo, se necessário.
- Você precisa ter Databricks CLI versão 0.205 ou superior instalado. Consulte a seção Instalar ou atualizar a CLI do Databricks para obter instruções. Para configurar e usar a autenticação OAuth, consulte Autenticação OAuth de usuário para máquina (U2M).
- O administrador workspace deve definir uma lista de domínios aprovados que podem hospedar o painel de controle incorporado. Consulte a seção "Gerenciar incorporação de painel" para obter instruções.
- Um aplicativo externo para hospedar seu painel de controle incorporado. Você pode usar seu próprio aplicativo ou usar os aplicativos de exemplo fornecidos.
o passo 1: Criar uma entidade de serviço
Crie uma entidade de serviço para servir como identidade da sua aplicação externa no Databricks. Esta entidade de serviço autentica solicitações em nome do seu aplicativo.
Para criar uma entidade de serviço:
- Como administrador do workspace, faça login no workspace do Databricks.
- Clique no seu nome de usuário na barra superior do workspace do Databricks e selecione Configurações .
- Clique em Identidade e acesso no painel esquerdo.
- Ao lado de Entidades de serviço , clique em Gerenciar .
- Clique em Adicionar entidade de serviço .
- Clique em Adicionar novo .
- Insira um nome descritivo para a entidade de serviço.
- Clique em Adicionar .
- Abra a entidade de serviço que você acabou de criar na página de listagem de entidades de serviço . Use o campo de filtro para pesquisar pelo nome, se necessário.
- Na página de detalhes da entidade de serviço , registre o ID do aplicativo . Verifique se as caixas de seleção " AcessoDatabricks SQL e " Acesso ao espaço de trabalho" estão marcadas.
o passo 2: Criar um segredo OAuth
Gere um segredo para a entidade de serviço e colete os seguintes valores de configuração, que você precisará para seu aplicativo externo:
- ID da entidade de serviço (cliente)
- Segredo do cliente
A entidade de serviço utiliza um segredo OAuth para verificar sua identidade ao solicitar um access token do seu aplicativo externo.
Para gerar um segredo:
- Clique em Segredos na página de detalhes da entidade de serviço .
- Clique em Gerar segredo .
- Insira o valor da duração da vida para o novo segredo em dias (por exemplo, entre 1 e 730 dias).
- Copie o segredo imediatamente. Você não poderá view este segredo novamente depois de sair desta tela.
o passo 3: Atribua permissões à sua entidade de serviço
A entidade de serviço que você criou funciona como a identidade que fornece acesso ao painel de controle por meio do seu aplicativo. Suas permissões se aplicam somente se o painel não for publicado com permissões de dados compartilhados. Se forem utilizadas permissões de dados compartilhados, as credenciais do editor acessarão os dados. Para obter mais detalhes e recomendações, consulte Incorporando abordagens de autenticação.
- Clique em Painéis na barra lateral workspace para abrir a página com a lista de painéis.
- Clique no nome do painel que deseja incorporar. O painel de controle publicado é aberto.
- Clique em Compartilhar .
- Utilize o campo de entrada de texto na caixa de diálogo de compartilhamento para encontrar sua entidade de serviço e, em seguida, clique nela. Defina o nível de permissão para CAN RUN . Em seguida, clique em Adicionar .
- Anote o ID do painel de controle . Você pode encontrar o ID do painel no URL do painel (por exemplo,
https://<your-workspace-url>/dashboards/<dashboard-id>). Veja os detalhes workspace Databricks.
Se você publicar um painel com permissões de dados individuais, deverá conceder à sua entidade de serviço acesso aos dados usados no painel. O acesso computacional sempre utiliza as credenciais do editor, portanto, você não precisa conceder permissões compute à entidade de serviço.
Para ler e exibir dados, a entidade de serviço deve ter pelo menos SELECT privilégios nas tabelas e visualizações referenciadas no painel. Veja Quem pode gerenciar privilégios?
Etapa 4: Use o aplicativo de exemplo para autenticar e gerar tokens
Utilize um aplicativo de exemplo para praticar a incorporação externa do seu painel de controle. Os aplicativos incluem instruções e código que iniciam a troca de tokens necessária para gerar tokens com escopo definido. Os seguintes blocos de código não possuem dependências. Copie e salve um dos seguintes aplicativos.
- Python
- JavaScript
Copie e salve isto em um arquivo chamado example.py.
#!/usr/bin/env python3
import os
import sys
import json
import base64
import urllib.request
import urllib.parse
from http.server import HTTPServer, BaseHTTPRequestHandler
# -----------------------------------------------------------------------------
# Config
# -----------------------------------------------------------------------------
CONFIG = {
"instance_url": os.environ.get("INSTANCE_URL"),
"dashboard_id": os.environ.get("DASHBOARD_ID"),
"service_principal_id": os.environ.get("SERVICE_PRINCIPAL_ID"),
"service_principal_secret": os.environ.get("SERVICE_PRINCIPAL_SECRET"),
"external_viewer_id": os.environ.get("EXTERNAL_VIEWER_ID"),
"external_value": os.environ.get("EXTERNAL_VALUE"),
"workspace_id": os.environ.get("WORKSPACE_ID"),
"port": int(os.environ.get("PORT", 3000)),
}
basic_auth = base64.b64encode(
f"{CONFIG['service_principal_id']}:{CONFIG['service_principal_secret']}".encode()
).decode()
# -----------------------------------------------------------------------------
# HTTP Request Helper
# -----------------------------------------------------------------------------
def http_request(url, method="GET", headers=None, body=None):
headers = headers or {}
if body is not None and not isinstance(body, (bytes, str)):
raise ValueError("Body must be bytes or str")
req = urllib.request.Request(url, method=method, headers=headers)
if body is not None:
if isinstance(body, str):
body = body.encode()
req.data = body
try:
with urllib.request.urlopen(req) as resp:
data = resp.read().decode()
try:
return {"data": json.loads(data)}
except json.JSONDecodeError:
return {"data": data}
except urllib.error.HTTPError as e:
raise RuntimeError(f"HTTP {e.code}: {e.read().decode()}") from None
# -----------------------------------------------------------------------------
# Token logic
# -----------------------------------------------------------------------------
def get_scoped_token():
# 1. Get all-api token
oidc_res = http_request(
f"{CONFIG['instance_url']}/oidc/v1/token",
method="POST",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": f"Basic {basic_auth}",
},
body=urllib.parse.urlencode({
"grant_type": "client_credentials",
"scope": "all-apis"
})
)
oidc_token = oidc_res["data"]["access_token"]
# 2. Get token info
token_info_url = (
f"{CONFIG['instance_url']}/api/2.0/lakeview/dashboards/"
f"{CONFIG['dashboard_id']}/published/tokeninfo"
f"?external_viewer_id={urllib.parse.quote(CONFIG['external_viewer_id'])}"
f"&external_value={urllib.parse.quote(CONFIG['external_value'])}"
)
token_info = http_request(
token_info_url,
headers={"Authorization": f"Bearer {oidc_token}"}
)["data"]
# 3. Generate scoped token
params = token_info.copy()
authorization_details = params.pop("authorization_details", None)
params.update({
"grant_type": "client_credentials",
"authorization_details": json.dumps(authorization_details)
})
scoped_res = http_request(
f"{CONFIG['instance_url']}/oidc/v1/token",
method="POST",
headers={
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": f"Basic {basic_auth}",
},
body=urllib.parse.urlencode(params)
)
return scoped_res["data"]["access_token"]
# -----------------------------------------------------------------------------
# HTML generator
# -----------------------------------------------------------------------------
def generate_html(token):
return f"""<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Demo</title>
<style>
body {{ font-family: system-ui; margin: 0; padding: 20px; background: #f5f5f5; }}
.container {{ max-width: 1200px; margin: 0 auto; height:calc(100vh - 40px) }}
</style>
</head>
<body>
<div id="dashboard-content" class="container"></div>
<script type="module">
import {{ DatabricksDashboard }} from "https://cdn.jsdelivr.net/npm/@databricks/aibi-client@0.0.0-alpha.7/+esm";
const dashboard = new DatabricksDashboard({{
instanceUrl: "{CONFIG['instance_url']}",
workspaceId: "{CONFIG['workspace_id']}",
dashboardId: "{CONFIG['dashboard_id']}",
token: "{token}",
container: document.getElementById("dashboard-content")
}});
dashboard.initialize();
</script>
</body>
</html>"""
# -----------------------------------------------------------------------------
# HTTP server
# -----------------------------------------------------------------------------
class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path != "/":
self.send_response(404)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write(b"Not Found")
return
try:
token = get_scoped_token()
html = generate_html(token)
status = 200
except Exception as e:
html = f"<h1>Error</h1><p>{e}</p>"
status = 500
self.send_response(status)
self.send_header("Content-Type", "text/html")
self.end_headers()
self.wfile.write(html.encode())
def start_server():
missing = [k for k, v in CONFIG.items() if not v]
if missing:
print(f"Missing: {', '.join(missing)}", file=sys.stderr)
sys.exit(1)
server = HTTPServer(("localhost", CONFIG["port"]), RequestHandler)
print(f":rocket: Server running on http://localhost:{CONFIG['port']}")
try:
server.serve_forever()
except KeyboardInterrupt:
sys.exit(0)
if __name__ == "__main__":
start_server()
Copie e salve isto em um arquivo chamado example.js.
#!/usr/bin/env node
const http = require('http');
const https = require('https');
const { URL, URLSearchParams } = require('url');
// This constant is just a mapping of environment variables to their respective
// values.
const CONFIG = {
instanceUrl: process.env.INSTANCE_URL,
dashboardId: process.env.DASHBOARD_ID,
servicePrincipalId: process.env.SERVICE_PRINCIPAL_ID,
servicePrincipalSecret: process.env.SERVICE_PRINCIPAL_SECRET,
externalViewerId: process.env.EXTERNAL_VIEWER_ID,
externalValue: process.env.EXTERNAL_VALUE,
workspaceId: process.env.WORKSPACE_ID,
port: process.env.PORT || 3000,
};
const basicAuth = Buffer.from(`${CONFIG.servicePrincipalId}:${CONFIG.servicePrincipalSecret}`).toString('base64');
// ------------------------------------------------------------------------------------------------
// Main
// ------------------------------------------------------------------------------------------------
function startServer() {
const missing = Object.keys(CONFIG).filter((key) => !CONFIG[key]);
if (missing.length > 0) throw new Error(`Missing: ${missing.join(', ')}`);
const server = http.createServer(async (req, res) => {
// This is a demo server, we only support GET requests to the root URL.
if (req.method !== 'GET' || req.url !== '/') {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
return;
}
let html = '';
let status = 200;
try {
const token = await getScopedToken();
html = generateHTML(token);
} catch (error) {
html = `<h1>Error</h1><p>${error.message}</p>`;
status = 500;
} finally {
res.writeHead(status, { 'Content-Type': 'text/html' });
res.end(html);
}
});
server.listen(CONFIG.port, () => {
console.log(`🚀 Server running on http://localhost:${CONFIG.port}`);
});
process.on('SIGINT', () => process.exit(0));
process.on('SIGTERM', () => process.exit(0));
}
async function getScopedToken() {
// 1. Get all-api token. This will allow you to access the /tokeninfo
// endpoint, which contains the information required to generate a scoped token
const {
data: { access_token: oidcToken },
} = await httpRequest(`${CONFIG.instanceUrl}/oidc/v1/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${basicAuth}`,
},
body: new URLSearchParams({
grant_type: 'client_credentials',
scope: 'all-apis',
}),
});
// 2. Get token info. This information is **required** for generating a token that is correctly downscoped.
// A correctly downscoped token will only have access to a handful of APIs, and within those APIs, only
// a the specific resources required to render the dashboard.
//
// This is essential to prevent leaking a privileged token.
//
// At the time of writing, OAuth tokens in Databricks are valid for 1 hour.
const tokenInfoUrl = new URL(
`${CONFIG.instanceUrl}/api/2.0/lakeview/dashboards/${CONFIG.dashboardId}/published/tokeninfo`,
);
tokenInfoUrl.searchParams.set('external_viewer_id', CONFIG.externalViewerId);
tokenInfoUrl.searchParams.set('external_value', CONFIG.externalValue);
const { data: tokenInfo } = await httpRequest(tokenInfoUrl.toString(), {
headers: { Authorization: `Bearer ${oidcToken}` },
});
// 3. Generate scoped token. This call is very similar to what was issued before, but now we are providing the scoping to make the generated token
// safe to pass to a browser.
const { authorization_details, ...params } = tokenInfo;
const {
data: { access_token },
} = await httpRequest(`${CONFIG.instanceUrl}/oidc/v1/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${basicAuth}`,
},
body: new URLSearchParams({
grant_type: 'client_credentials',
...params,
authorization_details: JSON.stringify(authorization_details),
}),
});
return access_token;
}
startServer();
// ------------------------------------------------------------------------------------------------
// Helper functions
// ------------------------------------------------------------------------------------------------
/**
* Helper function to create HTTP requests.
* @param {string} url - The URL to make the request to.
* @param {Object} options - The options for the request.
* @param {string} options.method - The HTTP method to use.
* @param {Object} options.headers - The headers to include in the request.
* @param {Object} options.body - The body to include in the request.
* @returns {Promise<Object>} A promise that resolves to the response data.
*/
function httpRequest(url, { method = 'GET', headers = {}, body } = {}) {
return new Promise((resolve, reject) => {
const isHttps = url.startsWith('https://');
const lib = isHttps ? https : http;
const options = new URL(url);
options.method = method;
options.headers = headers;
const req = lib.request(options, (res) => {
let data = '';
res.on('data', (chunk) => (data += chunk));
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
try {
resolve({ data: JSON.parse(data) });
} catch {
resolve({ data });
}
} else {
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
}
});
});
req.on('error', reject);
if (body) {
if (typeof body === 'string' || Buffer.isBuffer(body)) {
req.write(body);
} else if (body instanceof URLSearchParams) {
req.write(body.toString());
} else {
req.write(JSON.stringify(body));
}
}
req.end();
});
}
function generateHTML(token) {
return `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Demo</title>
<style>
body { font-family: system-ui; margin: 0; padding: 20px; background: #f5f5f5; }
.container { max-width: 1200px; margin: 0 auto; height:calc(100vh - 40px) }
</style>
</head>
<body>
<div id="dashboard-content" class="container"></div>
<script type="module">
/**
* We recommend bundling the dependency instead of using a CDN. However, for demonstration purposes,
* we are just using a CDN.
*
* We do not recommend one CDN over another and encourage decoupling the dependency from third-party code.
*/
import { DatabricksDashboard } from "https://cdn.jsdelivr.net/npm/@databricks/aibi-client@0.0.0-alpha.7/+esm";
const dashboard = new DatabricksDashboard({
instanceUrl: "${CONFIG.instanceUrl}",
workspaceId: "${CONFIG.workspaceId}",
dashboardId: "${CONFIG.dashboardId}",
token: "${token}",
container: document.getElementById("dashboard-content")
});
dashboard.initialize();
</script>
</body>
</html>`;
}
o passo 5: execução da aplicação exemplo
Substitua os seguintes valores e, em seguida, execute o bloco de código a partir do seu terminal. Seus valores não devem estar entre colchetes angulares (< >):
-
Utilize o URLworkspace para localizar e substituir os seguintes valores:
<your-instance><workspace_id><dashboard_id>
-
Substitua os seguintes valores pelos valores que você criou ao criar a entidade de serviço (o passo 2):
<service_principal_id><service_principal_secret>(segredo do cliente)
-
Substitua os seguintes valores pelos identificadores associados aos usuários do aplicativo externo:
<some-external-viewer><some-external-value>
-
Substitua
</path/to/example>pelo caminho para o arquivo.pyou.jsque você criou na etapa anterior. Inclua a extensão do arquivo.
Não inclua nenhuma informação de identificação pessoal (PII) no valor EXTERNAL_VIEWER_ID .
INSTANCE_URL='https://<your-instance>.databricks.com' \
WORKSPACE_ID='<workspace_id>' \
DASHBOARD_ID='<dashboard_id>' \
SERVICE_PRINCIPAL_ID='<service-principal-id>' \
SERVICE_PRINCIPAL_SECRET='<service-principal_secret>' \
EXTERNAL_VIEWER_ID='<some-external-viewer>' \
EXTERNAL_VALUE='<some-external-value>' \
~</path/to/example>
# Terminal will output: :rocket: Server running on http://localhost:3000