How to Make an API Call With Canvas Data

Document created by smccann@instructure.com Employee on Apr 12, 2016Last modified by Eric Coan on Nov 10, 2017
Version 11Show Document
  • View in full screen mode


Overview

Making an API call with Canvas Data is not the same process as making an API call within Canvas. The process is more in-depth, technical, and secure. This does require some technical knowledge. For each call you want to make you MUST do the following three steps.

 

STEP 1 - Build Message

 

Generate API Credentials from Canvas Data Portal

These 8 items need to be IN ORDER as an list/array separated by a /n (new-line)

 

  1. HTTP METHOD: GET

    Canvas Data currently only supports the GET method and has no API calls for POST, PUT, DELETE

  2. HOST: portal.inshosteddata.com

    It will always be this URL
  3. conent_type: application/json
  4. contentMD5:
    It will always be blank for GET requests within Canvas Data
  5. path: path for call

    e.g. /api/schema or /api/account/self/file/latest

  6. parameters need to be sorted alphabetical and joined with &
    after and limit

    are the only supported parameters for /api/account/self/dump and /api/account/self/file/byTable/:tableName.

  7. HTTP Date: this can be in either RFC 7231 (Tue, 01 Dec 2015 05:22:24 GMT) or in ISO-8601 format (2015-12-01T05:22:24.324Z)
    • Most languages have a date() or similar function that will generate this for you.

    • Remember to use UTC or GMT time and not the local time.
  8. API Secret you generated from Canvas Data Portal

 

Example 1.1: An Example Signature

GET  portal.inshosteddata.com application/json

/api/account/self/dumpafter=20&limit=10Thu, 07 Apr 2016 14:44:29 GMT<Canvas_data_Secret>

 

STEP 2 - Create Encoded Hash Signature


Canvas Data requires that the signature you created from Step 1 needs to represented as  HMAC-SHA-256 hash that is base64 encoded.

 

  1. Use any programming language’s digest method to take the signature from Step 1 and convert to HMAC sha256

  2. The byte response from the digest method then needs to be base64 encoded

 

NOTE: If there is a  /n at the end, remove it


Example 1.2: An Example Signature from Step #1 after going through HMAC and base 64 encoding

 

4MGJAn+LX1tqlC7JT1qfVNG61l4URPFxN70hW02g8n0=



STEP 3 - Generate Headers

 

Now that we have the message and encoded signature from steps 1 and 2, we can put these both together as a header to make our API call.


Authorization: HMACAuth <api_key>:<signature>Date: Same timestamp from step 1.7Content-type: application/json


Your return response will be in JSON format.


Example 1.3: Full Request

{"Authorization: HMACAuth <canvas_data_api_key>:4MGJAn+LX1tqlC7JT1qfVNG61l4URPFxN70hW02g8n0=", "Date:Thu, 07 Apr 2016 14:50:51 GMT", "Content-Type:application/json"}

 

Examples

 

Node JS/JS

var crypto = require('crypto')
var url = require('url')
var HMAC_ALG = 'sha256' 
var apiAuth = module.exports = { 
  buildMessage: function(secret, timestamp, uri) { 
    var urlInfo = url.parse(uri, false); 
    var query = urlInfo.query ? urlInfo.query.split('&').sort().join('&') : ''; 
    var parts = [ 'GET', urlInfo.host, '', '', urlInfo.pathname, query, timestamp, secret ] 
    return parts.join('\n') 
  }, 
  buildHmacSig: function(secret, timestamp, reqOpts) { 
    var message = apiAuth.buildMessage(secret, timestamp, reqOpts) 
    var hmac = crypto.createHmac(HMAC_ALG, new Buffer(secret)) 
    hmac.update(message) 
    return hmac.digest('base64') 
  } 
} 

Java

Github Java Example - harvard-data-tools/RestUtils.java at master · penzance/harvard-data-tools · GitHub


Ruby

Ruby Gem - https://github.com/ben-y/canvas_data_client


PHP

<?php

function curl_execs($url, $headers) {
  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

  $server_output = curl_exec($ch);
  curl_close($ch);
  return $server_output;
}

function generate_http_headers($uri_path) {
  $timestamp = gmdate( 'D, d M Y H:i:s T' );
  $url = 'https://portal.inshosteddata.com/api/' . $uri_path;

  $api_key = getenv('CANVAS_DATA_API_KEY');
  $api_secret = getenv('CANVAS_DATA_API_SECRET');

  // Generate signature
  $hmac = hmac_signature( $timestamp, $url, $api_secret );

  return array (
    'Authorization: HMACAuth ' . $api_key . ':' . $hmac,
    'Date: ' . $timestamp,
    'Content-Type: application/json'
  );
}

function hmac_signature($timestamp = '', $url = '', $secret = '') {
  if (empty($timestamp) || empty($url) || empty($secret)) {
    return '';
  }
  $u = parse_url($url);
  if ($u === FALSE) {
    return '';
  }
  $host = !empty($u['host']) ? $u['host'] : 'portal.inshosteddata.com';
  $path = $u['path'];
  $query = '';
  if (! empty( $u['query'] )) {
    $parms = explode('&', $u['query']);
    sort($parms);
    $query = implode('&', $parms);
  }
  $parts = array ('GET', $host, 'application/json', '', $path, $query, $timestamp, $secret);
  $message = implode("\n", $parts);
  return base64_encode(hash_hmac('sha256', $message, $secret, TRUE));
}

