Aprenda a incorporar dashboards do Oracle Analytics Cloud em um site público sem expor senhas, client secrets ou tokens estáticos. Neste guia, vou apresentar uma integração segura com OCI Vault, Oracle Functions e API Gateway. Para exibir um dashboard do Oracle Analytics Cloud (OAC) dentro de um portal sem pedir login ao visitante, a página não pode conter senha, client secret nem token fixo. Esses dados ficam na Oracle Cloud Infrastructure (OCI), e o navegador recebe somente um access token temporário, mantido em memória e válido por pouco tempo.

Com esse objetivo, nossa página pública deve solicitar um token ao API Gateway. O Gateway chama uma Oracle Function, que recupera os segredos no OCI Vault e pede o token ao Identity Domain. O token representa um usuário técnico com acesso mínimo ao workbook no OAC.

Arquitetura recomendada

A solução separa a exibição do dashboard da emissão de credenciais temporárias.

  • O visitante acessa uma página web com o dashboard incorporado.
  • A página solicita um token temporário a um endpoint controlado.
  • O API Gateway encaminha a solicitação para uma Oracle Function.
  • A Function obtém segredos protegidos no OCI Vault.
  • A Function solicita um access token ao Identity Domain.
  • O navegador recebe somente o token temporário necessário para carregar o conteúdo.
  • O OAC aplica as permissões associadas ao usuário técnico configurado.

Antes de começar

  • Tenha acesso ao OAC, ao Identity Domain e ao compartment OCI em que os recursos serão criados.
  • Tenha um workbook publicado no OAC e defina quais dados poderão ser exibidos.
  • Use um ambiente de homologação para o primeiro teste, sempre que possível.
  • Crie um usuário técnico exclusivo, com permissão somente de leitura.

Os nomes a seguir são exemplos. Substitua todos os valores entre <...> pelos dados do seu ambiente.

<COMPARTMENT>       Exemplo: ambiente-analytics
<USUARIO_TECNICO>    Exemplo: usuario.dashboard@exemplo.com
<HOST_OAC>           Exemplo: analytics.exemplo.oraclecloud.com
<HOST_GATEWAY>       Host mostrado pelo API Gateway
<PROJECT_PATH>       Caminho copiado da opção Embed do workbook

1. Criar e preparar o usuário técnico

No Console OCI, acesse Identity & Security → Domains → Users. Crie ou selecione um usuário técnico, preencha e-mail, nome e sobrenome e confirme que a conta está ativa.

Depois, entre no OAC com esse usuário e conceda acesso de visualização somente à pasta e ao workbook que serão incorporados. Faça um teste: ele deve abrir o workbook diretamente no OAC. O token não cria permissões novas; ele usa exatamente os acessos desse usuário.

2. Criar a aplicação OAuth

No Console OCI, acesse Identity & Security → Domains → <SEU_DOMAIN> → Integrated applications. Crie uma Confidential Application, por exemplo app-dashboard-token.

  1. Ative a aplicação após criá-la.
  2. Na aba OAuth configuration, habilite OAuth.
  3. Habilite o fluxo de autenticação aprovado para o seu ambiente, como Resource Owner quando aplicável.
  4. Selecione o recurso do Analytics e copie o scope apresentado.
  5. Registre a URL do token, o Client ID e o Client Secret.

A Function precisará destes valores não secretos:

IDCS_TOKEN_URL=https://<SEU_DOMAIN>.identity.oraclecloud.com/oauth2/v1/token
OAC_CLIENT_ID=<CLIENT_ID_DA_APLICACAO>
OAC_SCOPE=<SCOPE_DO_RECURSO_ANALYTICS>
OAC_USERNAME=<USUARIO_TECNICO>

O client secret e a senha do usuário técnico também serão necessários, mas ficarão no Vault, nunca no HTML ou no repositório.

3. Autorizar o domínio do portal no OAC

No OAC, acesse Navigator → Console → Safe Domains. Adicione a origem do site que exibirá o dashboard e marque Embedding.

Informe a origem exata, com protocolo e porta. Para teste local: http://127.0.0.1:5500. Para produção: https://portal.exemplo.com. Não inclua index.html nem outro caminho na origem cadastrada.

4. Copiar as informações do workbook

Abra o workbook no OAC em modo de edição e localize a opção Embed. Copie o project-path, o active-page e o active-tab-id. Esses valores identificam o conteúdo que será carregado.

<oracle-dv
  project-path="<PROJECT_PATH>"
  active-page="<ACTIVE_PAGE>"
  active-tab-id="<ACTIVE_TAB_ID>">
</oracle-dv>

5. Guardar os segredos no OCI Vault

No Console OCI, acesse Identity & Security → Vault. Crie ou reutilize um Vault e uma chave mestra. Em Secrets, crie dois secrets separados:

  • secret-dashboard-client-secret: contém o client secret OAuth.
  • secret-dashboard-user-password: contém a senha do usuário técnico.

Após criá-los, copie apenas os OCIDs dos secrets. O OCID é uma referência que pode ser usada na configuração da Function; o conteúdo secreto não deve aparecer em arquivos, variáveis abertas ou logs.

