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

Using the Backpack Connect API

Sue Smith edited this page Jan 19, 2015 · 8 revisions

As an issuer, you can push earner badges to the Mozilla hosted Backpack with their permission. You can do this using the Issuer API or using Backpack Connect. This guide demonstrates using the Backpack Connect approach to send earner badges to their Backpacks.

With the Issuer API, the user must grant permission every time you attempt to push to their Backpack. With Backpack Connect, you can manage user permission on an ongoing basis, using a persistent session. Once the earner has granted you permission to push badges to their Backpack, you will be able to do so without their interaction unless they revoke your permission, which they can do at any time.

To manage your interaction with the earner Backpack, the Connect API uses access tokens.

In order to use the Issuer API, you must have your badge assertions stored as hosted files (at a stable location) or as JSON Web signatures. To check your assertions for validity, use the Validator. To find out more about assertion structure, see the current assertion spec. If you're new to Open Badges, check out Open Badges Onboarding: Issuers and Assertion Information for the Uninitiated.

Contents

Prepare your assertions

Before you attempt to push an earner badge to the Backpack, prepare your assertions - the Connect API accepts both signed and hosted assertions.

Note: The url field in your issuer organization JSON (linked to from the badge class issuer field) must indicate the same domain as the location you request access to the Backpack Connect API from. For example, if you request access to the Connect API for http://example.org, you will only be able to push badges whose issuer url field also indicates http://example.org.

The earner will see your domain listed in their Backpack Settings, together with the level of access they have granted you.

As with the standard Issuer API, your hosted assertions should be at stable URLs accessible via HEAD and GET.

You can validate your assertions (signed or hosted) at the Validator.

Note: Some proposed changes to the assertion specification are currently under discussion. See these threads in particular:

Get user permission

To use the Connect API, you first need to get the earner permission. Include the issuer script in your site or application code:

<script src="https://backpack.openbadges.org/issuer.js"></script>

You can then call the connect method to request permission to access the earner's Backpack, indicating the scope of what you plan on doing with it - in this case issue, which will allow you to push badges to the Backpack:

function connectToBackpack(){
	OpenBadges.connect({
		callback: "http://yoursite.com/callback",
		scope: ['issue']
	});
}

When you call this JavaScript function, the browser will redirect the earner to the Backpack, with the following dialog asking them to log in if necessary:

Backpack Permission

Once signed in, the earner is presented with a dialog asking their permission for your domain to access their Backpack:

Granting Permission

The earner can manage issuer permission to access their Backpack in the Settings section, revoking access whenever they choose:

Backpack Settings

Callback

When the earner grants permission (or denies it), the browser sends data to the callback you included when you called the connect method. At the location you specified, you can retrieve the GET parameters sent from the Backpack to your URL, which include:

Parameter
error If the user denied the issuer access to their backpack, this will be set to access_denied.
access_token A string you can use to access the user's Backpack.
refresh_token A string that can be used to obtain a new access token whenever yours expires.
expires The number of seconds that the access token is valid before it needs to be refreshed.
api_root The absolute URL pointing to the root of the user's backpack connect API endpoint. Note that this won't necessarily point to the openbadges.org domain, since a user's Backpack may be located anywhere on the Web.

See the following node.js example:

app.get('/callback', function(req, res){
	var error = req.param('error');
	var access = req.param('access_token');//use this to push to the earner Backpack
	var refresh = req.param('refresh_token');
	var expiry = req.param('expires');
	var api = req.param('api_root');
	//...

});

When you connect for the first time, you will receive an access token and a refresh token for when the access token expires. Each time you refresh your token, you will receive another access token and another refresh token to use next time.

Maintain access

Each token you receive for accessing the Backpack will only last a short period of time. For example, you may receive an expires value of 3600, meaning that you have 1 hour to use the token. If your token has expired, you can request a new one using the refresh_token you will receive each time you receive an access token.

If you attempt to use the API when your token has expired, you will receive an invalid_token error.

To refresh your token, use the token endpoint at the location you received as api_root, passing the refresh_token and grant_type parameters as in the following node.js example:

var tokenData = querystring.stringify({
			grant_type: 'refresh_token',
			refresh_token: 'your-refresh-token-value'
	});

//where api root is backpack.openbadges.org/api
var requestOptions = {
		host : 'backpack.openbadges.org',
		path : '/api/token', 
		method : 'POST', 
		headers: {'Content-Type': 'application/json',
			'Content-Length': Buffer.byteLength(tokenData)
		}
};

var postRequest = http.request(requestOptions, function(refreshResponse) {

	var response = [];
	refreshResponse.setEncoding('utf8');

		//store data
	refreshResponse.on('data', function (responseData) {
		response.push(responseData);
	});

	refreshResponse.on('end', function(){
		var refreshData=JSON.parse(response.join('')); 
		//...

	});
});

postRequest.on('error', function(e) {
	console.error(e);
});

// post the data
postRequest.write(tokenData);
postRequest.end();

The response will contain the following JSON structure:

{
    "expires": 3600,
    "access_token": "new-access-token",
    "refresh_token": "new-refresh-token"
}

The data items are as follows:

Field
expires Seconds before the new token expires.
access_token Your new access token.
refresh_token Your new refresh token.

Push to the Backpack

When you have your tokens organized, you can push earner badges to the Backpack without their interaction. Use the issue endpoint at the api_location you received from the connect method.

When pushing to the Backpack, include the URL or signature of the assertion as badge parameter.

You also need to authenticate your attempt to push to the Backpack by including the Base64 encoding of your access token as Bearer in the Authorization header of your HTTP request.

See the following node.js example for a hosted badge assertion:

var assertionData = querystring.stringify({
			badge: 'http://yoursite.com/badge-assertion.json'
	});

