Security and authentication
Principles
The whole security system for our widget relies on the Authorization header.
The Authorization header is made of up to four space-separated values,
such as : <value1> <value2> <value3> <value4>
This string is passed under the auth field on the config object (see next page)
The first value is the level of permissions
This level signifies the objects the widget is allowed to access and control, there are 3 “levels”:
- apikey - This is the client account security level. The widget is allowed to create candidates for any job of this account and is allowed to create jobs (or just update the default job).
- job - In this level the widget can behave much like a direct link, the widget is allowed to create as many candidates as it wishes but only for this one job_id (see id’s below)
- candidate - This is the most strict and recommended use of the security system, made for invites, in this level the widget is not allowed to create any object at all and is tied to a specific candidate object, once the interview is done, the candidate object (and it’s associated video object) will be “sealed” and no longer accessible
If you choose the apikey
level, you can generate a static token that will be valid forever across any job and candidate that applies.
The second value is the object reference
The second value is the Object ID that the widget is allowed to access (the api_key, the job_id, the candidate_id respectively), this is what ties the widget to a specific object, either the account or a job or a candidate.
The third value is the expiration time (optional)
The next value is optional and starts with “exp=”, this is a timestamp that represents how long the signed authorization header is valid until.
We recommend to put a value of at least 24 hours if you are using this parameter to prevent issues with candidates staying on the page but not applying immediately
exp=<val>
must be an integer that is the seconds from epoch, so in javascript Math.floor(new Date().getTime() / 1000)
The last value in this list is the most important one, the signature
It must start with sig=
to be valid, and is the result of removing all the spaces (appending sig= to the end) and HMAC-SHA256 -ing the authorization header with the secret_key from within the ApiKey
A demonstration of a complete header:
apikey <APIKEY> exp=1653841377 sig=7bdd6651937bb7e6f2b2b550e00a0853e913553e885eab9f7fe13b7fc95db710
You can see below for details of implementations to calculate the signature. This scheme will provide our you with fine-grained server-sided security (since the server should hold the secret_key and never give it to the client)
Implementation examples
The following is a few examples of code that can be used to generate a complete header,
This code should be executed from the server-side (as the secret_key should never be exposed to the client)
NodeJS
const { createHmac } = require("crypto");
function createSignature(objectId, secret_key, expiry, securityLevel = "apikey") {
if (expiry instanceof Date) {
expiry = expiry.getTime() / 1000;
}
const key = `${securityLevel} ${objectId}${expiry ? ' exp=' + expiry : ''} sig=`;
const hmac = createHmac('sha256', secret_key);
hmac.update(key.replace(/\s*/gi, ''));
const signature = hmac.digest('hex');
return `${key}${signature}`;
};
PHP
<?php
function createSignature($objectId, $secret_key, $expiry, $securityLevel = "apikey") {
$key = "$securityLevel $objectId";
if ($expiry) {
$key = "$key exp=$expiry";
}
$key = "$key sig=";
$signature = hash_hmac("sha256", str_replace(" ", "", $key), $secret_key);
return "$key$signature";
}
Python
import hashlib
import hmac
def createSignature(objectId, secret_key, expiry = None , securityLevel = "apikey"):
key = f"{securityLevel} {objectId}"
if (expiry is not None):
key = f"{key} exp={expiry}"
key = f"{key} sig="
byte_secret_key = bytes(secret_key, 'UTF-8')
signature = hmac.new(byte_secret_key, key.replace(" ","").encode() , hashlib.sha256).hexdigest()
return f"{key}{signature}"
C# / .NET
using System;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static void Main()
{
var objId = System.Text.Encoding.UTF8.GetBytes("");
var output = createSignature("yourAPIKEY", "yourSECRETKEY", "");
Console.WriteLine(output);
}
public static string createSignature(string objectId, string secret_key, string expiry, string securityLevel = "apikey") {
var key = string.Format("{0} {1}", securityLevel, objectId);
if (!string.IsNullOrEmpty(expiry)) {
key += string.Format(" exp={0}", expiry);
}
key += " sig=";
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret_key)))
{
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(key.Replace(" ", "")));
key += BitConverter.ToString(hash).Replace("-", "").ToLower();
}
return key;
}
}