Documentação técnica

Manual de Integração

Autenticação com certificado digital ICP-Brasil no seu sistema web. Dois métodos — Popup ou XHR — com 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
Os dois métodos

Escolha como integrar.

O Popup é o caminho recomendado e seguro: devolve um ticket que o seu backend troca pela identidade. O XHR entrega o JSON direto no navegador e existe só para compatibilidade com sistemas legados — será desativado em 1º de outubro de 2026.

Recomendado

Popup

Inclui a biblioteca ihubauth.js e chama uma função. A janela é isolada e devolve um ticket de uso único — a identidade é resgatada no seu backend, nunca exposta ao navegador.

Use quando puder incluir um arquivo JavaScript na página — é o caso da maioria das aplicações web.
Ver exemplo
Compatibilidade · Será desativado em Outubro de 2026

XHR

Chamada direta ao endpoint, com o certificado apresentado na requisição (mTLS). Retorna o JSON direto no navegador — sem etapa de backend, com menor garantia.

Use quando precisar integrar com um sistema legado (ex.: PHP) sem abrir popup.
Ver exemplo

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)

Método 2 — XHR (compatibilidade · será desativado em 1º de outubro de 2026)

Faça uma requisição GET ao endpoint com credentials: 'include' — isso faz o navegador apresentar o certificado do cliente (mTLS). A resposta vem com HTTP 200 e o JSON no corpo, ou um dos códigos de erro.

JavaScript (fetch)
// Cole aqui o token de licença fornecido pela iHub:
var TOKEN = 'SEU_TOKEN_AQUI';

var API_URL = 'https://api.ihub.com.br/certificado/login?token=' + encodeURIComponent(TOKEN);

function autenticar() {
  fetch(API_URL, {
    method: 'GET',
    credentials: 'include'   // apresenta o certificado do cliente (mTLS)
  })
  .then(function (res) {
    return res.text().then(function (body) {
      return { status: res.status, body: body };
    });
  })
  .then(function (r) {
    if (r.status === 200) {
      var dados = JSON.parse(r.body);
      // dados.Nome, dados.CPF, dados.Validade, dados.DiasValidade,
      // dados.Verificado, dados.TipoCertificado
      realizarLogin(dados);
    } else {
      tratarErro(r.status);   // ver "Códigos de erro"
    }
  });
}
Método legado — será desativado em 1º de outubro de 2026. O XHR devolve a identidade direto no navegador e existe apenas para compatibilidade com sistemas legados (ex.: PHP). Após essa data, o endpoint deixará de responder. Para qualquer integração — nova ou existente — utilize o Popup + verificação no backend, em que a identidade nunca é exposta ao navegador.
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. (No XHR legado, o mesmo objeto vem direto no corpo.)

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).
Códigos de erro

O que fazer quando não dá 200.

Os códigos variam um pouco entre os métodos. Trate cada um com uma mensagem clara para o usuário.

Popup

O callback recebe { codigo, etapa, mensagem } como primeiro parâmetro.

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
Muitas requisições. Aguarde alguns instantes e tente novamente.
456
Certificado digital revogado — o usuário deve obter um novo.
457
Certificado inválido ou não apresentado.
0
Popup bloqueado pelo navegador ou fechado antes de concluir.

XHR

O código vem em res.status; o corpo pode trazer detalhes.

Código
Significado
200
Sucesso — JSON do certificado no corpo da resposta.
402
Referer ou Origin ausente na requisição.
455
Token inválido, domínio não autorizado ou saldo esgotado.
456
Certificado digital revogado.
457
Certificado inválido ou não apresentado.
Exemplo · tratando o erro do Popup
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.