Incorpore a verificação de emblemas no seu site (sem iframe, sem SDK)

Adicione um pequeno trecho de JavaScript (ou uma chamada no servidor) ao seu site para verificar qualquer Open Badge em um endpoint JSON-LD público. Sem iframe, sem rastreadores de terceiros.

Nacho Coll Por Atualizado 11 min de leitura
Adicione um pequeno trecho de JavaScript (ou uma chamada no servidor) ao seu site para verificar qualquer Open Badge em um endpoint JSON-LD público. Sem iframe, sem rastreadores de terceiros.

Se você emite credenciais digitais, cedo ou tarde alguém vai perguntar: “como sei que este emblema é real?“. A resposta honesta nunca deveria ser “confie no PNG”. O Open Badge v2.0 resolve isso com um endpoint de verificação público e legível por máquina, e colocar um widget de “Verificado” no seu próprio site leva cerca de dez linhas de código: sem iframe, sem script de terceiros e sem SDK.

Este post mostra exatamente como chamar o endpoint e renderizar o resultado, em JavaScript puro e no servidor com Node e Python. Ele também explica o que a “verificação” realmente checa, onde o endpoint se encaixa ao lado das páginas para pessoas que o badges.ninja já oferece aos destinatários, e os erros que fazem um widget de verificação parecer pronto sem provar nada.

O que “verificado” realmente significa

Uma asserção do Open Badge v2.0 é um documento JSON-LD com formato fixo: a asserção em si (quem recebeu e quando), uma referência ao BadgeClass (o que é e quem define os critérios) e uma referência ao perfil do emissor (quem a endossa). Quando você “verifica” um emblema, na verdade está checando três coisas separadas:

  1. Se a asserção existe e é resolvida. O ID do prêmio é real, não foi chutado nem adulterado.
  2. Se ela foi revogada. Um emissor pode revogar um emblema depois — uma credencial emitida por engano, uma violação de política, uma certificação vencida — e um verificador correto precisa mostrar isso, e não apenas confirmar que o registro existe.
  3. Se a cadeia fecha. As referências da asserção ao emblema e ao emissor realmente levam a registros reais e coerentes, não a IDs órfãos ou divergentes.

Um widget de verificação que só faz ping em uma URL e confere um status 200 ignora completamente o ponto 2. Uma asserção revogada continua sendo resolvida normalmente — ela apenas carrega um indicador de revogação no payload. Se o seu widget só checa “a requisição funcionou?”, uma credencial revogada vai aparecer como verificada, o que é pior do que não ter widget nenhum.

A página pública para pessoas vs. o endpoint da API

Cada prêmio emitido em https://badges.ninja ganha uma URL pública que os destinatários podem compartilhar diretamente, sem precisar de cadastro para visualizar:

Public credential page

Essa é a página que a maioria dos destinatários vai linkar no LinkedIn, no currículo ou na assinatura de e-mail: ela é feita para pessoas, mostra a imagem do emblema, o emissor e a data de emissão, e não precisa de nada da sua parte para funcionar. O endpoint da API abaixo retorna a mesma asserção como JSON-LD estruturado, que é o que você quer quando está construindo a sua própria interface em vez de apenas apontar para a nossa.

O endpoint

GET https://api.badges.ninja/certify-badge/award/{guid}

Nenhum cabeçalho de autenticação é necessário — ele é público de propósito, porque todo o objetivo dos Open Badges é que qualquer pessoa (um empregador, um recrutador, um conselho profissional) possa verificar uma credencial sem ter conta. Os detalhes completos do contrato, incluindo os nomes exatos dos campos da asserção retornada, estão na documentação de verificação.

Passe o GUID do prêmio, que você terá nos seus próprios registros de emissão ou poderá extrair de um link badges.ninja/awards/<guid> que um destinatário compartilhou com você.

JS puro: buscar e renderizar

Este é o widget inteiro. Sem etapa de build, sem pacote npm, é só colar em qualquer página:

<div id="badge-verify"></div>
<script>
  async function renderBadgeVerification(awardId, mountId) {
    const el = document.getElementById(mountId);
    el.textContent = "Checking badge status";
    try {
      const res = await fetch(
        `https://api.badges.ninja/certify-badge/award/${awardId}`
      );
      if (!res.ok) {
        el.textContent = "Credential not found.";
        return;
      }
      const assertion = await res.json();
      // —— revocation check first, a resolvable assertion isn't enough on its own
      if (assertion.revoked) {
        el.textContent = `Revoked: ${assertion.revocationReason || "no reason given"}`;
        return;
      }
      el.textContent = `Verified, issued ${new Date(assertion.issuedOn).toLocaleDateString()}`;
    } catch (err) {
      el.textContent = "Verification check failed.";
    }
  }
  renderBadgeVerification("REPLACE_WITH_AWARD_ID", "badge-verify");
