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