Upota merkin varmennus verkkosivullesi (ilman iframea ja SDK:ta)

Lisää sivustollesi pieni JavaScript-pätkä (tai palvelinpuolen haku), joka varmentaa minkä tahansa Open Badgen julkisesta JSON-LD-päätepisteestä. Ei iframea, ei kolmannen osapuolen seurantaa.

Nacho Coll Kirjoittanut Päivitetty 8 min lukuaika
Lisää sivustollesi pieni JavaScript-pätkä (tai palvelinpuolen haku), joka varmentaa minkä tahansa Open Badgen julkisesta JSON-LD-päätepisteestä. Ei iframea, ei kolmannen osapuolen seurantaa.

Jos myönnät todistuksia, joku kysyy ennemmin tai myöhemmin: “mistä tiedän, että tämä merkki on aito?” Rehellinen vastaus ei saa koskaan olla “luota PNG-kuvaan.” Open Badge v2.0 ratkaisee tämän julkisella, koneluettavalla varmennuspäätepisteellä, ja “Varmennettu”-widgetin lisääminen omalle sivustollesi vie noin kymmenen koodiriviä — ei iframea, ei kolmannen osapuolen upotusskriptiä eikä SDK:ta.

Tämä artikkeli näyttää tarkasti, miten päätepistettä kutsutaan ja tulos näytetään, tavallisella JavaScriptillä sekä palvelimella Nodessa ja Pythonissa. Siinä käydään läpi myös se, mitä “varmennus” oikeasti tarkistaa, miten päätepiste istuu badges.ninjan vastaanottajille jo tarjoamien ihmisille tarkoitettujen sivujen rinnalle, sekä virheet, joiden takia varmennuswidget näyttää valmiilta mutta ei hiljaa todista mitään.

Mitä “varmennettu” oikeasti tarkoittaa

Open Badge v2.0 -assertion on JSON-LD-dokumentti, jolla on kiinteä rakenne: itse assertion (kuka sen sai ja milloin), viittaus BadgeClassiin (mikä se on ja kuka määrittelee kriteerit) ja viittaus myöntäjän profiiliin (kuka sen takaa). Kun “varmennat” merkin, tarkistat oikeasti kolme erillistä asiaa:

  1. Onko assertion olemassa ja löytyykö se. Award-tunnus on aito, ei arvattu eikä väärennetty.
  2. Onko se peruutettu. Myöntäjä voi peruuttaa merkin jälkikäteen — erehdyksessä myönnetty todistus, käytäntörikkomus, vanhentunut sertifiointi — ja oikein toimivan varmentajan on tuotava se esiin, ei vain vahvistettava, että tietue on olemassa.
  3. Pitääkö ketju yhtä. Assertionin viittaukset merkkiin ja myöntäjään johtavat oikeisiin, toisiinsa sopiviin tietueisiin, ei orpoihin tai ristiriitaisiin tunnuksiin.

Varmennuswidget, joka vain pingaa URL-osoitetta ja tarkistaa tilakoodin 200, ohittaa kohdan 2 kokonaan. Peruutettu assertion löytyy silti moitteetta — siinä on vain peruutuslippu datassa. Jos widgettisi tarkistaa vain, “onnistuiko pyyntö”, peruutettu todistus näkyy varmennettuna, mikä on pahempi kuin ettei widgettiä olisi lainkaan.

Julkinen ihmisille tarkoitettu sivu vs. API-päätepiste

Jokaisella osoitteessa https://badges.ninja myönnetyllä awardilla on julkinen URL, jonka vastaanottajat voivat jakaa suoraan, eikä sen katsominen vaadi rekisteröitymistä:

Julkinen todistussivu

Tähän sivuun useimmat vastaanottajat linkittävät LinkedInistä, ansioluettelosta tai sähköpostin allekirjoituksesta — se on tehty ihmisille, näyttää merkin kuvan, myöntäjän ja myöntöpäivän, eikä vaadi sinulta mitään toimiakseen. Alla oleva API-päätepiste palauttaa saman assertionin jäsenneltynä JSON-LD:nä, mitä tarvitset, kun rakennat oman käyttöliittymän sen sijaan, että vain linkittäisit meidän sivullemme.

Päätepiste

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

Auth-otsaketta ei tarvita — tämä on tarkoituksella julkinen, koska Open Badgesin koko pointti on, että kuka tahansa (työnantaja, rekrytoija, lupaviranomainen) voi varmentaa todistuksen ilman tiliä. Sopimuksen yksityiskohdat, mukaan lukien palautetun assertionin tarkat kenttien nimet, löytyvät varmennuksen dokumentaatiosta.

Anna awardin GUID, jonka saat joko omista myöntötiedoistasi tai poimimalla sen vastaanottajan sinulle jakamasta badges.ninja/awards/<guid>-linkistä.

Vanilla JS: hae ja näytä

Tämä on koko widget. Ei build-vaihetta, ei npm-pakettia — pudota se suoraan millä tahansa sivulle:

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

