Вградете верификация на значки в сайта си (без iframe, без SDK)

Добавете към сайта си малък JavaScript фрагмент (или заявка от страна на сървъра), който верифицира всяка Open Badge чрез публичен JSON-LD endpoint. Без iframe, без външен тракер.

Nacho Coll От Обновено 10 мин четене
Добавете към сайта си малък JavaScript фрагмент (или заявка от страна на сървъра), който верифицира всяка Open Badge чрез публичен JSON-LD endpoint. Без iframe, без външен тракер.

Ако издавате удостоверения, рано или късно някой ще попита: „как да разбера, че тази значка е истинска?“ Честният отговор никога не бива да е „доверете се на PNG файла“. Open Badge v2.0 решава този проблем с публичен, машинночетим endpoint за верификация, а добавянето на джаджа „Верифицирано“ към вашия сайт отнема около десет реда код — без iframe, без външен скрипт за вграждане и без SDK.

Тази публикация показва точно как да извикате endpoint-а и как да изобразите резултата — с чист JavaScript и от страна на сървъра в Node и Python. Тя обяснява и какво всъщност проверява „верификацията“, как endpoint-ът се вписва до страниците за хора, които badges.ninja вече предлага на получателите, и кои грешки правят джаджата да изглежда завършена, докато тихомълком не доказва нищо.

Какво всъщност означава „верифицирано“

Assertion на Open Badge v2.0 е JSON-LD документ с фиксирана структура: самият assertion (кой го е получил и кога), препратка към BadgeClass (какво представлява и кой определя критериите) и препратка към профила на издателя (кой гарантира за него). Когато „верифицирате“ значка, всъщност проверявате три отделни неща:

  1. Съществува ли assertion-ът и разрешава ли се. Идентификаторът на наградата е реален, не е налучкан или подправен.
  2. Отменен ли е. Издателят може да отмени значка впоследствие — удостоверение, издадено по грешка, нарушение на правила, изтекла сертификация — и коректният верификатор трябва да го покаже, а не само да потвърди, че записът съществува.
  3. Издържа ли веригата. Препратките на assertion-а към значката и издателя наистина водят до реални, съвпадащи записи, а не до осиротели или несъответстващи идентификатори.

Джаджа, която само пингва URL адрес и проверява за статус 200, изцяло пропуска точка 2. Отменен assertion продължава да се разрешава нормално — просто носи флаг за отмяна в данните. Ако джаджата ви проверява само „успешна ли беше заявката“, отменено удостоверение ще се покаже като верифицирано, което е по-лошо, отколкото изобщо да нямате джаджа.

Публичната страница за хора срещу API endpoint-а

Всяка награда, издадена в https://badges.ninja, получава публичен URL адрес, който получателите могат да споделят директно, без регистрация за разглеждане:

Public credential page

Именно към тази страница повечето получатели ще препращат от LinkedIn, от автобиографията или от имейл подписа си — тя е изградена за хора, показва изображението на значката, издателя и датата на издаване и не изисква нищо от вас, за да работи. Endpoint-ът по-долу връща същия assertion като структуриран JSON-LD, което е точно онова, което ви трябва, когато изграждате собствен интерфейс, вместо просто да препращате към нашия.

Endpoint-ът

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

Не се изисква auth header — това е умишлено публично, защото целият смисъл на Open Badges е всеки (работодател, рекрутър, лицензиращ орган) да може да верифицира удостоверение без акаунт. Пълните подробности за договора, включително точните имена на полетата във върнатия assertion, са в документацията за верификация.

Подайте GUID на наградата, който ще имате или от собствените си записи за издаване, или извлечен от линк badges.ninja/awards/<guid>, споделен ви от получател.

Чист JS: заявка и изобразяване

Това е цялата джаджа. Без стъпка за изграждане, без npm пакет — просто я поставете на всяка страница:

<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>

Заменете REPLACE_WITH_AWARD_ID с реален GUID на награда. Това е цялата интеграция от страна на клиента: няма граница на iframe, с която да се борите с CSS, и няма външен скрипт, който да носи собствена аналитика или проследяващи пиксели покрай проверката.

