All calls to the Project API require authentication. Assuming you've set up an application in your account already, it is time to make your first call to the API.
Initial Steps
There are a few security steps that must be performed for any request to the API:
- Firstly a cryptographic nonce must be obtained from the API. This is done by making a request to any endpoint without a nonce in the request. The nonce protects your requests from being resent by malicious parties, or in the event of an unexpected technical issue.
- An Authorization header needs to be created, and it should initially include an 'oauth_consumer_key' (the Public API key presented when you added an application), 'oauth_timestamp' and 'oauth_nonce' (If the nonce is missing, the API will respond with a nonce).
- To make a request to the API, a signature must be included.
- The signature validates that the request has not been tampered with. A signature can be created using the following instructions:
1. Create a string representation of the request.
The format of the string is as follows:
<http method>&<url>&<all params>
where:
- 'http method' can be 'POST' or 'GET',
- 'url' is the full URL to be called
- 'all params' is an alphabetically ordered list of the authorization headers and any GET/POST parameters in the format of a query string.
For example:
GET&http://api.elucidat.com/v2/account&oauth_consumer_key=my-consumer-key&oauth_nonce=my-nonce&oauth_timestamp=1434557774
2. Generate a HMAC (hash-based message authentication code) of the string representation using a SHA1 hashing algorithm with a url encoded version of your secret key.
3. Base 64 encode the HMAC.
You can then package the signature in the authorization headers as 'oauth_signature'
If you've done all of the above, you should have a crytopgraphic nonce you can use for the call which follows. To make life easier, we've included some examples of this process below.
Examples:
PHP
/**
<?php
/**
* Makes an API request to elucidat
* @param $headers
* @param $fields
* @param $url
* @param $consumer_secret
* @return mixed
*/
function call_elucidat($headers, $fields, $method, $url, $consumer_secret)
{
//Build a signature
$headers['oauth_signature'] = build_signature($consumer_secret, array_merge($headers, $fields) , $method, $url);
//Build OAuth headers
$auth_headers = 'Authorization:';
$auth_headers .= build_base_string($headers, ',');
//Build the request string
$fields_string = build_base_string($fields, '&');
//Set the headers
$header = array(
$auth_headers,
'Expect:'
);
// Create curl options
if (strcasecmp($method, "GET") == 0)
{
$url .= '?' . $fields_string;
$options = array(
CURLOPT_HTTPHEADER => $header,
CURLOPT_HEADER => false,
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false
);
}
else
{
$options = array(
CURLOPT_HTTPHEADER => $header,
CURLOPT_HEADER => false,
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_POST => count($fields) ,
CURLOPT_POSTFIELDS => $fields_string
);
}
//Init the request and set its params
$request = curl_init();
curl_setopt_array($request, $options);
//Make the request
$response = curl_exec($request);
$status = curl_getinfo($request, CURLINFO_HTTP_CODE);
curl_close($request);
return array(
'status' => $status,
'response' => json_decode($response, true)
);
}
/**
* Each request to the elucidat API must be accompanied by a unique key known as a nonce.
* This key adds an additional level of security to the API.
* A new key must be requested for each API call.
* @param $api_url
* @param $consumer_key
* @param $consumer_secret
* @return bool
*/
function get_nonce($api_url, $consumer_key, $consumer_secret)
{
// Start with some standard headers, unsetting the oauth_nonce. without the nonce header the API will automatically issue us one.
$auth_headers = auth_headers($consumer_key);
unset($auth_headers['oauth_nonce']);
//Make a request to elucidat for a nonce...any url is fine providing it doesnt already have a nonce
$json = call_elucidat($auth_headers, array() , 'GET', $api_url, $consumer_secret);
if (isset($json['response']['nonce']))
{
return $json['response']['nonce'];
}
return false;
}
/**
* Computes and returns a signature for the request.
* @param $secret
* @param $fields
* @param $request_type
* @param $url
* @return string
*/
function build_signature($secret, $fields, $request_type, $url)
{
ksort($fields);
//Build base string to be used as a signature
$base_info = $request_type . '&' . $url . '&' . build_base_string($fields, '&'); //return complete base string
//Create the signature from the secret and base string
$composite_key = rawurlencode($secret);
return base64_encode(hash_hmac('sha1', $base_info, $composite_key, true));
}
/**
* Builds a segment from an array of fields. Its used to create string representations of headers and URIs
* @param $fields
* @param $delim
* @return string
*/
function build_base_string($fields, $delim)
{
$r = array();
foreach ($fields as $key => $value)
{
$r[] = rawurlencode($key) . "=" . rawurlencode($value);
}
return implode($delim, $r); //return complete base string
}
/**
* Returns typical headers needed for a request
* @param $consumer_key
* @param $nonce
*/
function auth_headers($consumer_key, $nonce = '')
{
return array(
'oauth_consumer_key' => $consumer_key,
'oauth_nonce' => $nonce,
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_timestamp' => time() ,
'oauth_version' => '1.0'
);
}
$nonce = get_nonce('https://api.elucidat.com/v2/projects', 'PUBLIC_KEY', 'PRIVATE_KEY');
$headers = auth_headers('PUBLIC_KEY', $nonce);
$fields = array(
'simulation_mode' => 'simulation'
);
$result = call_elucidat($headers, $fields, 'GET', 'https://api.elucidat.com/v2/projects', 'PRIVATE_KEY');
echo ("HTTP status code: " . $result['status'] . "\n");
print_r($result['response']);
Python 3.9
(Please note, you may need to install the Requests module manually to use this code. Keys should be inserted where indicated at the bottom of the code block.)
import base64
import hmac
import json
import requests
import time
import urllib
from collections import OrderedDict
from hashlib import sha1
from urllib import parse
from pprint import pprint
class Api:
"""
Core API functions
"""
def __init__(self, api_root, endpoint, method, params, data, private_key, public_key):
self.api_root = api_root
self.endpoint = endpoint
self.method = method
self.private_key = private_key
self.public_key = public_key
self.headers = {}
self.data = data
self.params = params
self.nonce = ''
self.response = None
def send_request(self):
url = self.api_root + self.endpoint
self.response = requests.request(self.method, url, data=self.data, headers=self.headers, params=self.params)
return self.response
@staticmethod
def build_signature(private_key: str, auth_header: dict, params: dict, data: dict, request_type: str, url: str):
auth_header.update(params)
auth_header.update(data)
ordered_header = dict(OrderedDict(sorted(auth_header.items())))
ordered_header = urllib.parse.urlencode(ordered_header, quote_via=urllib.parse.quote)
base_string = str(ordered_header).replace(": ", "=").replace(", ", "&").replace("'", "").strip("{}")
base_info = (request_type + "&" + url + "&" + base_string).encode('utf8')
encoded_key = (urllib.parse.quote_plus(private_key)).encode('utf8')
hashed = hmac.new(encoded_key, base_info, sha1).digest()
signature = urllib.parse.quote_plus(base64.b64encode(hashed).decode())
return signature
def _get_nonce(self):
timestamp = str(int(time.time()))
auth_header = {'oauth_consumer_key': self.public_key,
'oauth_timestamp': timestamp,
'oauth_signature_method': 'HMAC-SHA1',
'oauth_version': '1.0'}
url = self.api_root + self.endpoint
oauth_signature = self.build_signature(self.private_key, auth_header, self.params, self.data, self.method, url)
self.headers = {'Authorization': f'oauth_consumer_key={self.public_key},oauth_signature_method=HMAC-SHA1,'
f'oauth_timestamp={timestamp},oauth_version=1.0,'
f'oauth_signature={oauth_signature}'}
self.send_request()
body = json.loads(self.response.text)
nonce = body.get('nonce')
return nonce
def call_elucidat(self):
timestamp = str(int(time.time()))
self.nonce = self._get_nonce()
auth_header = {'oauth_consumer_key': self.public_key,
'oauth_signature_method': 'HMAC-SHA1',
'oauth_nonce': self.nonce,
'oauth_timestamp': timestamp,
'oauth_version': '1.0'}
url = self.api_root + self.endpoint
oauth_signature = self.build_signature(self.private_key, auth_header, self.params, self.data, self.method, url)
self.headers = {
'Authorization': f'oauth_consumer_key={self.public_key},oauth_nonce={self.nonce},'
f'oauth_signature_method=HMAC-SHA1,oauth_timestamp={timestamp},'
f'oauth_version=1.0,oauth_signature={oauth_signature}'}
response = self.send_request()
pprint("Response code: " + str(response.status_code))
pprint("Response text: " + response.text)
if __name__ == '__main__':
private_key = '[PRIVATE_KEY]'
public_key = '[PUBLIC_KEY]'
api_root = 'https://api.elucidat.com'
Response
[
{
"project_code": "60b64z4491fa5",
"name": "Audio Test",
"created": "2021-09-01 15:07:48",
"project_views": "0",
"project_views_this_month": "0",
"project_passes": "0",
"project_fails": "0",
"theme_skin_id": "97898",
"name_in_release": "",
"language": "en-GB",
"theme": "master_theme",
"theme_version_number": "1",
"scorm_mode": "1.2",
"lrs_endpoint": null,
"lrs_endpoint_username": null,
"learner_access": "any",
"project_description": null,
"use_learner_service": "0",
"edit_url": "https://app.elucidat.com/projects/edit/60b64z4491fa5",
"type": "project"
}
]