Authentication
Authentication
Getting Started
You should have received an API key from SafeSky. This single key consists of two components:
- API Key ID: Your public identifier
- API Secret: Your private secret key used for signing requests
Keep your API secret secure and never expose it in client-side code or public repositories.
⚠️ Important Notice: The legacy
X-API-Keyheader authentication method is deprecated and will be removed in an upcoming release. All integrations must migrate to HMAC authentication as described below. Please update your implementation as soon as possible to avoid service interruption.
HMAC Authentication
SafeSky API uses HMAC (Hash-based Message Authentication Code) for request authentication. This scheme provides several key benefits:
- Security: Credentials are never sent over the network; only a cryptographic signature is transmitted
- Integrity: Ensures requests haven't been tampered with during transit
- Replay Protection: Timestamps prevent replay attacks
- Stateless: No session management required on the server side
HMAC uses the SHA-256 hashing algorithm to create a unique signature for each request based on the request content and your API secret.
SDKs and Libraries
To simplify integration, SafeSky provides official SDKs for multiple platforms:
| Language | Link |
|---|---|
| Python | Documentation |
| Dart / Flutter | Documentation |
| JavaScript / Node.js | Documentation |
| Java | Documentation |
| Kotlin | Documentation |
| C / C++ | Documentation |
| C# | Documentation |
These SDKs handle HMAC signature generation, request signing, and error handling automatically. We recommend using an SDK whenever possible to reduce implementation complexity and potential security issues.
Manual HMAC Implementation
If an SDK is not available for your platform, you can implement HMAC authentication manually.
RequestAPI SignatureKey ComponentsDerivation
SafeSky uses a single API key from which both the credential identifier (KID) and the HMAC signing key are derived:
Derive KID (Credential Identifier)
kid = base64url(SHA256("kid:" + api_key)[0:16])
- instead of +, _ instead of /, no padding =)
Derive HMAC Signing Key
hmac_key = HKDF-SHA256(api_key, salt="safesky-hmac-salt-v1", info="auth-v1", len=32)
Required Headers
Each authenticated request must include the following headers:
X-SafeSky-Key-Id:Authorization: SS-HMAC Credential=<your-api-key-idkid>/v1, SignedHeaders=host;x-ss-date;x-ss-nonce, Signature=<base64-signature>
X-SafeSky-Timestamp:SS-Date: <unix-timestamp-in-secondsISO8601-timestamp>
X-SafeSky-Signature:SS-Nonce: <hmac-signatureuuid-v4>
X-SS-Alg: SS-HMAC-SHA256-V1
Signature Generation Process
-
Generate Request Metadata
timestamp: ISO8601 format (e.g.,2025-11-12T12:00:00.000Z)
nonce: UUID v4 (e.g., 123e4567-e89b-12d3-a456-426614174000)
Create the SignatureCanonical BaseRequest String
ConcatenateBuild the followingcanonical componentsstring with newline characters (\n):separators:
<HTTP-METHOD>\n
<REQUEST-PATH>\n
<TIMESTAMPQUERY-STRING>\n
host:<REQUEST-BODYhost-header>\n
x-ss-date:<timestamp>\n
x-ss-nonce:<nonce>\n
\n
<hex-sha256-of-body>
HTTP-METHOD: Uppercase HTTP method (GET, POST, PUT,DELETE, etc.)DELETE)REQUEST-PATH:TheURI pathand query stringonly (e.g.,/)api/v1/flights?status=activeuav:TIMESTAMPQUERY-STRINGUnixQuerytimestampparametersinsortedsecondsalphabetically (must match thee.g.,X-SafeSky-Timestamplat=50.6970&lng=4.3908header)
REQUEST-BODYhost: Host header value (e.g., api.safesky.app)
x-ss-date: ISO8601 timestamp
x-ss-nonce: UUID v4 nonce
Empty line before body hash
Body hash: SHA-256 of request body in lowercase hex (empty string hash for GET/Generate HMAC Signature
Compute the HMAC-SHA256 hash of the signature base string using your API secret:signature:
signaturesignature_bytes = HMAC-SHA256(api_secret,hmac_key, signature_base_string)canonical_request)
signature = base64(signature_bytes)
EncodeBuild theAuthorization SignatureHeader
ConvertConstruct the Authorization header:
Authorization: SS-HMAC Credential=<kid>/v1, SignedHeaders=host;x-ss-date;x-ss-nonce, Signature=<signature>
to hexadecimal format (lowercase).
Example Implementation
######## cURLPython
##!import hmac
import hashlib
import base64
import uuid
from datetime import datetime, timezone
def derive_kid(api_key):
"""Derive KID from API key"""
kid_string = f"kid:{api_key}"
hash_bytes = hashlib.sha256(kid_string.encode()).digest()
first_16 = hash_bytes[:16]
kid = base64.b64encode(first_16).decode()
return kid.replace('+', '-').replace('/bin/bash', API_KEY_ID='_').replace('=', '')
def derive_hmac_key(api_key):
"your_api_key_id"""Derive API_SECRET=HMAC key using HKDF-SHA256""your_api_secret""
METHOD=from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
hkdf = HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=b"safesky-hmac-salt-v1",
info=b"auth-v1"
)
return hkdf.derive(api_key.encode())
def build_canonical_request(method, path, query_string, host, timestamp, nonce, body):
"""Build canonical request string"""
# Hash the body
body_hash = hashlib.sha256((body or "").encode()).hexdigest()
# Build canonical request
canonical = (
f"{method.upper()}\n"
f"{path}\n"
f"{query_string or ''}\n"
f"host:{host}\n"
f"x-ss-date:{timestamp}\n"
f"x-ss-nonce:{nonce}\n"
f"\n"
f"{body_hash}"
)
return canonical
def generate_signature(canonical_request, hmac_key):
"""Generate HMAC-SHA256 signature"""
signature = hmac.new(hmac_key, canonical_request.encode(), hashlib.sha256).digest()
return base64.b64encode(signature).decode()
# Example usage
API_KEY = "your_api_key_here"
METHOD = "GET"
PATH="/api/v1/flights"URL TIMESTAMP=$(date +%s)
BODY=""
## Create signature base string
SIGNATURE_BASE="${METHOD}\n${PATH}\n${TIMESTAMP}\n${BODY}"
## Generate HMAC signature
SIGNATURE=$(echo -n -e "${SIGNATURE_BASE}" | openssl dgst -sha256 -hmac "${API_SECRET}" | cut -d' ' -f2)
## Make request
curl -X GET= "https://api.safesky.app${PATH}app/v1/uav?lat=50.6970&lng=4.3908"
BODY = ""
\# Parse URL
from urllib.parse import urlparse
parsed = urlparse(URL)
host = parsed.netloc
path = parsed.path
query = parsed.query
# Generate metadata
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-H3] + "Z"
nonce = str(uuid.uuid4())
# Derive credentials
kid = derive_kid(API_KEY)
hmac_key = derive_hmac_key(API_KEY)
# Build and sign request
canonical = build_canonical_request(METHOD, path, query, host, timestamp, nonce, BODY)
signature = generate_signature(canonical, hmac_key)
# Headers
headers = {
"Authorization": f"SS-HMAC Credential={kid}/v1, SignedHeaders=host;x-ss-date;x-ss-nonce, Signature={signature}",
"X-SafeSky-Key-Id:SS-Date": ${API_KEY_ID}" \
-Htimestamp,
"X-SafeSky-Timestamp:SS-Nonce": ${TIMESTAMP}" \
-Hnonce,
"X-SafeSky-Signature:SS-Alg": ${SIGNATURE}"SS-HMAC-SHA256-V1"
}
######## Java
See the complete SDK implementation at wiki/hmac/java-sdk/SafeSkyHmacAuth.java
Important Considerations
- Clock Synchronization: Ensure your system clock is synchronized with NTP. The API will reject requests with timestamps
moreoutsidethan±5 minutesoldclockorskewin the future.tolerance. - Character Encoding: Always use UTF-8 encoding for
theallsignaturestringbase string.operations. - Body Serialization: For POST/PUT requests, use the exact body bytes that will be
sent (typically JSON).sent. Don't modify or pretty-print the JSON after signing. - Query
ParametersString:IncludeMustquery parametersbe in thepathcanonicalcomponentrequest as a separate line (e.g.,sorted alphabetically if multiple parameters).
/api/v1/flights?status=activeX-SS- prefix (not X-SafeSky-).
SS-HMAC-SHA256-V1 X-SS-Alg header.
Error Responses
Authentication failures will return HTTP 401 with one of the following error codes:messages such as:
: Required headers are missing or malformedinvalid_signatureMissing or invalid HMAC headers
Invalid signature: The signature doesn't match the expected value
invalid_timestampTimestamp outside acceptable range: The timestamp is invalid_keyReplay attack detected - nonce already used: The Invalid credential - key IDnot found: The KID is unknown or the API key is inactivemissing_headersTesting Your Implementation
To verify your implementation:
-
Key Derivation: Verify your KID and HMAC key derivation matches the SDK
- Test KID generation with a known API key
- and _, no padding)
Verify HKDF implementation produces correct 32-byte key
Canonical Request: Build a canonical request and compare with SDK
\n)
Check body hash is lowercase hexadecimal SHA-256
Ensure empty line before body hash
Signature Generation: Compare your HMAC-SHA256 output
Clock Sync: Ensure your system clock is accurate
date -u +"%Y-%m-%dT%H:%M:%S.000Z" to check ISO8601 format
Server allows ±5 minutes tolerance
Test Request: Start with a simple GET request
/api/v1v1/uav?lat=50&lng=4dateheaders +%sare For additional support, contact SafeSky technical support or refer toto: