Skip to main content

Authentication

Overview​

The ONLIVE.SITE API uses a custom authentication scheme based on a signature mechanism similar to AWS Signature v4. Every authenticated request requires your API credentials (Access Key ID and Secret Access Key) and a properly calculated signature.

Prerequisites​

Before you begin, you need:

  • An ONLIVE.SITE Access Key ID
  • An ONLIVE.SITE Secret Access Key

These credentials are provided to you when you register for the ONLIVE.SITE API. Keep your Secret Access Key secure and never share it.

Authentication Process Overview​

  1. Create a timestamp for your request
  2. Add required headers to your request
  3. Create a canonical request string
  4. Calculate the signature using your Secret Access Key
  5. Add the authorization header with your Access Key ID and signature

Detailed Steps​

Step 1: Add a Timestamp Header​

Every request must include a timestamp in the x-onlive-site-date header. The timestamp must be in the format YYYYMMDDTHHmmssZ where:

  • YYYY is the four-digit year
  • MM is the two-digit month (01-12)
  • DD is the two-digit day (01-31)
  • HH is the two-digit hour in 24-hour format (00-23)
  • mm is the two-digit minute (00-59)
  • ss is the two-digit second (00-59)
  • Z indicates UTC timezone

For example: 20250526T143022Z represents May 26, 2025, at 14:30:22 UTC.

Important: The timestamp must be within 15 minutes of the ONLIVE.SITE server time, or the request will be rejected.

Node.js example for generating the timestamp:​

/**
* Pads a number with leading zeros to ensure it is two digits.
* @param {number} number - The number to pad.
* @returns {string} - The padded number as a string.
*/
function pad(number) {
return `${("00" + number).slice(-2)}`;
}

const now = new Date();

const date = `${now.getUTCFullYear()}${pad(now.getUTCMonth() + 1)}${pad(
now.getUTCDate()
)}T${pad(now.getUTCHours())}${pad(now.getUTCMinutes())}${pad(
now.getUTCSeconds()
)}Z`;

// Add the header to your request
const headers = {
"x-onlive-site-date": date,
};

Step 2: Create the String to Sign​

The string to sign consists of five components, each separated by a newline character (\n):

HTTPVerb\n
CanonicalHeaders\n
CanonicalResourceURI\n
CanonicalQueryString\n
HashedPayload

2.1 HTTPVerb​

The HTTP method of your request (GET, POST, PUT, DELETE, etc.) in uppercase.

2.2 CanonicalHeaders​

A string containing all header names starting with x-onlive-site- (in lowercase), sorted alphabetically, with their values. Each header and value pair should be separated by a colon (:), and each pair should be on a new line.

Node.js example:

/**
* Gets the canonical headers for the request.
* @param {Object} headers - The headers object with key-value pairs.
* @returns {string} - The canonical headers string formatted for the signature.
*/
function getCanonicalHeaders(headers) {
// Step 1: Extract all x-onlive-site-* headers
const onliveHeaders = [];

// For Node.js object headers
Object.keys(headers).forEach((key) => {
if (key.toLowerCase().startsWith("x-onlive-site-")) {
onliveHeaders.push({
name: key.toLowerCase(),
value: headers[key].trim(),
});
}
});

// Step 2: Sort headers alphabetically by name
onliveHeaders.sort((a, b) => a.name.localeCompare(b.name));

// Step 3: Format as name:value joined by newlines
return onliveHeaders
.map((header) => `${header.name}:${header.value}`)
.join("\n");
}

// Example usage:
// HTTP headers for request
const headers = {
"Content-Type": "application/json",
"x-onlive-site-date": "20250526T143022Z",
"x-onlive-site-custom": "some-value",
};

const canonicalHeaders = getCanonicalHeaders(headers);
// Result: "x-onlive-site-custom:some-value\nx-onlive-site-date:20250526T143022Z"

2.3 CanonicalResourceURI​

The absolute path component of the URI (without protocol, domain, or query string).

For example, if the full URL is https://openapi.onlive.site/api/v1/presets, then the CanonicalResourceURI is /api/v1/presets.