От страна на сървъра: Node

Ако предпочитате да верифицирате в бекенда си — например преди да покажете значка „Верифициран партньор“ на профилна страница, която контролирате, или преди да приемете удостоверение като доказателство в процес на въвеждане — заявката е идентична, само без частта с 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 };
}

От страна на сървъра: 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")}

И двата примера третират отговор, различен от 200, като „не е намерено“, вместо да хвърлят грешка. Повреден или налучкан идентификатор на награда е очакван вход за публичен endpoint за верификация, а не изключение, затова обработвайте го като нормален клон, а не като уловена грешка.

Стилизиране на резултата вместо вграждане на чужд интерфейс

Тъй като джаджата по-горе е просто fetch заявка към вашия собствен DOM, вие контролирате всеки пиксел. Често срещан модел са три визуални състояния, извлечени директно от отговора:

  • Верифицирано — зелена отметка плюс „Верифицирано“ и датата на издаване, стилизирани в съответствие с компонентите за значки/тагове на сайта ви.
  • Отменено — приглушено или червено състояние, което показва причината за отмяната, вместо да я крие. Както получателите, така и третите страни печелят от честното „защо“, а не от тиха грешка.
  • Не е намерено — третирайте го визуално като „отменено“ (не показвайте фалшиво състояние на успех), но с различен текст, тъй като грешен идентификатор и умишлена отмяна са различни ситуации за този, който чете страницата.

Нищо от това не изисква библиотека с компоненти. Един switch върху verified / revoked / not_found и три CSS класа са достатъчни.

Кеширане, за да не затрупвате endpoint-а

Endpoint-ът за верификация е публичен и без автентикация, което означава и че няма разход на кредити за заявка, за който да се безпокоите — но при извикване от клиента без кеширане това си остава мрежова заявка при всяко зареждане на страница. За страница, която показва една и съща значка на всеки посетител (например значка „Верифициран партньор“ във footer-а), кеширайте резултата от страна на сървъра с кратък TTL — пет до петнадесет минути са напълно достатъчни, тъй като отмените не са бързо променящ се сигнал — вместо да извличате наново при всяка заявка:

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;
}

За джаджа за всеки посетител поотделно (получател, който вгражда собственото си удостоверение в личен сайт), пропуснете кеша и просто я извиквайте наново при зареждане на страницата — допълнителната заявка е пренебрежима в такъв мащаб.

Какво badges.ninja вече предлага на получателите

Преди да изградите напълно персонализирано вграждане, струва си да знаете какво вече е налично наготово. Публичната страница на всяка награда включва готови фрагменти за споделяне и вграждане, които получателите могат да копират директно, без нужда от код от тяхна страна:

Public share page with social buttons + embed snippets

Ако случаят ви е „нека получателят покаже, че значката му е истинска“, препратката към страницата му badges.ninja/awards/<guid> често е по-проста от изграждането на персонализирана джаджа — това е показаната по-рано страница за хора и тя вече се грижи за разликата между верифицирано и отменено. Изградете персонализираната джаджа от тази публикация, когато ви трябва статусът на верификацията да се изобразява вътре в интерфейса на вашия продукт — партньорски указател, вътрешно табло за одит, инструмент за наемане, който проверява заявените сертификации на кандидат — където препращането към друг сайт не е правилното потребителско изживяване.

Защо не iframe или джаджа от доставчик?

Някои платформи ви дават фрагмент като <iframe src="https://vendor.example.com/verify-widget"> за вграждане на статус на верификация. Работи, но има реални недостатъци: не можете да преоформите съдържанието, за да пасне на сайта ви (зависите от CSS, който доставчикът е предоставил), въвежда се междудомейнов документ, който вашата Content-Security-Policy трябва изрично да позволи, и при всяко зареждане на страница се прави заявка към хоста на iframe-а на трета страна, независимо дали посетителят се интересува от статуса на верификацията. Джаджа с две заявки чрез fetch избягва и трите — това е вашият DOM, вашият стил и тя се изпълнява само когато вие решите да я извикате.

