How Can We Help?

API Security & Authentication

Any request made to one of Cortex’s APIs is authenticated so that your business’s data remains secure. Cortex offers two options for authenticating your API requests: Basic Authentication and API Signatures. In this guide, we’ll walk through how to use either of these security measures.

API Credentials

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

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

When to Use Authentication

Authentication is 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 authentication is not necessary. The below table provides details on default authentication requirements for Cortex’s various APIs. Cortex provides flexibility on the requirements marked with an asterisk, so if you would like to modify these settings please contact support@mparticle.com.

Cortex API Auth Required? Supports Basic Auth? Supports Signatures?
Behavioral API No* No Yes
Predictions API Yes* Yes Yes
Personalization API Yes* Yes Yes
Pipelines API Yes Yes Yes
User Privacy API Yes Yes Yes
Items API Yes Yes Yes

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. Cortex 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@mparticle.com.

Basic Authentication

Basic Authentication is an HTTP security framework which requires a username and password to be submitted in an “Authorization” header field when a client makes a request to the server. Because the credentials in this field are not encrypted, Cortex only supports Basic Authentication over HTTPS (https://api-us.vidora.com) so that your data remains secure as it’s transmitted across the network. Note that API signatures provide an even more secure option, since your secret key will never be sent via HTTP. To use Basic Authentication, include in your request a header field of the form Authorization: Basic <credentials>, where <credentials> is your API key and API secret joined by a colon. See below for a basic example using the curl commandline tool. Example Basic Authentication

curl -u "<API_KEY>:<API_SECRET>"
"https://api-us.vidora.com/v1/users/123/recommendations?category=comedy&limit=10"

API Signatures

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. 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-us.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-us.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-us.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-us.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. Cortex 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 Cortex 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 encoded because they will be integrated into the signature. These parameters will be encoded 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. Encode the hash value with base64. For some languages it is important to use strict encode, as standard encode64 might append an extra (and unwanted) “\n”.
  3. Take the first 43 characters of the resulting base64 encoded 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-us.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-us.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@mparticle.com for more info!

Table of Contents