Documentação técnica

Manual de Integração

Autenticação com certificado digital ICP-Brasil no seu sistema web. Popup com ticket de uso único, verificação no seu backend e retorno em JSON pronto para o seu login.

e-CPF · e-CNPJ A1 + A3 173 Certificados de AC ICP-Brasil
Integração mínima · Popup
// inclua ihubauth.js e chame:
ihubauth(TOKEN, function (erro, resultado) {
  if (erro) return tratarErro(erro);
  // envie resultado.ticket ao seu backend → /verificar
  enviarAoBackend(resultado.ticket);
});
Homologação e desenvolvimento

Testando em rede local.

Antes de ir para produção, você pode validar a integração em ambiente de desenvolvimento ou homologação na sua rede local.

Não consome créditos

Autenticações originadas de redes locais não descontam créditos do seu plano — valide a integração à vontade, sem afetar seu saldo. Valem as faixas privadas padrão:

10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 127.0.0.0/8 ::1/128 fc00::/7 fe80::/10

Até 7 autenticações por dia

Para evitar abuso, cada ambiente de homologação em rede local é limitado a 7 autenticações por dia — suficiente para validar a integração ponta a ponta.

Em produção é diferente. Com os domínios públicos cadastrados na licença, vale a cobrança normal: cada autenticação bem-sucedida consome 1 crédito (tentativas com erro não são cobradas).
Visão geral

O que o IHub-Auth faz por você.

Você inclui a integração na sua página de login e configura o token de licença. O serviço valida a cadeia ICP-Brasil e devolve ao navegador apenas um ticket de uso único; o seu backend troca esse ticket pela identidade do titular — que nunca passa pelo navegador.

Usuário
→ Clica em "Autenticar com certificado digital"
Sua página
→ Abre o popup de autenticação com o seu token de licença
Navegador
→ Apresenta o seletor de certificado ICP-Brasil instalado
IHub-Auth
→ Valida o certificado e devolve um ticket de uso único (sem dados)
Sua página
→ Envia o ticket ao seu backend
Seu backend
→ Resgata o ticket em /certificado/verificar e recebe os dados
Seu backend
→ Confere o CPF/CNPJ na sua base e cria a sessão
Integração

Um caminho, e é o seguro.

A autenticação acontece em um popup: a janela devolve ao navegador apenas um ticket de uso único, e o seu backend troca esse ticket pela identidade do titular. Os dados do certificado nunca passam pelo navegador.

O método XHR foi descontinuado. A chamada direta que devolvia a identidade no próprio navegador deixou de responder e não é mais documentada nem suportada. Integrações que ainda a utilizem precisam migrar para o popup com verificação no backend.

Inclua o arquivo ihubauth.js (fornecido pela iHub) e chame ihubauth(TOKEN, callback). No sucesso, o callback recebe um ticket de uso único — não os dados. Envie esse ticket ao seu backend, que o troca pela identidade em /certificado/verificar.

HTML + JavaScript
<!-- 1. Inclua a biblioteca fornecida pela iHub -->
<script src="/js/ihubauth.js"></script>

<!-- 2. Botão de login -->
<button onclick="autenticar()">Autenticar com certificado digital</button>

<script>
  // Cole aqui o token de licença fornecido pela iHub:
  var TOKEN = 'SEU_TOKEN_AQUI';

  function autenticar() {
    ihubauth(TOKEN, function (erro, resultado) {
      if (erro) {
        // erro = { codigo, etapa, mensagem } — ver "Códigos de erro"
        alert(erro.mensagem);
        return;
      }
      // resultado.ticket — opaco, uso único (~120s). Envie ao SEU backend:
      fetch('/sessao/certificado', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ ticket: resultado.ticket })
      }).then(function (r) {
        if (r.ok) location.href = '/painel';   // sessão criada no backend
        else alert('Falha ao validar o login.');
      });
    });
  }
</script>

A biblioteca já aponta para a API de produção. Se precisar, dá para informar a base por opção: ihubauth(TOKEN, callback, { baseUrl: 'https://api.ihub.com.br' }). Para o Popup funcionar, o navegador precisa permitir popups para o domínio da sua aplicação.

Exemplo pronto. Baixe um arquivo HTML completo e funcional (método Popup, com a biblioteca já embutida) — é só inserir o seu token e abrir.
↓ Baixar exemplo (HTML)
Verificação no backend

Troque o ticket pela identidade.

O frontend recebe um ticket; o seu backend o troca pela identidade autoritativa do titular. Assim a identidade nunca é confiada a partir do navegador — só o ticket, opaco e de uso único, trafega no cliente.