Обхождане на цялата верига за верификация

Ако изграждате нещо по-обстойно от джаджа за статус — например инструмент за одит, който проверява партида удостоверения, или справка за съответствие — обходете цялата верига, вместо да спирате при assertion-а. Assertion-ът препраща към URL адрес на badge (BadgeClass: име, описание, критерии), а BadgeClass на свой ред препраща към URL адрес на issuer (профила на организацията). Строгият верификатор разрешава и трите и потвърждава, че са вътрешно съгласувани — значката наистина принадлежи на издателя, който заявява, assertion-ът наистина препраща към значката, която заявява — а не само че assertion-ът от най-високо ниво връща 200. Това е повече дълбочина, отколкото ви трябва за джаджа за статус на единично удостоверение, но е добре да знаете, че тази структура съществува, ако партньор или одитор някога попита: „колко дълбока е всъщност вашата верификация?“

Често срещани грешки, които да избягвате

  • Проверка само на HTTP статуса. Както е описано по-горе, отменен assertion продължава да се разрешава. Винаги четете assertion.revoked, не извеждайте верификацията само от статус кода.
  • Твърдо кодиран идентификатор на награда в споделен компонент. Ако джаджата е предназначена за многократна употреба в страниците на много получатели, приемайте идентификатора на наградата като параметър (data атрибут, query string, prop), а не като литерал, вграден в скрипта.
  • Тихо поглъщане на мрежови грешки. Един try/catch, който нищо не логва и показва същия текст „не е намерено“ като при наистина липсваща значка, прави отстраняването на грешки болезнено по-късно. Разграничавайте „мрежовата заявка се провали“ от „API-то каза не“ във вашите клонове за грешки, поне в логовете.
  • Повторна верификация при всяко изобразяване в клиентско приложение. Ако джаджата живее в React или Vue компонент, който се изобразява често, верифицирайте веднъж при монтиране (или веднъж за прозорец на кеширан TTL, според раздела за кеширане по-горе), а не при всяко повторно изобразяване.

Струва ли кредити и работи ли с безплатния план?

Не. Endpoint-ът за верификация е умишлено извън системата от кредити, която управлява издаването — проверката дали една значка е валидна не струва нищо на издателя и не изисква посетителят, който я извиква, да има акаунт, API ключ или какъвто и да е план. Това важи независимо дали значката е издадена на план Free, Starter или Pro: верификацията е свойство на самото удостоверение, а не функция, ограничена от абонамента на издателя. Затова спокойно можете да поставите тази джаджа на публична страница с непредвидим трафик (страница за кариери, партньорски указател), без да се тревожите за лимити на заявките, свързани с вашия акаунт — извиквате публичен endpoint само за четене, а не автентикиран API маршрут.

Едно нещо заслужава изричното уточнение: JSON assertion-ът, върнат от endpoint-а, не включва суровия имейл адрес на получателя. Имейлът на получателя се съхранява хеширан в бекенда именно за да не може публичният отговор за верификация да бъде обходен за лични контактни данни — получавате достатъчно, за да потвърдите удостоверението (значка, издател, дати, статус на отмяна), без endpoint-ът да се превръща в инструмент за събиране на лийдове за всеки, който обхожда идентификатори на награди.

Готови ли сте да издадете първото си верифицируемо удостоверение?

Започнете безплатно в badges.ninja — визуален дизайнер, публична страница за верификация, PDF сертификат, изход в Open Badge v2.0. Не се изисква кредитна карта.

За пълната анотирана структура на всяко поле в assertion вижте справочника за полетата на JSON-LD. А ако издавате удостоверения програмно, а не само ги верифицирате, бързият старт с API обхваща автентикацията и извикването за създаване на награда, от което идва изходът на този endpoint.

Nacho Coll

За автора

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.

Назад към блога

Свързани статии