Nesta modalidade de autenticação, a interface completa da aplicação é disponibilizada ao cliente. Qualquer restrição visual ou de regra de negócio ocorre em conformidade com as configurações predefinidas para os diferentes perfis de usuários. O fluxo operacional assemelha-se ao cenário em que o próprio usuário insere a URL da aplicação no navegador e utiliza o formulário de login convencional.


Contrato de Integração de Relatórios do Servidor de Relatórios (Lado Servidor)

Objetivo

Este documento descreve o contrato de integração determinístico usado por sistemas externos para autenticar um usuário mapeado e abrir o ReportServer.

Não confundir: ExternalAuth x ReportIntegration

Este contrato cobre apenas o fluxo app-app de bootstrap de integração.

  • ReportIntegration (este documento): autenticação entre sistemas via API key + token temporário de integração.
  • ExternalAuth: autenticação de credenciais do usuário (username/senha) no login direto do ReportServer.

Importante:

  • No endpoint /api/integration/bootstrap-report-session, não há validação de senha do usuário.
  • A validação do bootstrap usa ReportIntegration:RequestApiKey* e BootstrapTokenSigningKey.

Resumo do Fluxo

  1. O sistema cliente envia uma requisição de bootstrap server-to-server.
  2. O ReportServer valida a chave de integração compartilhada e o payload da requisição.
  3. O ReportServer gera um token assinado, de curta duração e uso único.
  4. O ReportServer retorna uma URL de abertura contendo o token.
  5. O navegador abre /integration/entry com token e returnUrl opcional.
  6. IntegrationEntry consome o token, autentica o usuário mapeado e redireciona para o ReportServer completo.
  7. O usuário continua usando os recursos padrão de relatório, já protegidos por papéis e permissões do ReportServer.

Endpoint de Bootstrap

  • Método: POST
  • Rota: /api/integration/bootstrap-report-session
  • Content-Type: application/json

Header obrigatório

  • O nome do header é configurável por ReportIntegration:RequestApiKeyHeaderName.
  • Nome de header padrão configurado: X-Report-Integration-Key.
  • O valor do header deve corresponder a ReportIntegration:RequestApiKey.
  • Essa chave é um segredo compartilhado entre o AdhocReport e o servidor de autorização do cliente.

Corpo da Requisição

{
  "externalUserId": "external-user-id",
  "clientSystemId": "client-system-id",
  "returnUrl": "/optional-relative-return"
}

Regras dos campos

  • externalUserId: obrigatório, deve mapear para um usuário existente no ReportServer (ApplicationUser.ExternalUserId).
  • clientSystemId: obrigatório, identifica o sistema chamador.
  • returnUrl: opcional, quando informado deve ser um caminho relativo iniciando com /.

Resposta de Sucesso

HTTP 200 OK

{
  "success": true,
  "message": "ok",
  "launchUrl": "https://server/integration/entry?token=...",
  "expiresAtUtc": "2026-06-02T18:30:00Z",
  "reference": "trace-or-correlation-id"
}

Respostas de Erro

  • 400 BadRequest
    • invalid-request
    • invalid-bootstrap-payload
    • invalid-return-url
  • 401 Unauthorized
    • invalid-integration-key
  • 403 Forbidden
    • report-integration-disabled
  • 404 NotFound
    • external-user-not-found
  • 500 InternalServerError
    • report-integration-misconfigured

Todos os payloads de erro seguem:

{
  "success": false,
  "message": "error-code",
  "launchUrl": null,
  "expiresAtUtc": null,
  "reference": "trace-or-correlation-id"
}

Comportamento do Token

  • O payload do token contém:
    • externalUserId
    • clientSystemId
    • tokenId
    • issuedAtUtc
    • expiresAtUtc
  • Algoritmo de assinatura: HMACSHA256 com ReportIntegration:BootstrapTokenSigningKey.
  • O token é de curta duração (BootstrapTokenLifetimeSeconds).
  • O token é de uso único (replay é bloqueado com cache em memória).
  • Falhas de validação incluem:
    • missing-token
    • invalid-token-format
    • invalid-token-signature
    • invalid-token-payload
    • expired-token
    • token-already-used
    • report-integration-misconfigured

Configuração Obrigatória

