HMAC Auth for Node.js
This SDK provides easy-to-use functions for authenticating requests to the SafeSky API using HMAC signatures.
Click here to download the SDK
Installation
No external dependencies required! Simply copy the SDK file to your project:
### Copy the SDK to your project
cp safesky-hmac-auth.js /path/to/your/project/
Requirements:
- Node.js 16+
Quick Start
const safeskyAuth = require('./safesky-hmac-auth');
// Your SafeSky API key - REPLACE WITH YOUR ACTUAL API KEY
const API_KEY = 'YOUR_SAFESKY_API_KEY_HERE';
// Generate authentication headers for a GET request
const headers = safeskyAuth.generateAuthHeaders(
API_KEY,
'GET',
'https://sandbox-public-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908'
// 'https://public-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908' // Production
);
// Use the headers in your HTTP request
console.log(headers);
// {
// 'Authorization': 'SS-HMAC Credential=xxx/v1, SignedHeaders=host;x-ss-date;x-ss-nonce, Signature=yyy',
// 'X-SS-Date': '2025-11-12T14:30:00Z',
// 'X-SS-Nonce': '550e8400-e29b-41d4-a716-446655440000',
// 'X-SS-Alg': 'SS-HMAC-SHA256-V1'
// }
######## API Reference
############ generateAuthHeaders(apiKey, method, url, body = '')
Generates all required HMAC authentication headers for a SafeSky API request.
Parameters:
apiKey(string): Your SafeSky API key (e.g.,ssk_live_...)method(string): HTTP method (GET,POST, etc.)url(string): Full request URL including protocol, host, path, and query stringbody(string, optional): Request body for POST/PUT requests (default: empty string)
Returns: Object containing all required headers
Example:
const headers = safeskyAuth.generateAuthHeaders(
'YOUR_SAFESKY_API_KEY_HERE',
'POST',
'https://sandbox-public-api.safesky.app/v1/uav',
// 'https://public-api.safesky.app/v1/uav', // Production
JSON.stringify({id: 'uav1', lat: 50.69, lng: 4.39})
);
############ deriveKid(apiKey)
Derives the KID (Key Identifier) from an API key.
Parameters:
apiKey(string): Your SafeSky API key
Returns: String - The derived KID in base64url format
############ deriveHmacKey(apiKey)
Derives the HMAC signing key from an API key using HKDF-SHA256.
Parameters:
apiKey(string): Your SafeSky API key
Returns: Buffer - The derived HMAC key (32 bytes)
############ generateNonce()
Generates a cryptographically secure nonce (UUID v4 format).
Returns: String - A UUID v4 nonce
############ generateTimestamp()
Generates the current timestamp in ISO8601 format.
Returns: String - ISO8601 timestamp (e.g., 2025-11-12T14:30:00Z)
############ buildCanonicalRequest(method, path, queryString, host, timestamp, nonce, body)
Builds the canonical request string for HMAC signature.
Parameters:
method(string): HTTP methodpath(string): Request pathqueryString(string): Query string without leading?host(string): Host header valuetimestamp(string): ISO8601 timestampnonce(string): Unique noncebody(string): Request body
Returns: String - The canonical request string
############ generateSignature(canonicalRequest, hmacKey)
Generates HMAC-SHA256 signature for the canonical request.
Parameters:
canonicalRequest(string): The canonical request stringhmacKey(Buffer): The derived HMAC signing key
Returns: String - The signature in lowercase hexadecimal format
######## Complete Examples
The SDK includes 5 complete examples in example.js:
- GET nearby aircraft - Retrieve traffic in a specific area
- POST UAV position - Submit UAV telemetry data
- POST UAV with query parameters - Submit UAV and get nearby traffic
- POST advisory (GeoJSON) - Publish GeoJSON FeatureCollection with polygon and point
- Manual step-by-step - Detailed authentication flow
############ Example 1: GET Request
const safeskyAuth = require('./safesky-hmac-auth');
const https = require('https');
const API_KEY = 'YOUR_SAFESKY_API_KEY_HERE';
const url = 'https://sandbox-public-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908&rad=20000';
// const url = 'https://public-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908&rad=20000'; // Production
// Generate auth headers
const authHeaders = safeskyAuth.generateAuthHeaders(API_KEY, 'GET', url);
// Make request
const urlObj = new URL(url);
const options = {
hostname: urlObj.hostname,
path: urlObj.pathname + urlObj.search,
method: 'GET',
headers: {
...authHeaders,
'Content-Type': 'application/json'
}
};
https.get(options, (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => console.log(JSON.parse(data)));
});
############ Example 2: POST Request
const safeskyAuth = require('./safesky-hmac-auth');
const https = require('https');
const API_KEY = 'YOUR_SAFESKY_API_KEY_HERE';
const url = 'https://sandbox-public-api.safesky.app/v1/uav';
// const url = 'https://public-api.safesky.app/v1/uav'; // Production
const body = JSON.stringify([{
id: 'my_uav_001',
altitude: 110,
latitude: 50.69378,
longitude: 4.39201,
last_update: Math.floor(Date.now() / 1000),
status: 'AIRBORNE',
call_sign: 'Test UAV'
}]);
// Generate auth headers (include body for POST)
const authHeaders = safeskyAuth.generateAuthHeaders(API_KEY, 'POST', url, body);
// Make request
const urlObj = new URL(url);
const options = {
hostname: urlObj.hostname,
path: urlObj.pathname,
method: 'POST',
headers: {
...authHeaders,
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body)
}
};
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => data += chunk);
res.on('end', () => console.log(JSON.parse(data)));
});
req.write(body);
req.end();
############ Example 3: POST Advisory (GeoJSON)
const safeskyAuth = require('./safesky-hmac-auth');
const https = require('https');
const API_KEY = 'YOUR_SAFESKY_API_KEY_HERE';
const url = 'https://sandbox-public-api.safesky.app/v1/advisory';
// const url = 'https://public-api.safesky.app/v1/advisory'; // Production
const body = JSON.stringify({
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {
id: "my_advisory_id1",
max_altitude: 111,
last_update: Math.floor(Date.now() / 1000),
call_sign: "Advisory test with polygon",
remarks: "Inspection powerlines"
},
geometry: {
type: "Polygon",
coordinates: [[[4.3948, 50.6831], [4.3952, 50.6832], /* ... more coordinates ... */]]
}
},
{
type: "Feature",
properties: {
id: "my_advisory_id2",
max_altitude: 150,
max_distance: 500,
last_update: Math.floor(Date.now() / 1000),
call_sign: "Advisory test with a point",
remarks: "Inspection rails"
},
geometry: {
type: "Point",
coordinates: [4.4, 50.7]
}
}
]
});
// Generate auth headers
const authHeaders = safeskyAuth.generateAuthHeaders(API_KEY, 'POST', url, body);
// Make request (similar to Example 2)
############ Example 4: Using with Axios
const safeskyAuth = require('./safesky-hmac-auth');
const axios = require('axios');
const API_KEY = 'YOUR_SAFESKY_API_KEY_HERE';
async function getNearbyAircraft() {
const url = 'https://sandbox-public-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908&rad=20000';
// const url = 'https://public-api.safesky.app/v1/uav?lat=50.6970&lng=4.3908&rad=20000'; // Production
const authHeaders = safeskyAuth.generateAuthHeaders(API_KEY, 'GET', url);
const response = await axios.get(url, {
headers: {
...authHeaders,
'Content-Type': 'application/json'
}
});
return response.data;
}
getNearbyAircraft()
.then(data => console.log(data))
.catch(error => console.error(error));
############ Example 5: Using with Fetch API
const safeskyAuth = require('./safesky-hmac-auth');
const API_KEY = 'YOUR_SAFESKY_API_KEY_HERE';
async function publishUAV() {
const url = 'https://sandbox-public-api.safesky.app/v1/uav';
// const url = 'https://public-api.safesky.app/v1/uav'; // Production
const body = JSON.stringify([{
id: 'my_uav_001',
altitude: 110,
latitude: 50.69378,
longitude: 4.39201,
last_update: Math.floor(Date.now() / 1000)
}]);
const authHeaders = safeskyAuth.generateAuthHeaders(API_KEY, 'POST', url, body);
const response = await fetch(url, {
method: 'POST',
headers: {
...authHeaders,
'Content-Type': 'application/json'
},
body: body
});
return await response.json();
}
publishUAV()
.then(data => console.log(data))
.catch(error => console.error(error));
######## Running Examples
###### Run the provided example file
node example.js
######## Security Notes
- Keep your API key secret - Never commit it to version control or expose it in client-side code
- Use environment variables - Store your API key in environment variables:
const API_KEY = process.env.SAFESKY_API_KEY; - Use HTTPS only - The SafeSky API only accepts HTTPS requests
- Handle errors - Always implement proper error handling in production code
######## How It Works
The HMAC authentication process:
- Derive KID: A public identifier is derived from your API key
- Derive HMAC Key: A signing key is derived using HKDF-SHA256
- Build Canonical Request: A standardized string representation of the request
- Generate Signature: HMAC-SHA256 signature of the canonical request
- Set Headers: Add Authorization, X-SS-Date, X-SS-Nonce, and X-SS-Alg headers
This ensures:
- Authentication: Only holders of valid API keys can make requests
- Integrity: Request data cannot be modified in transit
- Replay Protection: Each request uses a unique nonce and timestamp
######## Troubleshooting
############ "Invalid signature" error
- Ensure your API key is correct
- Verify the request URL, method, and body exactly match what you're signing
- Check that the timestamp is within ±5 minutes of server time
############ "Replay attack detected" error
- Don't reuse the same nonce within the validity window
- Generate a fresh nonce for each request using
generateNonce()
############ "Clock skew too large" error
- Ensure your system clock is synchronized
- The timestamp must be within ±5 minutes of the server time
######## Support
For questions or issues:
- Email: support@safesky.app
- Documentation: https://docs.safesky.app