API Security & Authentication

Any request made to a Cortex APIs is authenticated so that your business’s data remains secure. In this guide, we’ll walk through security measures including API keys and API signatures.

API Keys

Vidora provides your account with an API Key and an API Secret in order to authenticate each request submitted to the Vidora server. To see the API credentials for your account, navigate to the “API Keys” area of the APIs tab within Cortex.

Signing your API request provides an extra level of security around your data. An API signature is a base64 encoded string which represents information about your API request and authentication credentials, transformed by a cryptographic hash that can be easily verified but cannot be decrypted. A signed request is only valid if the API Key and API Secret used to generate the signature are associated with one another.

While your API Key will be visible in your query parameters when making GET/POST requests, your API Secret should never be exposed to any third party.

API Signatures

When to Use a Signature

Signatures are recommended for server-to-server integrations. If you plan on sending requests from the client (e.g. front-end web applications, mobile devices, etc.), guaranteeing security is often difficult and a signature is not necessary.

Vidora provides flexibility around signature requirements for Cortex’s various APIs. By default, signature requirements are switched off, but if you would like to modify these settings please contact support@vidora.com.

Sending Requests without a Signature

When constructing an HTTP or HTTPs request without a signature, your API call should include the following information. Any special characters included in your final API call must be properly escaped.

  • Base URL: the base URL to which all requests are sent (e.g. http://api.vidora.com).
  • Request Path: the path specific to your query (e.g. /v1/users/123/recommendations).
  • API Key: your API Key (e.g. api_key=<YOUR_KEY>).
  • Parameters: the required and optional parameters that define your request (e.g. category=comedy&limit=10).

Example GET request without a signature

http://api.vidora.com/v1/users/123/recommendations?api_key=<YOUR_KEY>&category=comedy&limit=10

Sending Requests with a Signature

When constructing an HTTP or HTTPs request with a signature, your API call should include the following information. Any special characters included in your final API call must be properly escaped.

  • Base URL: the base URL to which all requests are sent (e.g. http://api.vidora.com).
  • Request Path: the path specific to your query (e.g. /v1/users/123/recommendations).
  • API Key: your API Key (e.g. api_key=<YOUR_KEY>).
  • Expiration: the expiration date of your signature (e.g. expires=2016-01-01T00%3A00).
  • Parameters: the required and optional parameters that define your request (e.g. limit=10).
  • Signature: the base64 encoded string generated to authenticate your request (e.g. signature=<YOUR_SIGNATURE>).

Example GET request with a signature

http://api.vidora.com/v1/users/123/recommendations?api_key=<YOUR_KEY>&expires=2016-01-01T00%3A00&category=comedy&limit=10&signature=<YOUR_SIGNATURE>

Generating a Signature

This section describes how to generate a valid signature specific to your request and suitable to protect your data from third-party tampering. Vidora will validate each request based on the calculated signature to ensure that values were not modified en-route to its API.

The following steps describe the components necessary to build a valid query request with a signature.

Once you’ve generated a signature, you can use the Test Your API Signatures tool within your Cortex Account to test whether or not the signature you’ve provided is valid and matches the one that Cortex expects. The tool will also show the valid output at each step of the signature generation process, allowing you to pinpoint exactly where a mismatch may have occurred.

Step 1: Build a hash of required and optional parameters.

In addition to optional parameters that shape your request, there are two required parameters that must be included in every signed query:

  • api_key: Your API Key.
  • expires: The expiration date of your signature. This must be a string value in UTC time with the format ‘YYYY-MM-DDTHH:MM’. Note that Vidora does not currently support granularity of seconds for expirations.

A params hash including your required parameters and additional category and limit parameters might look like the following:

params = {
    api_key: "<YOUR_KEY>",
    expires: "2016-01-01T00:00",
    category: "comedy",
    limit: "10"
}

Step 2: Build a string containing the sorted list of query parameters.

Sort alphabetically and join the params hash into a string. Note that at this point none of the parameters are URL enpred because they will be integrated into the signature. These parameters will be enpred when they are included separately in the final request string (this is addressed in Step 5).

sorted_params = "api_key=<YOUR_KEY>&category=comedy&expires=2016-01-01T00:00&limit=10"

Step 3: Build a final string containing all the information about your request.

The string_to_sign should contain the following information in the following order, with each piece of information separated by a newline character:

  • api_secret: Your API Secret.
  • http_method: GET, POST, etc.
  • request_path: The path specifix to your query.
  • sorted_params: The sorted_params from Step 2. This may be empty for a POST request.
  • body: The body of a POST request. This will be empty for a GET request.*

Note that within the string_to_sign, special characters in the request_path must be escaped, but special characters in the sorted_params do not need to be escaped. After the signature has been generated, all special characters must be escaped from the final API call.

For example, the special character in the user_id of the following request_path has been escaped:

/v1/users/123%3Aabc/recommendations

GET requests
In our example, the string_to_sign created for a GET request would look like this:

string_to_sign = "08F9113D69E5E913705147D7C882202621B00C79BECF57B434\nGET\n/v1/users/123/recommendations\napi_key=<YOUR_KEY>&category=comedy&expires=2016-01-01T00:00&limit=10\n"

*Note that a newline character is present at the end of the GET request. This is due to the empty string that represents the body of data that would otherwise be submitted with a POST request.

POST requests
The string_to_sign created for a POST request must include JSON-serialized event parameters which serve as the body of your request (this is further explained in the Sending Behavioral Events section.)

The string_to_sign created to POST a ‘click’ event by user 123 on content XYZ might look like this:

string_to_sign = '08F9113D69E5E913705147D7C882202621B00C79BECF57B434\nPOST\n/v1/validate\napi_key=<YOUR_KEY>&expires=2016-01-01T00:00\n{"data":[{"user_id":"123","content_id":"XYZ","type":"click"}]}'

Step 4: Generate the signature.

The process for calculating a final signature can be described in the following steps:

  1. Create a SHA-256 hash of the string created in Step 3.
  2. Enpre the hash value with base64. For some languages it is important to use strict enpre, as standard enpre64 might append an extra (and unwanted) “/n”.
  3. Take the first 43 characters of the resulting base64 enpred string.
  4. Remove any trailing “=” if it exists.

The pre snippets below demonstrate how to turn your string_to_sign into a valid signature using various programming languages.

Ruby

require "digest/sha2"
require "base64"

def generate_signature(secret, http_method, request_path, params = {}, body = nil)
  string_to_sign = [
    secret,
    http_method,
    request_path,
    params.sort { |pair1, pair2| pair1[0] <=> pair2[0] }.map { |k, v| "#{k}=#{v}" }.join("&"),
    body
  ].join("\n")

  Base64::strict_encode64(Digest::SHA256.digest(string_to_sign))[0, 43].chomp("=")
end

Python

import hashlib
import base64
import json

def generate_signature(secret, http_method, request_path, params = None, body = None):
  if params is None:
    params = {}
 
  info_list = [
    secret,
    http_method,
    request_path,
    '&'.join([key + '=' + value for key, value in sorted(params.items())]),
    body
  ]

  string_to_sign = '\n'.join([item for item in info_list if item])
  if body == None:
    string_to_sign += '\n'

  signature = base64.b64encode(hashlib.sha256(string_to_sign.encode('utf-8')) \
                    .digest())[:43].decode('utf-8').rstrip('=')
  return signature

JavaScript

var CryptoJS = require("crypto-js");

function generateSignature(secret, httpMethod, requestPath, params, body) {
  var paramsArr = [];
  var sortedKeys = Object.keys(params).sort();
  for (var i = 0; i < sortedKeys.length; i++) {
    var key = sortedKeys[i];
    var value = params[key];
    if (!value || value.length === 0) {
      paramsArr.push(key);
    }
    else {
      paramsArr.push(key + "=" + decodeURIComponent(value));
    }
  }

  var stringToSign = [
    secret,
    httpMethod,
    requestPath,
    paramsArr.join("&"),
    body
  ].join("\n");

  var hash = CryptoJS.SHA256(stringToSign);
  var hashInBase64 = CryptoJS.enc.Base64.stringify(hash).substring(0, 43);
  if (hashInBase64.endsWith("=")) {
    hashInBase64 = hashInBase64.substring(0, hashInBase64.length - 1);
  }

  return hashInBase64;
}

Step 5: Append the URI-encoded signature to the final request string.

Any special characters in your request path, query parameters, and generated signature must be properly URI escaped before finalizing your API request.  For example, when querying Recommendations for a specific user, you may want to filter the list of returned items to only include those for a specific list of categories. In this case, you will pass over a list of categories within a single parameter. In these instances, it’s required to URI Escape the list to ensure proper functionality of the API Call.

Example GET Request

Here is an example using the User Recommendations API call. Our goal is to return only Recommended items that fall under the categories comedy, drama, or action. Here is how to properly URI Escape this parameter.

Not Escaped: category=comedy&drama&action
Properly Escaped: category=comedy%26drama%26action

And here is how that would appear in the full API Call

http://api.vidora.com/v1/users/<USER_ID>/recommendations?api_key=<YOUR_KEY>&category=comedy%26drama%26action&limit=3&expires=2018-0101T00%3A00&signature=<YOUR_SIGNATURE>

Example POST Request

POST http://api.vidora.com/v1/validate?api_key=<YOUR_KEY>&expires=2016-01-01T00%3A00&signature=<YOUR_SIGNATURE>
Content-Type: application/json
'{"data":[{"user_id":<USER_ID>,"content_id":<CONTENT_ID>,"type":"click"}]}'

Related Links

Still have questions? Reach out to support@vidora.com for more info!

Table of Contents