Skip to content

Signing & Verification

badges.ninja publishes a public cryptographic identity so third parties can verify credentials that carry a digital signature. This is the foundation for Open Badges 3.0 / W3C Verifiable Credentials support.

These endpoints are public and require no authentication. They are relative to https://api.badges.ninja.

Issuer DID

The platform's issuer identity is the did:web identifier:

did:web:api.badges.ninja

By the did:web rules, this resolves to the DID document served at the well-known path below.

DID Document

GET /.well-known/did.json

Returns the DID document exposing the issuer's public signing key (an Ed25519 JsonWebKey2020) as its assertionMethod.

bash
curl https://api.badges.ninja/.well-known/did.json
json
{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/jws-2020/v1"
  ],
  "id": "did:web:api.badges.ninja",
  "verificationMethod": [
    {
      "id": "did:web:api.badges.ninja#<kid>",
      "type": "JsonWebKey2020",
      "controller": "did:web:api.badges.ninja",
      "publicKeyJwk": { "kty": "OKP", "crv": "Ed25519", "x": "<base64url>" }
    }
  ],
  "assertionMethod": ["did:web:api.badges.ninja#<kid>"],
  "authentication": ["did:web:api.badges.ninja#<kid>"]
}

Response content type is application/did+json. The document is cacheable (it changes only on key rotation).

JWKS

For verifiers that prefer JWK Set discovery over DID resolution:

GET /.well-known/jwks.json
bash
curl https://api.badges.ninja/.well-known/jwks.json
json
{
  "keys": [
    {
      "kty": "OKP",
      "crv": "Ed25519",
      "x": "<base64url>",
      "kid": "<thumbprint>",
      "alg": "EdDSA",
      "use": "sig"
    }
  ]
}

The kid is the RFC 7638 thumbprint of the public key and matches the fragment on the DID document's verificationMethod id.

Verifiable Credential (Open Badges 3.0)

Every award can be retrieved as an Open Badges 3.0 / W3C Verifiable Credential, signed with the issuer key above.

GET /certify-badge/award/{guid}/vc
QueryResult
(default) or ?format=jwtThe signed VC-JWT (application/jwt) — import into an OB 3.0 wallet.
?format=jsonThe unsigned OpenBadgeCredential JSON (application/vc+ld+json) — for inspection.
bash
# Signed credential (VC-JWT)
curl https://api.badges.ninja/certify-badge/award/<guid>/vc

# Human-readable credential JSON
curl "https://api.badges.ninja/certify-badge/award/<guid>/vc?format=json"

The credential is a ["VerifiableCredential", "OpenBadgeCredential"] typed VC (contexts https://www.w3.org/ns/credentials/v2 and the OB 3.0 context). Its issuer.id is did:web:api.badges.ninja, and the achievement, recipient identity (hashed email), and issue/expiry dates come from the award.

Verifying a VC-JWT

The token is a compact JWS with an EdDSA signature. To verify:

  1. Split the JWT into header.payload.signature.
  2. Read kid from the header — it points into the DID document / JWKS.
  3. Fetch the public key and verify the Ed25519 signature over header.payload.
js
import crypto from "node:crypto";

const [h, p, s] = jwt.split(".");
const jwks = await (await fetch("https://api.badges.ninja/.well-known/jwks.json")).json();
const jwk = jwks.keys[0];
const pub = crypto.createPublicKey({ key: jwk, format: "jwk" });
const ok = crypto.verify(null, Buffer.from(`${h}.${p}`), pub, Buffer.from(s, "base64url"));

Recipients can also grab the credential from the Download menu on their public credential page (Verifiable Credential).

Signature Algorithm

Signatures use Ed25519 (EdDSA). The private key is custodied server-side and never exposed; only the public key above is published.

Hosted Verification (Open Badges 2.0)

Every credential is also independently verifiable today via its hosted Open Badge 2.0 assertion. See Public Verification for the assertion, badge, and issuer JSON endpoints, and Sharing & Verification for the recipient-facing verification page.

badges.ninja Documentation