Skip to content
This repository has been archived by the owner on Aug 22, 2019. It is now read-only.

Verifying Badges for Display

Sue Smith edited this page Jul 17, 2014 · 12 revisions

If you plan on displaying earner badges within a site, application or widget, you are encouraged to verify the badges first. In this guide we will work through the recommended verification steps - first, check what your options are:

  1. Are you retrieving the badges from the earner's Mozilla hosted Backpack via the Displayer API?
  • Badges returned from the Backpack can be assumed valid so there's no need to carry out additional verification on them!
  1. Are you developing your display implementation in node.js?
  1. Otherwise you can verify badges for display by following the recommended steps.

You will need some familiarity with the JSON badge assertion structure to verify and/or parse the badges you display - if you're new to assertions see these resources:

Overview

To verify an earner badge, you need to:

  • check that the badge assertion is structurally valid
  • check the badge assertion's verification URL
  • check that the badge has not been revoked - only for signed assertions
  • verify the earner identity - which may have been hashed
  • carry out JWS verification using a public key - only for signed assertions

Contents

Using the Validator

You can install the validator in a node.js app via npm as follows:

npm install mozilla/openbadges-validator

Then include it in your app:

var validator = require('openbadges-validator');

The validator provides a range of methods you can see on the repo readme. The following example works for both signed and hosted assertions:

//pass assertion JSON as parameter
validator(assertion, function(err, info){ 
	if(err) console.log(err);
	else if(info) console.log(JSON.stringify(info.version));//for demonstration
});

If validation fails, the err parameter will include details in the following format:

{
	"message":"Issued in error",
	"code":"verify-revoked"
}

In this case the badge has been revoked.

If validation was successful, the validator will return several fields in the info parameter, including:

  • version: Version of the specification that the validated assertion corresponds to.
  • guid: The GUID of the assertion.
  • signature: JSON Web Signature representation of the assertion (if applicable)
  • structures:
  • assertion
  • badge
  • issuer
  • resources

Verification Steps

If you can't use the node.js validator and need to verify badges for display, you will need to implement a number of verification steps - tailored to the type of badge you are dealing with in each case.

Badge Types

Awarded badges can be either hosted or signed. With a hosted badge, the issuer hosts three JSON files representing:

  • the badge assertion
  • the badge class
  • the issuer organization

The badge assertion includes a link to the badge class, which includes a link to the issuer organization. With a hosted badge, the issuer stores the badge assertion at a stable URL and validation will be performed via that URL.

With a signed badge, the issuer provides a JSON Web Signature representing the badge assertion - the badge class and issuer organization are still stored in hosted files. Validation for a signed badge is performed via the signature string.

The assertion structure is discussed in more detail here: Assertion Information for the Uninitiated

In order to verify a badge before displaying it, you will need to establish whether it is signed or hosted, as the verification processes are different. If you're retrieving a hosted badge for display you will be dealing with raw JSON (or a URL hosting a raw JSON file). With a signed badge, you will be dealing with a JSON Web Signature (JWS).

A badge assertion should have the following sample structure:

{
  "uid": "f2c20",
  "recipient": {
    "type": "email",
    "hashed": true,
    "salt": "deadsea",
    "identity": "sha256$c7ef86405ba71b85acd8e2e95166c4b111448089f2e1599f42fe1bba46e865c5"
  },
  "image": "https://example.org/badge-image.png",
  "evidence": "https://example.org/evidence.html",
  "issuedOn": 1359217910,
  "badge": "https://example.org/the-badge-class.json",
  "verify": {
    "type": "hosted",
    "url": "https://example.org/the-badge-assertion.json"
  }
}

The verify.type field indicates whether the badge is signed or hosted.

Verifying Hosted Badges

For a hosted badge, the verification process is as follows:

To verify that the badge assertion is available, meaning that the JSON file representing the awarded badge is available online at the specified URL, displayers should perform a GET request on the location listed in the badge data verify.url field.

  • If the location listed in the verify.url field does not eventually return a 200 OK status, the assertion should be considered invalid.
  • If the URL does return a 200 OK status, displayers can proceed to check the badge structure and check the email address.

To check any expiry date set in the badge assertion, retrieve the timestamp in the expires field and check it against the current date and time.

Verifying Signed Badges