var requestOptions = {
		host : 'backpack.openbadges.org',//adjust for your api root
		path : '/api/issue', 
		method : 'POST', 
		headers: { 'Authorization': 'Bearer ' + b64enc('your-access-token'),
			'Content-Type': 'application/json',
			'Content-Length': Buffer.byteLength(assertionData)
		}
};

var postRequest = http.request(requestOptions, function(pushResponse) {
	var response = [];
	pushResponse.setEncoding('utf8');

	//store data
	pushResponse.on('data', function (responseData) {
		response.push(responseData);
	});

	pushResponse.on('end', function(){
		var pushData=JSON.parse(response.join('')); 
		//...
	});
});

postRequest.on('error', function(e) {
	console.error(e);
});

// post the data
postRequest.write(assertionData);
postRequest.end();

This alternative would apply to a signed assertion:

/*build sigature*/
var assertion = {

    "uid": "assertion-uid",
    "recipient": {
        "identity": "sha256$hashed-email",
        "type": "email",
        "hashed": true
    },
    "badge": "http://yoursite.com/badge-class.json",
    "verify": {
        "url": "http://yoursite.com/public.pem",
        "type": "signed"
    },
    "issuedOn": 1403784577

};
const signature = jws.sign({
      header: {alg: 'rs256'},
      payload: assertion,
      privateKey: fs.readFileSync(__dirname + '/private.pem')//if the private key is in this directory
    });

/*as above except badge field is signature string instead of url*/
var signatureData = querystring.stringify({
			badge: signature
	});

var requestOptions = {
		host : 'backpack.openbadges.org',//adjust for your api root
		path : '/api/issue', 
		method : 'POST', 
		headers: { 'Authorization': 'Bearer ' + b64enc('your-access-token'),
			'Content-Type': 'application/json',
			'Content-Length': Buffer.byteLength(signatureData)
		}
};

var postRequest = http.request(requestOptions, function(pushResponse) {
	var response = [];
	pushResponse.setEncoding('utf8');

	//store data
	pushResponse.on('data', function (responseData) {
		response.push(responseData);
	});

	pushResponse.on('end', function(){
		var pushData=JSON.parse(response.join('')); 
		//...
	});
});

postRequest.on('error', function(e) {
	console.error(e);
});

// post the data
postRequest.write(signatureData);
postRequest.end();

The Backpack will respond with either success or failure detail. If successful, your response should have the following example JSON structure:

{
    "exists": false,
    "badge": {
        "uid": "assertion-uid",
        "recipient": "sha256$hashed-email",
        "badge": {
            "name": "Badge Name",
            "description": "Badge description.",
            "image": "http://yoursite.com/badge-image.png",
            "criteria": "http://yoursite.com/criteria.html",
            "alignment": [ ],
            "issuer": {
                "name": "Issuer Name",
                "url": "http://yoursite.com",
                "_location": "http://yoursite.com/issuer-organization.json",
                "origin": "http://yoursite.com"
            },
            "_location": "http://yoursite.com/badge-class.json"
        },
        "verify": {
            "url": "http://yoursite.com/public.pem",
            "type": "signed"
        },
        "issuedOn": 1403784577,
        "_originalRecipient": {
            "identity": "sha256$hashed-email",
            "type": "email",
            "hashed": true
        },
        "issued_on": 1403784577
    }
}

If your attempt to push the earner badge to the Backpack was unsuccessful, your response will have the following example structure:

{
    "message": "issuer origin must be identical to bearer token origin"
}

This error message would apply where your issuer organization url does not match the domain for the access token you are using.

You will receive an error response in any of the following circumstances:

  • the assertion is malformed/ invalid
  • no assertion URL or signature is passed
  • the assertion is for a different earner
  • issuer origin does not match access token origin
  • the badge has already been added to the earner Backpack
  • your access token has expired
  • the access token is invalid

Identity

The Connect API also provides the identity endpoint, which allows you to query the Backpack for the current earner's hashed email address. This allows you to issue to the earner without even knowing their email address. See the following node.js example:

var requestOptions = {
	host : 'backpack.openbadges.org',//adjust for your api root
	path : '/api/identity', 
	method : 'GET',
	headers: { 'Authorization': 'Bearer ' + b64enc('your-access-token') }
	};

var idRequest = http.request(requestOptions, function(idResponse) {
	var response = [];
	idResponse.setEncoding('utf8');

	//store data
	idResponse.on('data', function (responseData) {
		response.push(responseData);
	});

	idResponse.on('end', function(){
		var idData=JSON.parse(response.join('')); 
		//...

	});
});

idRequest.on('error', function(e) {
	console.error(e);
});
idRequest.end();

The API will respond with the following structure:

{
    "recipient": "sha256$hashed-email",
    "salt": "_123abc456",
    "type": "email"
}

You could then use the returned hash of the user email to issue a badge to them - the recipient value can be placed directly into your badge assertion JSON as the recipient identity value, with type set to email, hashed set to true and salt set to the received salt value.

Notes

The Connect API is designed in part to accommodate a future in which there may be multiple Open Badges Backpacks - see also the Federated BadgeKit Backpack which is currently under development.

You are encouraged to use HTTPS in your issuing site, although this is not enforced - it is recommended to ensure that your authorization credentials remain confidential.

The API supports Cross Origin Resource Sharing (CORS) for AJAX requests. You can read the CORS W3C working draft or the MDN HTTP Access Control page for more information.

Remember to use application/json as Content-Type for your POST requests.

The Issuer API is an alternative, less complex way to push earner badges to the Backpack - it requires the earner to grant permission each time but involves much less coding on the issuer site.

Clone this wiki locally