2.4 CanonicalQueryString​

A string representation of the query parameters, URL-encoded and sorted alphabetically by parameter name. If there are no query parameters, use an empty string.

Node.js example:

function getCanonicalQueryString(queryParams = {}) {
const params = [];

// Process the query parameters object
for (const [key, value] of Object.entries(queryParams)) {
params.push({
name: encodeURIComponent(key),
value: encodeURIComponent(value),
});
}

// Sort alphabetically by name
params.sort((a, b) => a.name.localeCompare(b.name));

// Join as name=value pairs with &
return params.length > 0
? params.map((param) => `${param.name}=${param.value}`).join("&")
: "";
}

// Example usage:
const queryParams = {
title: "Demo Preset",
sort: "asc",
limit: "10",
};

const canonicalQueryString = getCanonicalQueryString(queryParams);
// Result: "limit=10&sort=asc&title=Demo%20Preset"

2.5 HashedPayload​

The hexadecimal string representation of the SHA-256 hash of the request body. If there's no request body, use the hash of an empty string.

Node.js example:

const crypto = require("crypto");

const hashedPayload = crypto
.createHash("sha256")
.update(requestBody || "")
.digest("hex");

Complete String to Sign Example:​

/**
* Creates the string to sign for ONLIVE.SITE API signature calculation
* @param {string} method - HTTP method (GET, POST, PUT, DELETE, etc.)
* @param {string} path - Resource path without domain or query string
* @param {Object} queryParams - Query parameters as key-value pairs
* @param {Object} headers - Request headers as key-value pairs
* @param {string} body - Request body as string or empty string
* @returns {string} - The formatted string to sign
*/
function stringToSign(method, path, queryParams, headers, body) {
const httpVerb = method.toUpperCase();
const canonicalHeaders = getCanonicalHeaders(headers);
const canonicalResourceURI = path;
const canonicalQueryString = getCanonicalQueryString(queryParams);

// Calculate hash of request body (if any)
const hashedPayload = crypto
.createHash("sha256")
.update(body || "")
.digest("hex");

return `${httpVerb}\n${canonicalHeaders}\n${canonicalResourceURI}\n${canonicalQueryString}\n${hashedPayload}`;
}

Step 3: Calculate the Signature​

Calculate the HMAC-SHA256 hash of the string to sign using your Secret Access Key, and convert the result to a hexadecimal string.

Node.js example:

const crypto = require("crypto");

const signature = crypto
.createHmac("sha256", secretKey)
.update(stringToSign)
.digest("hex");

Step 4: Add the Authorization Header​

Create an authorization header with your Access Key ID and the calculated signature:

Authorization: ONLIVESITE Credential=your_access_key_id, Signature=calculated_signature

Node.js example:

headers[
"Authorization"
] = `ONLIVESITE Credential=${accessKeyId}, Signature=${signature}`;

Complete Example​

Here's a complete example that demonstrates how to authenticate a request to the ONLIVE.SITE API:

Example Request:​

GET /api/v1/presets?sort=asc&title=demo HTTP/1.1
Host: openapi.onlive.site
Content-Type: application/json
x-onlive-site-date: 20250526T143022Z

Step-by-Step Authentication​

1. Define Request Parameters​

// HTTP method
const method = "GET";

// Resource path
const path = "/api/v1/presets";

// Query parameters (as an object)
const queryParams = {
sort: "asc",
title: "demo",
};

// Headers
const headers = {
"Content-Type": "application/json",
"x-onlive-site-date": "20250526T143022Z",
};

// Request body (empty in this example)
const body = "";

2. String to Sign Components​

  • HTTPVerb: GET
  • CanonicalHeaders: x-onlive-site-date:20250526T143022Z
  • CanonicalResourceURI: /api/v1/presets
  • CanonicalQueryString: sort=asc&title=demo (from sorting and encoding the queryParams object)
  • HashedPayload: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 (SHA-256 hash of an empty string)

3. Generate the String to Sign​

// Generate canonical headers
const canonicalHeaders = getCanonicalHeaders(headers);