6. Criar a Oracle Function de token

Em Developer Services → Functions, crie uma Application e uma Function, por exemplo fn-dashboard-token. A Function deve recuperar os dois secrets no Vault usando Resource Principal, chamar a URL OAuth e responder um JSON com access_token, token_type e expires_in.

A Application da Function deve ter saída HTTPS para o OCI Vault e o Identity Domain. Se ela estiver em uma subnet privada, configure uma rota 0.0.0.0/0 para um NAT Gateway e uma regra de saída TCP na porta 443. Sem essa conectividade, a Function não conseguirá ler os secrets nem solicitar o token.

Na aba Configuration da Function, informe apenas valores não secretos e OCIDs:

IDCS_TOKEN_URL=https://<SEU_DOMAIN>.identity.oraclecloud.com/oauth2/v1/token
OAC_CLIENT_ID=<CLIENT_ID_DA_APLICACAO>
OAC_SCOPE=<SCOPE_DO_RECURSO_ANALYTICS>
OAC_USERNAME=<USUARIO_TECNICO>
OAC_CLIENT_SECRET_SECRET_OCID=<OCID_DO_SECRET_CLIENT_SECRET>
OAC_USER_PASSWORD_SECRET_OCID=<OCID_DO_SECRET_SENHA>
OAC_TOKEN_EXPIRY_SECONDS=300

OAC_TOKEN_EXPIRY_SECONDS é opcional e define, em segundos, a validade solicitada para o token; use um valor inteiro de pelo menos 60, como 300. Não use variáveis como OAC_CLIENT_SECRET ou OAC_USER_PASSWORD com valores reais. A leitura deve acontecer apenas em memória, durante a execução da Function.

Código da Function (func.js)

Substitua o conteúdo padrão da Function pelo código abaixo. Ele usa o Resource Principal para ler os dois secrets no OCI Vault, sem gravar senha, client secret ou token nos logs.

const fdk = require('@fnproject/fdk');
const fetch = require('node-fetch');
const common = require('oci-common');
const secrets = require('oci-secrets');

async function getSecretValue(secretsClient, secretId) {
  const response = await secretsClient.getSecretBundle({ secretId });
  const encodedValue =
    response.secretBundle.secretBundleContent.content;

  if (!encodedValue) {
    throw new Error('Vault returned an empty secret bundle.');
  }

  return Buffer.from(encodedValue, 'base64').toString('utf8');
}

fdk.handle(async () => {
  const required = [
    'IDCS_TOKEN_URL',
    'OAC_SCOPE',
    'OAC_USERNAME',
    'OAC_CLIENT_ID',
    'OAC_CLIENT_SECRET_SECRET_OCID',
    'OAC_USER_PASSWORD_SECRET_OCID'
  ];

  const missing = required.filter((key) => !process.env[key]);
  if (missing.length) {
    console.error(`Missing configuration: ${missing.join(', ')}`);
    return { error: 'Function configuration is incomplete.' };
  }

  const tokenExpirySeconds = process.env.OAC_TOKEN_EXPIRY_SECONDS;
  if (
    tokenExpirySeconds &&
    (!/^\d+$/.test(tokenExpirySeconds) || Number(tokenExpirySeconds) < 60)
  ) {
    console.error('OAC_TOKEN_EXPIRY_SECONDS must be an integer of at least 60.');
    return { error: 'Token expiration configuration is invalid.' };
  }

  try {
    const provider =
      await common.ResourcePrincipalAuthenticationDetailsProvider.builder();

    const secretsClient = new secrets.SecretsClient({
      authenticationDetailsProvider: provider
    });

    const [clientSecret, userPassword] = await Promise.all([
      getSecretValue(
        secretsClient,
        process.env.OAC_CLIENT_SECRET_SECRET_OCID
      ),
      getSecretValue(
        secretsClient,
        process.env.OAC_USER_PASSWORD_SECRET_OCID
      )
    ]);

    const tokenExpiryScope = tokenExpirySeconds
      ? ` urn:opc:resource:expiry=${tokenExpirySeconds}`
      : '';

    const form = new URLSearchParams({
      grant_type: 'password',
      username: process.env.OAC_USERNAME,
      password: userPassword,
      scope: `${process.env.OAC_SCOPE}${tokenExpiryScope}`
    });

    const basicAuth = Buffer.from(
      `${process.env.OAC_CLIENT_ID}:${clientSecret}`
    ).toString('base64');

    const response = await fetch(process.env.IDCS_TOKEN_URL, {
      method: 'POST',
      headers: {
        Authorization: `Basic ${basicAuth}`,
        'Content-Type':
          'application/x-www-form-urlencoded;charset=UTF-8'
      },
      body: form.toString()
    });

    const data = await response.json();

    if (!response.ok) {
      console.error(
        `Identity Domain returned HTTP ${response.status}: ${data.error}`
      );
      return { error: 'Unable to generate OAC token.' };
    }

    return {
      access_token: data.access_token,
      token_type: data.token_type,
      expires_in: data.expires_in
    };
  } catch {
    console.error('Token service failed.');
    return { error: 'Token service is unavailable.' };
  }
});