Vaihda REPLACE_WITH_AWARD_ID oikeaan award-GUID:iin. Siinä koko asiakaspuolen integraatio: ei iframe-rajaa, jonka kanssa CSS:n pitäisi tapella, eikä kolmannen osapuolen skriptiä, joka vetäisi mukanaan omat analytiikka- tai seurantapikselinsä varmennustarkistuksen kylkiäisenä.

Palvelinpuoli: Node

Jos haluat mieluummin varmentaa backendissä — vaikkapa ennen “Varmennettu kumppani” -merkin näyttämistä hallitsemallasi profiilisivulla tai ennen todistuksen hyväksymistä todisteeksi käyttöönottoprosessissa — haku on identtinen ilman DOM-osuutta:

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

Palvelinpuoli: 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")}

Molemmat esimerkit käsittelevät muun kuin 200-vastauksen tilana “ei löytynyt” poikkeuksen heittämisen sijaan. Rikkinäinen tai arvattu award-tunnus on julkiselle varmennuspäätepisteelle odotettu syöte, ei poikkeuksellinen, joten käsittele se tavallisena haarana eikä kiinniotettuna virheenä.

Tuloksen tyylittely toisen käyttöliittymän upottamisen sijaan

Koska yllä oleva widget on pelkkä fetch-kutsu omaan DOM:iisi, omistat jokaisen pikselin. Yleinen malli on kolme visuaalista tilaa, jotka johdetaan suoraan vastauksesta:

  • Varmennettu — vihreä valintamerkki sekä “Varmennettu” ja myöntöpäivä, tyylitelty sivustosi merkki-/tunnistekomponenttien mukaiseksi.
  • Peruutettu — hillitty tai punainen tila, joka näyttää peruutuksen syyn sen piilottamisen sijaan. Sekä vastaanottajat että kolmannet osapuolet hyötyvät rehellisestä “miksi”-tiedosta eikä hiljaisesta virheestä.
  • Ei löytynyt — käsittele visuaalisesti samoin kuin “peruutettu” (älä näytä väärää onnistumistilaa), mutta eri tekstillä, koska virheellinen tunnus ja tahallinen peruutus ovat sivun lukijalle eri tilanteita.

Mikään tästä ei vaadi komponenttikirjastoa. switch arvoilla verified / revoked / not_found ja kolme CSS-luokkaa riittävät.

Välimuisti, ettet jyrää päätepistettä

Varmennuspäätepiste on julkinen ja tunnistautumaton, mikä tarkoittaa myös, ettei pyyntökohtaisia krediittikustannuksia tarvitse murehtia — mutta se on silti verkkokierros jokaisella sivulatauksella, jos kutsut sitä asiakaspuolella ilman välimuistia. Sivulle, joka näyttää saman merkin kaikille kävijöille (esimerkiksi “Varmennettu kumppani” -merkki alatunnisteessa), tallenna tulos palvelimen välimuistiin lyhyellä TTL:llä — viisi–viisitoista minuuttia riittää hyvin, koska peruutukset eivät ole nopeasti muuttuva signaali — sen sijaan, että hakisit sen uudelleen joka pyynnöllä:

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

Kävijäkohtaiselle widgetille (vastaanottaja, joka upottaa oman todistuksensa henkilökohtaiselle sivulleen) voit jättää välimuistin pois ja kutsua päätepistettä tuoreena sivulatauksessa — ylimääräinen pyyntö on tuossa mittakaavassa merkityksetön.

Mitä badges.ninja jo tarjoaa vastaanottajille

Ennen kuin rakennat täysin räätälöidyn upotuksen, kannattaa tietää, mitä on jo valmiina. Jokaisen awardin julkisella sivulla on valmiit jako- ja upotuspätkät, jotka vastaanottajat voivat kopioida suoraan ilman omaa koodausta:

Julkinen jakosivu sosiaalisen median painikkeilla ja upotuspätkillä

Jos käyttötapauksesi on “anna vastaanottajan näyttää, että hänen merkkinsä on aito”, linkittäminen hänen badges.ninja/awards/<guid>-sivulleen on usein yksinkertaisempaa kuin oman widgetin rakentaminen — se on aiemmin näytetty ihmisille tarkoitettu sivu, ja se hoitaa jo varmennetun ja peruutetun erottelun puolestasi. Rakenna tämän artikkelin räätälöity widget, kun varmennustila pitää näyttää oman tuotteesi käyttöliittymän sisällä — kumppanihakemistossa, sisäisessä tarkastusnäkymässä, rekrytointityökalussa, joka tarkistaa hakijan ilmoittamat sertifioinnit — missä toiselle sivustolle linkittäminen ei ole oikea käyttökokemus.

Miksei iframe tai toimittajan widget?

Jotkin alustat antavat sinulle pätkän, kuten <iframe src="https://vendor.example.com/verify-widget">, varmennustilan upottamiseen. Se toimii, mutta sillä on todellisia kustannuksia: et voi tyylitellä sisältöä sivustosi mukaiseksi (olet toimittajan toimittaman CSS:n armoilla), se tuo mukanaan cross-origin-dokumentin, jonka Content-Security-Policysi on erikseen sallittava, ja jokainen sivulataus lähettää pyynnön kolmannen osapuolen iframe-palvelimelle riippumatta siitä, kiinnostaako kävijää varmennustila lainkaan. Kahden kutsun fetch-widget välttää kaikki kolme — se on sinun DOM:isi, sinun tyylisi, ja se ajetaan vain silloin, kun päätät kutsua sitä.