Em appsettings / ambiente:

  • ReportIntegration:Enabled
  • ReportIntegration:RequestApiKeyHeaderName
  • ReportIntegration:RequestApiKey
  • ReportIntegration:BootstrapTokenLifetimeSeconds
  • ReportIntegration:BootstrapTokenSigningKey
  • ReportIntegration:IntegrationReportsRoute

Configuração típica (desenvolvimento):

"ReportIntegration": {
  "Enabled": true,
  "RequestApiKeyHeaderName": "X-Report-Integration-Key",
  "RequestApiKey": "dev-report-integration-key",
  "BootstrapTokenLifetimeSeconds": 120,
  "BootstrapTokenSigningKey": "dev-bootstrap-signing-key-change-in-production",
  "IntegrationReportsRoute": "/integration/entry",
  "Cors": {
    "AllowedOrigins": [
      "https://localhost:7227",
      "http://localhost:5148"
    ]
  }
}

Observações:

  • BootstrapTokenSigningKey é lida de configuração. Se ausente, o sistema falha com report-integration-misconfigured.
  • Cors.AllowedOrigins é necessário quando o cliente chama o bootstrap em contexto cross-origin.

Notas de Segurança

  • Não exponha a chave compartilhada nem a chave de assinatura em código client-side.
  • Mantenha BootstrapTokenSigningKey obrigatório; não use valores de fallback hardcoded.
  • Mantenha todas as chamadas em HTTPS.
  • Use o reference da resposta para correlacionar erros visíveis ao usuário com logs do servidor.

Integração Mínima do Cliente (Guia Enxuto)

O cliente externo só precisa de duas ações:

  1. Chamar o endpoint de bootstrap (server-to-server).
  2. Abrir a launchUrl retornada no navegador.

O ReportIntegration.BlazorTestClient é um app de diagnóstico e não é obrigatório para integração de produção do cliente.

Exemplo cURL

curl -X POST "https://localhost:7019/api/integration/bootstrap-report-session" \
  -H "Content-Type: application/json" \
  -H "X-Report-Integration-Key: dev-report-integration-key" \
  -d '{"externalUserId":"ext-usr-001","clientSystemId":"client-system-demo","returnUrl":"/"}'
Exemplo JavaScript
const response = await fetch("https://localhost:7019/api/integration/bootstrap-report-session", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Report-Integration-Key": "dev-report-integration-key"
  },
  body: JSON.stringify({
    externalUserId: "ext-usr-001",
    clientSystemId: "client-system-demo",
    returnUrl: "/"
  })
});

const payload = await response.json();
if (payload.success && payload.launchUrl) {
  window.open(payload.launchUrl, "_blank");
}


Exemplo C#

using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Report-Integration-Key", "dev-report-integration-key");

var body = new
{
    externalUserId = "ext-usr-001",
    clientSystemId = "client-system-demo",
    returnUrl = "/"
};

var response = await client.PostAsJsonAsync("https://localhost:7019/api/integration/bootstrap-report-session", body);
var payload = await response.Content.ReadFromJsonAsync<BootstrapResponse>();

if (payload?.Success == true && !string.IsNullOrWhiteSpace(payload.LaunchUrl))
{
    // Abra payload.LaunchUrl no navegador no fluxo da aplicação hospedeira.
}

public sealed record BootstrapResponse(bool Success, string? Message, string? LaunchUrl, DateTimeOffset? ExpiresAtUtc, string? Reference);

Observabilidade em Runtime

Eventos de segurança e auditoria são registrados para:

  • Bootstrap negado e motivo.
  • Bootstrap bem-sucedido e contexto da URL gerada.
  • Negação/abertura de sessão na rota de integração.




Chamada Externa para Abrir o Servidor de Relatórios (Lado Cliente)

Objetivo

Permitir que um sistema externo abra o AdhocReport.ServerApp completo para um usuário já mapeado no AdhocReport.

Não confundir: ExternalAuth x ReportIntegration

Neste fluxo app-app, a autenticação entre sistemas usa a seção ReportIntegration.

  • ReportIntegration (este documento): valida a chamada server-to-server via API key e gera token/launchUrl.
  • ExternalAuth: valida credenciais do usuário (username/senha) no login direto pela tela do ReportServer.