A identidade vem do /verificar. Monte a sessão sempre com os dados que o /certificado/verificar devolveu — nunca com algo vindo do navegador. O ticket é de uso único e expira em segundos.

Do seu backend, faça um POST para o endpoint abaixo com o token de licença e o ticket recebido. A resposta traz os dados do titular (mesmo objeto da seção Retorno).

Endpoint
POST https://api.ihub.com.br/certificado/verificar
Content-Type: application/json

{ "token": "SEU_TOKEN_AQUI", "ticket": "<ticket recebido do frontend>" }

→ 200 OK
{
  "Nome": "JOAO DA SILVA",
  "CPF": "12345678909",
  "Validade": "22/07/2026",
  "DiasValidade": 425,
  "Verificado": true,
  "TipoCertificado": "A1"
}
Código
Significado
200
Sucesso — identidade do titular no corpo da resposta.
401
Token de licença inválido.
403
Ticket pertence a outra licença.
404
Ticket inexistente.
410
Ticket expirado ou já utilizado.
Proteja o endpoint contra CSRF. O seu endpoint que recebe o ticket cria sessão — trate-o como qualquer POST de login: exija o token anti-CSRF do seu framework (ou um state/nonce ligado à sessão do navegador que iniciou o login). Isso impede que um ticket seja injetado no fluxo de outra pessoa.

PHP

PHP
<?php
// Endpoint do SEU backend que recebe o ticket do frontend.
// Ex.: POST /sessao/certificado  ·  corpo JSON: { "ticket": "..." }

const IHUB_TOKEN     = 'SEU_TOKEN_AQUI';   // token de licença (config no servidor)
const IHUB_VERIFICAR = 'https://api.ihub.com.br/certificado/verificar';

session_start();

// 1) Proteja contra CSRF como qualquer POST que cria sessão
//    (token anti-CSRF do seu framework, ou um nonce/state ligado à sessão).

$entrada = json_decode(file_get_contents('php://input'), true);
$ticket  = $entrada['ticket'] ?? null;
if (!$ticket) {
    http_response_code(400);
    exit(json_encode(['erro' => 'ticket ausente']));
}

// 2) Resgata o ticket na iHub (servidor-a-servidor)
$ch = curl_init(IHUB_VERIFICAR);
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json'],
    CURLOPT_POSTFIELDS     => json_encode(['token' => IHUB_TOKEN, 'ticket' => $ticket]),
    CURLOPT_TIMEOUT        => 10,
]);
$resposta = curl_exec($ch);
$status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status !== 200) {
    http_response_code(401);
    exit(json_encode(['erro' => 'ticket inválido ou expirado']));
}

$dados = json_decode($resposta, true);

// 3) Confere a validação da cadeia ICP-Brasil
if (($dados['Verificado'] ?? false) !== true) {
    http_response_code(401);
    exit(json_encode(['erro' => 'certificado não verificado']));
}

// 4) Localize o usuário pelo CPF/CNPJ na SUA base (find-or-create)
$usuario = encontrarUsuarioPorDocumento($dados['CPF']);   // implemente na sua app
if (!$usuario) {
    http_response_code(403);
    exit(json_encode(['erro' => 'usuário não cadastrado']));
}

// 5) Cria a sessão com os dados da iHub (NUNCA com algo vindo do frontend)
$_SESSION['usuario_id'] = $usuario['id'];
$_SESSION['nome']       = $dados['Nome'];
echo json_encode(['ok' => true]);

Python (Flask)

Python
import requests
from flask import Flask, request, session, jsonify

app = Flask(__name__)
app.secret_key = "..."                      # chave de sessão do Flask

IHUB_TOKEN     = "SEU_TOKEN_AQUI"           # token de licença (config no servidor)
IHUB_VERIFICAR = "https://api.ihub.com.br/certificado/verificar"


@app.post("/sessao/certificado")
def sessao_certificado():
    # 1) Proteja contra CSRF como qualquer POST que cria sessão
    #    (Flask-WTF/CSRF, ou um nonce/state ligado à sessão).

    ticket = (request.get_json(silent=True) or {}).get("ticket")
    if not ticket:
        return jsonify(erro="ticket ausente"), 400

    # 2) Resgata o ticket na iHub (servidor-a-servidor)
    r = requests.post(
        IHUB_VERIFICAR,
        json={"token": IHUB_TOKEN, "ticket": ticket},
        timeout=10,
    )
    if r.status_code != 200:
        return jsonify(erro="ticket inválido ou expirado"), 401

    dados = r.json()

    # 3) Confere a validação da cadeia ICP-Brasil
    if dados.get("Verificado") is not True:
        return jsonify(erro="certificado não verificado"), 401

    # 4) Localize o usuário pelo CPF/CNPJ na SUA base (find-or-create)
    usuario = encontrar_usuario_por_documento(dados["CPF"])   # implemente na sua app
    if not usuario:
        return jsonify(erro="usuário não cadastrado"), 403

    # 5) Cria a sessão com os dados da iHub (NUNCA com algo vindo do frontend)
    session["usuario_id"] = usuario["id"]
    session["nome"] = dados["Nome"]
    return jsonify(ok=True)