function main($argv) {
  $headers = generate_http_headers("account/self/dump?limit=100&after=45");
  $results = curl_execs("https://portal.inshosteddata.com/api/account/self/dump?limit=100&after=45", $headers);
  var_dump($results);
}

main($argv);

 

HHVM

 

<?hh

async function curl_exec_await(string $url, array<string> $headers): Awaitable<string> {
  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

  $mh = curl_multi_init();
  curl_multi_add_handle($mh, $ch);

  $active = 0;
  do {
      $status = curl_multi_exec($mh, $active);
      await RescheduleWaitHandle::create(
          RescheduleWaitHandle::QUEUE_DEFAULT,
          0,
      );
  } while ($status == CURLM_CALL_MULTI_PERFORM || $active > 0);
  await curl_multi_await($mh);

  $content = (string)curl_multi_getcontent($ch);
  curl_multi_remove_handle($mh, $ch);
  curl_multi_close($mh);
  return $content;
}

function generate_http_headers(string $uri_path): array<string> {
  $timestamp = gmdate( 'D, d M Y H:i:s T' );
  $url = 'https://portal.inshosteddata.com/api/' . $uri_path;

  $api_key = getenv('CANVAS_DATA_API_KEY');
  $api_secret = getenv('CANVAS_DATA_API_SECRET');

  // Generate signature
  $hmac = hmac_signature( $timestamp, $url, $api_secret );

  return array (
    'Authorization: HMACAuth ' . $api_key . ':' . $hmac,
    'Date: ' . $timestamp,
    'Content-Type: application/json'
  );
}

function hmac_signature(string $timestamp = '', string $url = '', string $secret = ''): string {
  if (empty($timestamp) || empty($url) || empty($secret)) {
    return '';
  }
  $u = parse_url($url);
  if ($u === FALSE) {
    return '';
  }
  $host = !empty($u['host']) ? $u['host'] : 'portal.inshosteddata.com';
  $path = $u['path'];
  $query = '';
  if (! empty( $u['query'] )) {
    $parms = explode('&', $u['query']);
    sort($parms);
    $query = implode('&', $parms);
  }
  $parts = array ('GET', $host, 'application/json', '', $path, $query, $timestamp, $secret);
  $message = implode("\n", $parts);
  return base64_encode(hash_hmac('sha256', $message, $secret, TRUE));
}

function main($argv) {
  $headers = generate_http_headers("account/self/dump?limit=100&after=45");
  $results = HH\Asio\join(HH\Asio\v(array(
    curl_exec_await("https://portal.inshosteddata.com/api/account/self/dump?limit=100&after=45", $headers)
  )));
  var_dump($results);
}

main($argv);

 

PERL

#!/usr/bin/perl  
 
# This is a code snippet 
 
# Standard modules 
 
use strict; 
use warnings; 
use diagnostics; 
use Carp; 
 
# Specific modules required for signature 
 
use URI; 
use HTTP::Date; 
use Digest::SHA qw{hmac_sha256_base64}; 
 
# Minimal information needed to generate signature 
 
my $timestamp = time2str(); 
my $url    = '
https://portal.inshosteddata.com/api/account/self/dump?limit=100';
 

my $api_secret = 'YOUR SUPER SECRET API SECRET GOES HERE'; 
 
# Generate signature 
 
my $hmac = hmac_signature( $timestamp, $url, $api_secret ); 
printf("HMAC signature: %s\n", $hmac); 
 
# Headers to add to request 
 
my $api_key = 'YOUR API KEY GOES HERE'; 
my $headers = { 
  'Authorization' => 'HMACAuth ' . $api_key . ':' . $hmac, 
  'Date'          => $timestamp, 
}; 
 
1; 
 
# Subroutine to generate the proper signature 
# This is a whittled down version from what Canvas suggested.  
# Currently, the Content-type and Content-MD5 are not used 
 
sub hmac_signature { 
  my $timestamp = shift || return; 
  my $url       = shift || return; 
  my $secret    = shift || return; 
  my $uri       = URI->new($url); 
  my $host = defined( $uri->host ) ? $uri->host : 'portal.inshosteddata.com'; 
  my $query = $uri->query || ''; 
  if ( $query ne '' ) { 
    $query = join( '&', sort( split( '&', $query ) ) ); 
  } 
  my $parts = 
    [ 'GET', $host, '', '', $uri->path, $query, $timestamp, $secret, ]; 
  my $message = join( "\n", @{$parts} ); 
  my $hmac = hmac_sha256_base64( $message, $secret ); 
 
  # The HMAC implementation doesn't enforce the proper length. 
  # Pad the end with = signs until the length is a multiple of 4 
  while ( length($hmac) % 4 ) { 
    $hmac .= '='; 
  } 
  return $hmac; 
} 


Python: 

GitHub - Harvard-University-iCommons/canvas-data-sdk: Python SDK and command-line tool for working with the Canvas Data … 

3 people found this helpful

Attachments

    Outcomes