Koko varmennusketjun läpikäynti

Jos rakennat jotain perusteellisempaa kuin tilawidgetin — tarkastustyökalun, joka tarkistaa erän todistuksia, tai vaatimustenmukaisuusraportin — käy läpi koko ketju äläkä pysähdy assertioniin. Assertion viittaa badge-URL-osoitteeseen (BadgeClass: nimi, kuvaus, kriteerit), ja BadgeClass puolestaan viittaa issuer-URL-osoitteeseen (organisaation profiili). Perusteellinen varmentaja hakee kaikki kolme ja vahvistaa, että ne ovat keskenään johdonmukaisia — merkki todella kuuluu myöntäjälle, jonka se väittää, ja assertion todella viittaa merkkiin, jonka se väittää — eikä vain sitä, että ylimmän tason assertion palauttaa 200. Se on enemmän syvyyttä kuin yksittäisen todistuksen tilawidget tarvitsee, mutta rakenne kannattaa tuntea, jos kumppani tai tarkastaja joskus kysyy: “kuinka syvällä varmennuksenne oikeasti käy?”

Yleiset virheet, joita kannattaa välttää

  • Vain HTTP-tilan tarkistaminen. Kuten yllä todettiin, peruutettu assertion löytyy silti. Lue aina assertion.revoked, äläkä päättele varmennusta pelkästä tilakoodista.
  • Award-tunnuksen kovakoodaus jaettuun komponenttiin. Jos widgetin on tarkoitus olla uudelleenkäytettävä monen vastaanottajan sivuilla, ota award-tunnus parametrina (data-attribuutti, query string, prop) äläkä skriptiin kirjoitettuna vakiona.
  • Verkkovirheiden hiljainen nielaiseminen. try/catch, joka ei kirjaa mitään ja näyttää saman “ei löytynyt” -tekstin kuin todella puuttuvalle merkille, tekee virheenjäljityksestä myöhemmin kivuliasta. Erota “verkkokutsu epäonnistui” ja “API sanoi ei” virhehaaroissasi, ainakin lokeissa.
  • Uudelleenvarmennus jokaisella renderöinnillä asiakassovelluksessa. Jos widget on React- tai Vue-komponentissa, joka renderöityy usein, varmenna kerran per mount (tai kerran per välimuistin TTL-ikkuna, kuten välimuistiosiossa yllä), ei jokaisella uudelleenrenderöinnillä.

Maksaako tämä krediittejä, ja toimiiko se ilmaisessa suunnitelmassa?

Ei. Varmennuspäätepiste on tarkoituksella myöntämistä ohjaavan krediittijärjestelmän ulkopuolella — sen tarkistaminen, onko merkki voimassa, ei maksa myöntäjälle mitään, eikä sitä kutsuva kävijä tarvitse tiliä, API-avainta eikä mitään tilaustasoa. Tämä pätee riippumatta siitä, myönnettiinkö merkki Free-, Starter- vai Pro-tasolla: varmennus on todistuksen itsensä ominaisuus, ei myöntäjän tilauksen taakse lukittu toiminto. Siksi widgetin voi myös turvallisesti laittaa julkiselle sivulle, jonka liikenne on arvaamatonta (rekrytointisivu, kumppanihakemisto), huolehtimatta tiliisi sidotuista rate limiteistä — kutsut vain-luku-tyyppistä julkista päätepistettä, et tunnistautuneella API-reitillä.

Yksi asia kannattaa sanoa suoraan: päätepisteen palauttama assertion-JSON ei sisällä vastaanottajan raakaa sähköpostiosoitetta. Vastaanottajan sähköposti tallennetaan backendissä tiivisteenä juuri siksi, ettei julkisesta varmennusvastauksesta voi kaapia henkilökohtaisia yhteystietoja — saamasi tieto riittää todistuksen vahvistamiseen (merkki, myöntäjä, päivämäärät, peruutustila) muuttamatta päätepistettä liidigenerointityökaluksi kenelle tahansa, joka käy award-tunnuksia läpi.

Valmiina myöntämään ensimmäisen varmennettavan todistuksesi?

Aloita ilmaiseksi badges.ninjassa — visuaalinen suunnittelutyökalu, julkinen varmennussivu, PDF-todistus, Open Badge v2.0 -tuloste. Luottokorttia ei tarvita.

Assertionin jokaisen kentän täydellisen kommentoidun rakenteen löydät JSON-LD-kenttäviitteestä. Ja jos myönnät todistuksia ohjelmallisesti etkä vain varmenna niitä, API-pikaopas käsittelee tunnistautumisen ja award-luontikutsun, josta tämän päätepisteen tuloste tulee.

Nacho Coll

Kirjoittajasta

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.

Takaisin Blogiin

Aiheeseen liittyvät artikkelit