C# (.NET)

Um cliente reutilizável que encapsula o resgate do ticket — use-o em qualquer app .NET.

C#
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json.Serialization;

// Cliente reutilizável que troca o ticket pela identidade na iHub.
public sealed class IHubAuthClient
{
    private const string Verificar = "https://api.ihub.com.br/certificado/verificar";
    private readonly HttpClient _http;
    private readonly string _token;          // token de licença (config no servidor)

    public IHubAuthClient(HttpClient http, string token)
    {
        _http  = http;
        _token = token;
    }

    // Resgata o ticket. Lança se inválido/expirado ou não verificado.
    public async Task<CertificadoDados> VerifyAsync(string ticket, CancellationToken ct = default)
    {
        var resp = await _http.PostAsJsonAsync(
            Verificar, new { token = _token, ticket }, ct);

        if (!resp.IsSuccessStatusCode)
            throw new InvalidOperationException("Ticket inválido ou expirado.");

        var dados = await resp.Content.ReadFromJsonAsync<CertificadoDados>(cancellationToken: ct)
                    ?? throw new InvalidOperationException("Resposta vazia.");

        if (!dados.Verificado)
            throw new InvalidOperationException("Certificado não verificado.");

        return dados;
    }
}

public sealed record CertificadoDados(
    [property: JsonPropertyName("Nome")]            string Nome,
    [property: JsonPropertyName("CPF")]             string Cpf,
    [property: JsonPropertyName("Validade")]        string Validade,
    [property: JsonPropertyName("DiasValidade")]    int    DiasValidade,
    [property: JsonPropertyName("Verificado")]      bool   Verificado,
    [property: JsonPropertyName("TipoCertificado")] string TipoCertificado);

ASP.NET Core

O controller que recebe o ticket do frontend, usa o cliente acima e cria a sessão por cookie.

C# · ASP.NET Core
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

[ApiController]
[Route("sessao")]
public class SessaoController : ControllerBase
{
    private readonly IHubAuthClient _ihub;
    private readonly IUsuarioRepo   _usuarios;

    public SessaoController(IHubAuthClient ihub, IUsuarioRepo usuarios)
    {
        _ihub     = ihub;
        _usuarios = usuarios;
    }

    public record Entrada(string Ticket);

    [HttpPost("certificado")]
    [ValidateAntiForgeryToken]               // protege o POST de login contra CSRF
    public async Task<IActionResult> Certificado([FromBody] Entrada entrada)
    {
        if (string.IsNullOrEmpty(entrada.Ticket))
            return BadRequest(new { erro = "ticket ausente" });

        // 1) Resgata o ticket na iHub (servidor-a-servidor)
        CertificadoDados dados;
        try { dados = await _ihub.VerifyAsync(entrada.Ticket); }
        catch { return Unauthorized(new { erro = "ticket inválido ou expirado" }); }

        // 2) Localize o usuário pelo CPF/CNPJ na SUA base
        var usuario = await _usuarios.PorDocumentoAsync(dados.Cpf);
        if (usuario is null)
            return StatusCode(403, new { erro = "usuário não cadastrado" });

        // 3) Cria a sessão (cookie) com os dados da iHub
        var claims = new List<Claim>
        {
            new(ClaimTypes.NameIdentifier, usuario.Id.ToString()),
            new(ClaimTypes.Name, dados.Nome),
        };
        var id = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
        await HttpContext.SignInAsync(
            CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(id));

        return Ok(new { ok = true });
    }
}
Referência

JSON retornado no sucesso.

O /certificado/verificar devolve ao seu backend o objeto com os dados do titular.