No package.json, preserve os demais campos criados pelo template e inclua as dependências oci-common e oci-secrets, além de @fnproject/fdk e node-fetch.

{
  "dependencies": {
    "@fnproject/fdk": "^0.0.72",
    "node-fetch": "^2.6.12",
    "oci-common": "^2.137.1",
    "oci-secrets": "^2.137.1"
  }
}

Salve func.js e package.json e publique a nova imagem da Function. Editar os arquivos no Code Editor não atualiza a Function em execução.

fn -v deploy --app <NOME_DA_APPLICATION>

7. Permitir que a Function leia somente os secrets necessários

Crie um Dynamic Group em Identity & Security → Domains → <SEU_DOMAIN> → Dynamic Groups, restrito à Function criada. Depois, em Identity & Security → Policies, crie uma policy que permita a leitura apenas dos dois secrets usados pela Function.

Nome: dg-fn-dashboard-token-vault

Matching rule:
resource.id = '<OCID_DA_FUNCTION>'

Exemplo conceitual; substitua os marcadores:

Allow dynamic-group <DYNAMIC_GROUP_DA_FUNCTION>
to read secret-bundles in compartment <COMPARTMENT>
where target.secret.id = '<OCID_DO_SECRET_CLIENT_SECRET>'

Allow dynamic-group <DYNAMIC_GROUP_DA_FUNCTION>
to read secret-bundles in compartment <COMPARTMENT>
where target.secret.id = '<OCID_DO_SECRET_SENHA>'

Dynamic Groups e policies podem levar alguns minutos para propagar. Se a Function falhar ao ler o Vault logo após a mudança, aguarde antes de alterar o código.

8. Publicar a Function no API Gateway

Em Identity & Security → Policies, crie antes uma policy que permita ao API Gateway invocar Functions:

ALLOW any-user to use functions-family in compartment <COMPARTMENT>
where ALL {
  request.principal.type = 'ApiGateway',
  request.resource.compartment.id = '<OCID_DO_COMPARTMENT>'
}

Depois, acesse Developer Services → API Gateway → Gateways e crie um Gateway público. Quando ele estiver ativo, abra Deployments e crie uma rota com estes valores:

  • Path prefix: /dashboard-publico
  • Route path: /token
  • Method: GET
  • Backend type: Oracle Functions
  • Backend: a Application e a Function criadas na etapa anterior

A URL final ficará semelhante a https://<HOST_GATEWAY>/dashboard-publico/token. No CORS, autorize somente a origem do portal, por exemplo https://portal.exemplo.com, e permita GET. Configure também rate limiting no nível do deployment — não é configurado por rota — usando a chave CLIENT_IP e um limite inicial compatível com o volume esperado, por exemplo 5 a 10 requisições por segundo.

9. Inserir o código na página que exibirá o dashboard

Este exemplo carrega o framework do OAC, obtém um token do Gateway e o entrega ao componente. Substitua todos os marcadores antes de publicar.

<script
  src="https://<HOST_OAC>/public/dv/v1/embedding/standalone/embedding.js?TOKEN=true"
  type="application/javascript">
</script>

<oracle-dv
  project-path="<PROJECT_PATH>"
  active-page="<ACTIVE_PAGE>"
  active-tab-id="<ACTIVE_TAB_ID>">
</oracle-dv>

<script>
  const gatewayTokenUrl =
    'https://<HOST_GATEWAY>/dashboard-publico/token';

  async function getOacToken() {
    const response = await fetch(gatewayTokenUrl, {
      method: 'GET',
      cache: 'no-store'
    });

    if (!response.ok) {
      throw new Error(`Falha ao obter token: ${response.status}`);
    }

    const data = await response.json();
    if (!data.access_token) {
      throw new Error('A resposta não contém access_token.');
    }
    return data.access_token;
  }

  async function initializeOac() {
    const token = await getOacToken();

    requirejs([
      'jquery', 'knockout', 'obitech-application/application',
      'ojs/ojcore', 'ojs/ojknockout', 'ojs/ojcomposite',
      'jet-composites/oracle-dv/loader'
    ], function ($, ko, application) {
      application.setSecurityConfig('token', {
        tokenAuthFunction: function () {
          return token;
        }
      });
      ko.applyBindings();
    });
  }

  initializeOac().catch(console.error);
</script>

tokenAuthFunction deve retornar uma string, não uma Promise. Por isso, o exemplo busca o token antes de configurar o componente.

10. Validar antes de publicar

  • Abra a página por HTTP ou HTTPS; não use file://.
  • Confirme que o workbook aparece sem tela de login.
  • Verifique se o domínio está cadastrado tanto em Safe Domains quanto no CORS.
  • Teste filtros e visualizações com as permissões do usuário técnico.
  • Confirme que HTML, repositório e logs não contêm senha, client secret ou token real.
  • Se houver erro 401, 403 ou 5xx, valide Function, policy do Gateway, usuário técnico e OAuth nessa ordem.

Referências