Signed badges involve the badge assertion being packaged into a JSON Web Signature. Verification of signed badges involves a few more steps than with hosted badges.

If you are receiving a badge assertion as a signature, you will need to decode it, unpacking the JWS payload in order to get at the badge assertion structure.

Your approach will depend on the technologies/ languages you are using in your display application. The following demonstrates a technique you could use in Python:

>>> import base64
>>> # payload is second of three sections in signature string
>>> payload = signature.split('.')[1]
>>> payloadlen = len(payload)
>>> # payload string may not have correct padding so add it
>>> for x in range(0, 4-(payloadlen%4)):
	payload+='='
>>> print base64.decodestring(payload)

The payload from the decoded signature should contain a badge assertion (JSON with fields including uid, recipient, badge etc). If the payload does not parse as a JSON object, the badge assertion should be considered invalid.

You can then use the assertion JSON for a signed badge to validate its content. At this stage you can check the badge structure and earner email address, then verify the badge assertion using the public key that should be listed in the verify.url field.

JWS Verification

Signed badge assertions should include the URL for the public key corresponding to the private key used for signing - in the verify.url field. You can carry out a GET request on the URL to retrieve the public key - if the key cannot be retrieved (i.e. it doesn't eventually return a 200 OK status response), the badge should be considered invalid.

Once you have retrieved the public key from the verify.url location, you can attempt to carry out verification on the JWS object. Again, your approach will be determined by your implementation language/ technology.

Checking Badge Structure

To verify that a badge assertion has a valid structure, your code needs to establish the following:

  • the badge field includes a valid URL which eventually returns a 200 OK response
  • the recipient value is an object:
  • type is email (only value supported at the moment)
  • identity is text
  • hashed (optional) is boolean
  • salt (optional) is text
  • image (optional) is a valid URL or Data URL
  • evidence (optional) is a valid URL
  • issuedOn (optional) is a DateTime
  • expires (optional) is a DateTime
  • verify is an object:
  • type is either hosted or signed
  • url is a valid URL

You will naturally need to carry out structural validation using a method to suit the technology or language you are building your display implementation in.

The following demonstrates checking the http status of the URL in the badge field in Python:

>>> import requests
>>> import json
>>> # assertion has badge assertion json in it
>>> badgeclass=json.loads(assertion)['badge']
>>> print badgeclass
http://example.org/badge-class.json
>>> badgereq=requests.get(badgeclass)
>>> print badgereq.status_code
200

Once you've finished checking for structural validity, you can then check the earner email address.

Checking Earner Email Addresses

A badge assertion includes information about the recipient identity in the recipient object. The fields can include:

  • type
  • hashed
  • salt
  • identity

The type should be email, with the address value in the identity field. The email address may be included as plain text, or may be hashed for additional security. If hashed, the email address may also have been salted, in which case the salt value will be provided.

If an earner is claiming a badge using their email address, you can therefore check that against the email address stored in the assertion recipient object. To do this in cases where the email address has been hashed, you can hash the email address of the claimed earner (with salt if appropriate) and compare it to the (salted) hash in the recipient object.

The following demonstrates the process in Python:

>>> 'sha256$' + hashlib.sha256(claimedEmail + salt).hexdigest();

In this case claimedEmail is the address of the earner claiming the badge and salt is the value in the recipient.salt field of the assertion (if provided). You can compare the output to the identity value in the assertion - if they match, the earner identity is verified. See also How to hash and salt in various languages.

If you're dealing with a signed badge, once you've finished checking structural validity and the earner email, you can then carry out JWS verification on the assertion.

Overview

To recap, these are the verification steps for a hosted badge:

  • carry out GET request on verify.url to check for 200 status
  • check badge assertion structure
  • check earner email

For a signed badge:

  • unpack assertion from signature payload
  • check badge assertion structure
  • check earner email
  • retrieve public key from verify.url
  • perform JWS verification
  • check revocation list

You can carry the steps out in a way that suits your display implementation.

Notes

  • It is strongly recommended that a display implementation show the verify.url, with the origin (protocol, hostname, port if non-default) highlighted.
  • When checking relevant URLs for availability, the implication is that 3xx redirects are allowed, as long as the request eventually terminates on a resource that returns a 200 OK status.

See the assertion specification for more on verification.

Clone this wiki locally