Exemplo de retorno
{
  "Nome":            "JOAO DA SILVA",
  "CPF":             "12345678909",      // CPF (11 díg) ou CNPJ (14 díg)
  "Validade":        "22/07/2026",
  "DiasValidade":    425,
  "Verificado":      true,
  "TipoCertificado": "A1"               // "A1" ou "A3"
}
Campo
Tipo
Descrição
Nome
string
Nome completo do titular conforme o certificado ICP-Brasil.
CPF
string
CPF (11 dígitos) para e-CPF ou CNPJ (14 dígitos) para e-CNPJ.
Validade
string
Data de vencimento do certificado no formato DD/MM/AAAA.
DiasValidade
number
Dias restantes até o vencimento do certificado.
Verificado
boolean
true quando a cadeia ICP-Brasil foi validada com sucesso.
TipoCertificado
string
"A1" (arquivo) ou "A3" (token físico/smartcard).
Proteção contra automação

A tela de login é para pessoas.

O popup de autenticação conta com vários níveis de detecção de robôs e automações não autorizadas. Tudo acontece do lado do IHub-Auth: nada muda no seu código, e o usuário legítimo não ganha nenhuma etapa a mais.

Vários níveis — e não publicamos quais

Além da proteção de borda da Cloudflare e do desafio de verificação que o usuário já vê, toda abertura do popup passa por níveis adicionais de detecção de automação, que avaliam cada tentativa antes de liberar o login no sistema.

Não publicamos quais são nem como funcionam, e eles são revistos periodicamente. Descrever o critério em público facilitaria contorná-lo, e isso esvaziaria a proteção do seu sistema.

O que interessa para a sua integração:

Homologação não é afetada. Autenticações originadas das faixas de rede privada listadas em Homologação e desenvolvimento ficam fora dessas verificações.

O que acontece quando um acesso é recusado

A recusa acontece dentro do popup, antes de qualquer consumo: nenhum crédito é debitado e nenhum ticket é emitido. O popup exibe uma mensagem com um código de referência de 8 caracteres e não se fecha sozinho. Ao fechar, o seu sistema recebe o erro pelo caminho normal do callback.

Momento
O que o seu sistema recebe
Na abertura do popup
Código 429 no callback, depois que o usuário fecha a mensagem.
Na confirmação
Código 460 no callback, depois que o usuário fecha a mensagem.

A mensagem exibida é a mesma para qualquer nível acionado, de propósito: ela não revela qual critério barrou o acesso. O motivo fica no nosso registro e é localizado pelo código de referência.

Oriente o seu suporte. Para o código 429, a biblioteca ihubauth.js exibe um texto próprio — “Muitas requisições. Aguarde alguns instantes e tente novamente.”. Quando o popup mostrou um código de referência, esperar não resolve. A equipe de atendimento do seu sistema deve pedir esse código ao usuário — é por ele que o caso é localizado.

Automação legítima tem caminho próprio

Integração máquina a máquina — rotinas agendadas, coletas periódicas, sistemas de terceiros que consultam em lote — deve usar o webservice do sistema, não a tela de login feita para pessoas. É mais rápido, não depende de navegador e não disputa capacidade com o usuário final.

Se uma automação precisar continuar passando pelo popup por um período determinado, é possível cadastrar uma exceção com prazo de validade para endereços fixos ou para um certificado específico.

A decisão é sua, não nossa. O integrador barrado procura o suporte do seu sistema com o código de referência; cabe a você, responsável pela licença, avaliar se aquele uso é autorizado. Confirmado, você nos encaminha o pedido com o código e nós cadastramos a exceção. Não liberamos ninguém por conta própria nem atendemos o integrador diretamente.

Códigos de erro

O que fazer quando não dá 200.

Trate cada código com uma mensagem clara para o usuário. O callback recebe { codigo, etapa, mensagem } como primeiro parâmetro.

Códigos devolvidos ao seu sistema

Código
Significado
401
Licença inválida ou sessão expirada / já utilizada.
402
Domínio não autorizado para este token de licença.
403
Token de licença inválido ou saldo esgotado.
429
Volume de requisições acima do limite, ou acesso contido pela proteção contra automação. Se o popup exibiu um código de referência, esperar não resolve — veja o que fazer.
456
Certificado digital revogado — o usuário deve obter um novo.
457
Certificado inválido ou não apresentado.
460
Verificação de navegador inválida ou expirada. Ver proteção contra automação.
0
Popup bloqueado pelo navegador ou fechado antes de concluir.
Exemplo · tratando o erro no callback
ihubauth(TOKEN, function (erro, resultado) {
  if (erro) {
    switch (erro.codigo) {
      case 456:
        alert('Seu certificado foi revogado. Renove-o na sua certificadora.');
        break;
      case 457:
        alert('Certificado não encontrado. Verifique se está instalado.');
        break;
      default:
        alert(erro.mensagem);
    }
    return;
  }
  enviarAoBackend(resultado.ticket);  // envie o ticket ao seu backend
});
Pré-requisitos do usuário

O que o usuário final precisa.