</script>

Troque REPLACE_WITH_AWARD_ID por um GUID de prêmio real. Essa é toda a integração no cliente: sem fronteira de iframe para brigar com o CSS e sem script de terceiros trazendo suas próprias análises ou pixels de rastreamento junto com a checagem de verificação.

No servidor: Node

Se você prefere verificar no seu backend — por exemplo, antes de exibir um emblema de “Parceiro Verificado” em uma página de perfil que você controla, ou antes de aceitar uma credencial como comprovação em um fluxo de onboarding — o fetch é idêntico, menos a parte do DOM:

async function verifyAward(awardId) {
  const res = await fetch(
    `https://api.badges.ninja/certify-badge/award/${awardId}`
  );
  if (!res.ok) return { verified: false, reason: "not_found" };
  const assertion = await res.json();
  if (assertion.revoked) {
    return { verified: false, reason: assertion.revocationReason || "revoked" };
  }
  return { verified: true, issuedOn: assertion.issuedOn, badge: assertion.badge };
}

No servidor: Python

import requests
# —— resolve the assertion, then check revocation before trusting it
def verify_award(award_id):
    res = requests.get(f"https://api.badges.ninja/certify-badge/award/{award_id}")
    if res.status_code != 200:
        return {"verified": False, "reason": "not_found"}
    assertion = res.json()
    if assertion.get("revoked"):
        return {"verified": False, "reason": assertion.get("revocationReason", "revoked")}
    return {"verified": True, "issued_on": assertion.get("issuedOn"), "badge": assertion.get("badge")}

Os dois exemplos tratam uma resposta diferente de 200 como “não encontrado” em vez de lançar uma exceção. Um ID de prêmio quebrado ou chutado é uma entrada esperada em um endpoint de verificação público, não algo excepcional, então trate isso como um ramo normal do fluxo, e não como um erro capturado.

Estilizando o resultado em vez de incorporar a interface de outra pessoa

Como o widget acima é só uma chamada fetch para o seu próprio DOM, você controla cada pixel. Um padrão comum são três estados visuais mapeados direto da resposta:

  • Verificado — um check verde com “Verificado” e a data de emissão, estilizado para combinar com os componentes de tag/emblema do seu site.
  • Revogado — um estado discreto ou vermelho que mostra o motivo da revogação em vez de escondê-lo. Destinatários e terceiros ganham com um “porquê” honesto, não com uma falha silenciosa.
  • Não encontrado — trate visualmente como “revogado” (não renderize um estado de sucesso falso), mas com um texto diferente, já que um ID inválido e uma revogação deliberada são situações distintas para quem está lendo a página.

Nada disso exige uma biblioteca de componentes. Um switch em verified / revoked / not_found e três classes CSS resolvem.

Cache, para não sobrecarregar o endpoint

O endpoint de verificação é público e não autenticado, o que também significa que não há custo de crédito por requisição — mas ainda é uma ida à rede a cada carregamento de página se você o chama no cliente sem cache. Para uma página que exibe o mesmo emblema para todos os visitantes (um emblema de “Parceiro Verificado” no rodapé, por exemplo), faça cache do resultado no servidor com um TTL curto — de cinco a quinze minutos é mais que suficiente, já que revogações não são um sinal que muda rápido — em vez de buscar de novo a cada requisição:

const cache = new Map();
async function verifyAwardCached(awardId, ttlMs = 10 * 60 * 1000) {
  const hit = cache.get(awardId);
  if (hit && Date.now() - hit.at < ttlMs) return hit.result;
  const result = await verifyAward(awardId);
  cache.set(awardId, { result, at: Date.now() });
  return result;
}

Para um widget por visitante (um destinatário incorporando a própria credencial em um site pessoal), dispense o cache e chame na hora, a cada carregamento da página — a requisição extra é desprezível nessa escala.

O que o badges.ninja já oferece aos destinatários

Antes de construir uma incorporação totalmente customizada, vale saber o que já está disponível de fábrica. A página pública de cada prêmio inclui trechos prontos de compartilhamento e incorporação que os destinatários podem copiar direto, sem escrever código:

Public share page with social buttons + embed snippets

Se o seu caso de uso é “deixar o destinatário mostrar que o emblema dele é real”, linkar para a página badges.ninja/awards/<guid> dele costuma ser mais simples do que construir um widget customizado — é a página para pessoas mostrada antes, e ela já trata a distinção entre verificado e revogado por você. Construa o widget customizado deste post quando precisar renderizar o status de verificação dentro da interface do seu próprio produto — um diretório de parceiros, um painel de auditoria interna, uma ferramenta de recrutamento que confere as certificações declaradas por um candidato — onde apontar para outro site não é a melhor experiência.

