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
- Create a timestamp for your request
- Add required headers to your request
- Create a canonical request string
- Calculate the signature using your Secret Access Key
- 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 yearMM
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