// Generate canonical query string
const canonicalQueryString = getCanonicalQueryString(queryParams);

// Calculate hashed payload
const crypto = require("crypto");
const hashedPayload = crypto
.createHash("sha256")
.update(body || "")
.digest("hex");

// Create the string to sign
const stringToSign = `${method}\n${canonicalHeaders}\n${path}\n${canonicalQueryString}\n${hashedPayload}`;

console.log(stringToSign);
/*
Result:
GET
x-onlive-site-date:20250526T143022Z
/api/v1/presets
sort=asc&title=demo
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
*/

4. Calculate Signature (using the Secret Access Key)​

// Assuming secretKey is '0eee568a0ff563fc93232fc15dcfa886b5f331bc21c460bf1823db9ced60dc66'
const accessKeyId = "8dd4935890402ffb06b667a7c532e0cd";
const secretKey =
"0eee568a0ff563fc93232fc15dcfa886b5f331bc21c460bf1823db9ced60dc66";

const signature = crypto
.createHmac("sha256", secretKey)
.update(stringToSign)
.digest("hex");
// Result: a8f7e5b362a3c8ff61a5317813000c6a3cf83f0b387196f8d7aa9f53c54d0b86 (example value)

5. Add Authorization Header​

Authorization: ONLIVESITE Credential=8dd4935890402ffb06b667a7c532e0cd, Signature=a8f7e5b362a3c8ff61a5317813000c6a3cf83f0b387196f8d7aa9f53c54d0b86

Final Request with Authentication​

GET /api/v1/presets?sort=asc&title=demo HTTP/1.1
Host: openapi.onlive.site
Content-Type: application/json
x-onlive-site-date: 20250526T143022Z
Authorization: ONLIVESITE Credential=8dd4935890402ffb06b667a7c532e0cd, Signature=a8f7e5b362a3c8ff61a5317813000c6a3cf83f0b387196f8d7aa9f53c54d0b86

Complete Implementation Example​

Complete Node.js Implementation​

const https = require("https");
const crypto = require("crypto");
const url = require("url");

/**
* Make an authenticated request to the ONLIVE.SITE API
*
* @param {Object} options - Request options
* @param {string} options.method - HTTP method
* @param {string} options.url - Full URL to request
* @param {Object} options.headers - Request headers
* @param {string|Object} options.body - Request body (optional)
* @param {string} accessKeyId - Your ONLIVE.SITE Access Key ID
* @param {string} secretKey - Your ONLIVE.SITE Secret Access Key
* @returns {Promise} - Promise resolving to response data
*/
function makeOnliveSiteRequest(options, accessKeyId, secretKey) {
return new Promise((resolve, reject) => {
const parsedUrl = new url.URL(options.url);
const method = (options.method || "GET").toUpperCase();
const headers = options.headers || {};
const body =
typeof options.body === "string"
? options.body
: options.body
? JSON.stringify(options.body)
: "";

// Get query parameters
const queryParams = options.queryParams || {};

// Step 1: Add timestamp header
const now = new Date();
const date = `${now.getUTCFullYear()}${pad(now.getUTCMonth() + 1)}${pad(
now.getUTCDate()
)}T${pad(now.getUTCHours())}${pad(now.getUTCMinutes())}${pad(
now.getUTCSeconds()
)}Z`;
headers["x-onlive-site-date"] = date;

// Step 2: Create string to sign
const canonicalHeaders = getCanonicalHeaders(headers);
const canonicalResourceURI = parsedUrl.pathname;
const canonicalQueryString = getCanonicalQueryString(queryParams);

const hashedPayload = crypto
.createHash("sha256")
.update(body || "")
.digest("hex");

const stringToSign = `${method}\n${canonicalHeaders}\n${canonicalResourceURI}\n${canonicalQueryString}\n${hashedPayload}`;

// Step 3: Calculate signature
const signature = crypto
.createHmac("sha256", secretKey)
.update(stringToSign)
.digest("hex");

// Step 4: Add authorization header
headers[
"Authorization"
] = `ONLIVESITE Credential=${accessKeyId}, Signature=${signature}`;

// If content-length not set and we have a body, set it
if (body && !headers["content-length"]) {
headers["content-length"] = Buffer.byteLength(body);
}

// Add query parameters to URL
if (Object.keys(queryParams).length > 0) {
for (const [key, value] of Object.entries(queryParams)) {
parsedUrl.searchParams.append(key, value);
}
}

// Prepare request options
const requestOptions = {
protocol: parsedUrl.protocol,
hostname: parsedUrl.hostname,
port: parsedUrl.port || (parsedUrl.protocol === "https:" ? 443 : 80),
path: parsedUrl.pathname + parsedUrl.search,
method: method,
headers: headers,
};

// Make request
const req = https.request(requestOptions, (res) => {
let data = "";

res.on("data", (chunk) => {
data += chunk;
});

res.on("end", () => {
try {
const contentType = res.headers["content-type"] || "";
if (contentType.includes("application/json")) {
resolve(JSON.parse(data));
} else {
resolve(data);
}
} catch (e) {
resolve(data);
}
});
});

req.on("error", (error) => {
reject(error);
});

if (body) {
req.write(body);
}

req.end();
});
}