Por que não um iframe ou um widget de fornecedor?

Algumas plataformas entregam um trecho como <iframe src="https://vendor.example.com/verify-widget"> para incorporar o status de verificação. Funciona, mas tem custos reais: você não consegue reestilizar o conteúdo para combinar com o seu site (fica preso ao CSS que o fornecedor enviou), ele introduz um documento de origem cruzada que o seu Content-Security-Policy precisa permitir explicitamente, e cada carregamento de página faz uma requisição ao host do iframe de um terceiro, o visitante ligando ou não para o status de verificação. Um widget com uma única chamada fetch evita os três problemas — o DOM é seu, o estilo é seu, e ele só roda quando você decide chamá-lo.

Percorrendo a cadeia de verificação completa

Se você está construindo algo mais completo que um widget de status — uma ferramenta de auditoria que confere um lote de credenciais, por exemplo, ou um relatório de conformidade —, percorra a cadeia inteira em vez de parar na asserção. A asserção referencia uma URL badge (o BadgeClass: nome, descrição, critérios) e o BadgeClass, por sua vez, referencia uma URL issuer (o perfil da organização). Um verificador rigoroso resolve as três e confirma que são internamente consistentes — o emblema realmente pertence ao emissor que diz pertencer, a asserção realmente referencia o emblema que diz referenciar —, e não apenas que a asserção de nível superior retorna 200. É mais profundidade do que um widget de status de uma única credencial precisa, mas vale conhecer esse formato caso um parceiro ou auditor pergunte um dia: “até que ponto a sua verificação realmente vai?”

Erros comuns a evitar

  • Checar apenas o status HTTP. Como vimos, uma asserção revogada continua sendo resolvida. Sempre leia assertion.revoked; não deduza a verificação só pelo código de status.
  • Fixar o ID do prêmio em um componente compartilhado. Se o widget deve ser reutilizável nas páginas de vários destinatários, receba o ID do prêmio como parâmetro (um data attribute, uma query string, uma prop), e não como um literal fixo no script.
  • Engolir erros de rede em silêncio. Um try/catch que não registra nada e mostra o mesmo texto de “não encontrado” de um emblema realmente inexistente deixa a depuração dolorosa depois. Diferencie “a chamada de rede falhou” de “a API disse que não” nos seus ramos de erro, pelo menos nos logs.
  • Reverificar a cada renderização em um app no cliente. Se o widget vive dentro de um componente React ou Vue que renderiza com frequência, verifique uma vez por montagem (ou uma vez por janela de TTL em cache, conforme a seção de cache acima), e não a cada nova renderização.

Isso custa créditos e funciona no plano gratuito?

Não. O endpoint de verificação fica deliberadamente fora do sistema de créditos que rege a emissão: checar se um emblema é válido não custa nada ao emissor, e quem chama não precisa ter conta, chave de API nem plano algum. Isso vale seja o emblema emitido em um plano Free, Starter ou Pro: a verificação é uma propriedade da própria credencial, não um recurso restrito pela assinatura do emissor. É também por isso que você pode colocar este widget com tranquilidade em uma página pública com tráfego imprevisível (uma página de carreiras, um diretório de parceiros) sem se preocupar com limites de taxa atrelados à sua conta — você está chamando um endpoint público somente leitura, não uma rota de API autenticada.

Um ponto que vale deixar explícito: o JSON da asserção retornado pelo endpoint não inclui o endereço de e-mail em texto puro do destinatário. O e-mail é armazenado como hash no backend justamente para que uma resposta de verificação pública não possa ser raspada em busca de informações de contato pessoais — o que você recebe basta para confirmar a credencial (emblema, emissor, datas, status de revogação) sem transformar o endpoint em uma ferramenta de geração de leads para quem quiser iterar sobre IDs de prêmios.

Pronto para emitir sua primeira credencial verificável?

Comece grátis no badges.ninja — designer visual, página pública de verificação, certificado em PDF e saída Open Badge v2.0. Sem cartão de crédito.

Para a estrutura anotada completa de cada campo de uma asserção, veja a referência de campos JSON-LD. E se você emite credenciais via programação em vez de apenas verificá-las, o guia rápido da API cobre a autenticação e a chamada de criação de prêmio da qual a saída deste endpoint vem.

Nacho Coll

Sobre o autor

Founder & Engineer at Badges Ninja

Nacho founded Badges Ninja to make issuing verifiable digital credentials as simple as a single API call — Open Badge v2.0 badges and certificates, minted, hosted, and verifiable without standing up your own issuer infrastructure. Writes about the Open Badges spec, credential verification, and running a credentialing platform serverless on AWS, from the operator side of the wire.

Voltar ao Blog

Artigos Relacionados