Importante:

  • No bootstrap (/api/integration/bootstrap-report-session), o sistema não valida senha do usuário via ExternalAuth.
  • A validação no bootstrap é de chave compartilhada (RequestApiKey) e integridade/expiração do token.

Como funciona (resumo rápido)

  1. Seu sistema chama o endpoint de bootstrap no ServerApp.
  2. O ServerApp valida a chave compartilhada e gera um token temporário (uso único).
  3. O ServerApp retorna uma launchUrl.
  4. O navegador abre essa launchUrl.
  5. O ServerApp autentica o usuário e redireciona para a aplicação completa.

Endpoint

  • Método: POST
  • Rota: /api/integration/bootstrap-report-session
  • Header obrigatório: X-Report-Integration-Key (ou nome configurado no servidor)

Configuração necessária no ReportServer (appsettings)

"ReportIntegration": {
  "Enabled": true,
  "RequestApiKeyHeaderName": "X-Report-Integration-Key",
  "RequestApiKey": "dev-report-integration-key",
  "BootstrapTokenLifetimeSeconds": 120,
  "BootstrapTokenSigningKey": "dev-bootstrap-signing-key-change-in-production",
  "IntegrationReportsRoute": "/integration/entry",
  "Cors": {
    "AllowedOrigins": [
      "https://localhost:7227",
      "http://localhost:5148"
    ]
  }
}

O que cada chave faz

  • Enabled: liga/desliga o bootstrap app-app.
  • RequestApiKeyHeaderName: nome do header esperado na chamada server-to-server.
  • RequestApiKey: valor da chave compartilhada entre sistemas.
  • BootstrapTokenLifetimeSeconds: validade do token temporário de abertura.
  • BootstrapTokenSigningKey: chave usada para assinar e validar o token.
  • IntegrationReportsRoute: rota de entrada que consome o token (/integration/entry).
  • Cors.AllowedOrigins: origens permitidas para chamadas cross-origin ao endpoint de bootstrap.

Observação importante sobre BootstrapTokenSigningKey

  • Essa chave não é hardcoded no fluxo de bootstrap.
  • Ela vem da configuração (ReportIntegration:BootstrapTokenSigningKey).
  • Se estiver ausente, o bootstrap retorna report-integration-misconfigured.

Body mínimo

{
  "externalUserId": "ext-usr-001",
  "clientSystemId": "client-system-demo",
  "returnUrl": "/"
}

Regras dos campos

  • externalUserId: obrigatório; deve existir no AdhocReport.
  • clientSystemId: obrigatório; identifica o sistema chamador.
  • returnUrl: opcional; deve ser rota relativa iniciando com /.

Exemplo de chamada (cURL)

curl -X POST "https://localhost:7019/api/integration/bootstrap-report-session" \
  -H "Content-Type: application/json" \
  -H "X-Report-Integration-Key: dev-report-integration-key" \
  -d '{"externalUserId":"ext-usr-001","clientSystemId":"client-system-demo","returnUrl":"/"}'

Exemplo de resposta de sucesso

{
  "success": true,
  "message": "ok",
  "launchUrl": "https://localhost:7019/integration/entry?token=...",
  "expiresAtUtc": "2026-06-02T18:30:00Z",
  "reference": "abc-123"
}

Depois disso, abra launchUrl no navegador (nova aba ou mesma aba, conforme sua UX).


Erros comuns

  • invalid-integration-key: chave compartilhada inválida.
  • external-user-not-found: usuário externo não mapeado no AdhocReport.
  • invalid-return-url: returnUrl inválida (não relativa).
  • report-integration-disabled: integração desabilitada no servidor.

Checklist rápido

  • Integração habilitada no ServerApp (ReportIntegration:Enabled=true).
  • Chave e nome do header alinhados entre cliente e servidor.
  • Usuário externo já vinculado (ExternalUserId).
  • URL HTTPS acessível entre os ambientes.
  • BootstrapTokenSigningKey configurada e protegida por segredo de ambiente.

Observação importante

O app ReportIntegration.BlazorTestClient é uma ferramenta de diagnóstico. Para o cliente final, a integração mínima é: chamar bootstrap + abrir launchUrl.