// Helper functions
/**
* Pads a number with leading zeros to ensure it is two digits.
* @param {number} number - The number to pad.
* @returns {string} - The padded number as a string.
*/
function pad(number) {
return `${("00" + number).slice(-2)}`;
}

/**
* Gets the canonical headers for the request.
* @param {Object} headers - The headers object.
* @returns {string} - The canonical headers string.
*/
function getCanonicalHeaders(headers) {
const customHeaders = [];

Object.keys(headers).forEach((key) => {
if (key.toLowerCase().startsWith("x-onlive-site-")) {
customHeaders.push({
name: key.toLowerCase(),
value: headers[key].trim(),
});
}
});

customHeaders.sort((a, b) => a.name.localeCompare(b.name));

return customHeaders
.map((record) => `${record.name}:${record.value}`)
.join("\n");
}

/**
* Gets the canonical query string from the query parameters object.
* @param {Object} queryParams - The query parameters object.
* @returns {string} - The canonical query string.
*/
function getCanonicalQueryString(queryParams = {}) {
const params = [];

// Process the query parameters object
Object.entries(queryParams).forEach(([key, value]) => {
params.push({
name: encodeURIComponent(key),
value: encodeURIComponent(value),
});
});

params.sort((a, b) => a.name.localeCompare(b.name));

return params.length > 0
? params.map((param) => `${param.name}=${param.value}`).join("&")
: "";
}

// Example usage:
async function fetchData() {
try {
const data = await makeOnliveSiteRequest(
{
method: "GET",
url: "https://openapi.onlive.site/api/v1/presets",
headers: {
"Content-Type": "application/json",
},
// Define query parameters directly as an object
queryParams: {
sort: "asc",
limit: "20",
filter: "active",
},
},
"your-access-key-id",
"your-secret-key"
);

console.log(data);
} catch (error) {
console.error("Error:", error);
}
}

Common Issues and Troubleshooting​

Request Signature Mismatch​

  • Verify that you're using the correct Secret Access Key
  • Ensure all components of the string to sign are in the correct order
  • Check that headers are properly formatted and sorted
  • Make sure query parameters are properly encoded and sorted
  • Verify the payload hash is correct

Invalid Timestamp​

  • Ensure your system clock is accurate
  • Format the timestamp exactly as specified: YYYYMMDDTHHmmssZ
  • Remember to use UTC time, not local time
  • Note that month values start from 1, not 0 (January = 01, not 00)

Missing Headers​

  • The x-onlive-site-date header is required for all authenticated requests
  • Make sure this header is included in the canonical headers string

Canonical URI Issues​

  • Use only the path component of the URL (no protocol, host, or query string)
  • The path should start with a forward slash (/)

Query String Issues​

  • All parameter names and values must be URL-encoded
  • Parameters must be sorted alphabetically by name (after encoding)
  • Use an empty string for the canonical query string if there are no parameters