Every request to Shoptimiza API must include the X-Shoptimiza-Auth
http header. The goal of this header is to prevent
unauthorised use of the platform, API's, third party services, etc by manipulating, reusing, or copying the request data.
The header includes apiKey, unix timestamp, body signature (optional) and a hmac signature of: apiKey, unix timestamp, body signature (optional), url and http verb.
The header can be built by concatenating your apiKey
a unix timestamp
and a signature
with dots.
I.e $apiKey.$unix_time.$signature
.
Where:
For GET
, HEAD
, DELETE
requests:
The header value looks like $apiKey.$unixTime.$signature
. Were signature is built by:
apiKey
, unixTime
, httpVerb
and target url without protocol
Building the authentication header is better explained with an example:
// We want to request 'https://api.shoptimiza.com/some_function
var urlWithouthProtocol = 'api.shoptimiza.com/some_function';
var apiKey = '123';
var unixTime = unixTimestamp();
var httpVerb = 'GET' // always use uppercase verbs
var signature = base64(hmacSHA256(apiKey + '.' + unixTime + '.' + httpVerb + '.' + urlWithouthProtocol);
var headerValue = apiKey + '.' + unixTime + '.' + signature;
We also have an implementation for node.js implementation for node.js
For POST
, PUT
the signature is similar but adding the body
signature (sha1 encoded as base64).
The header value will look like $apiKey.$unix_time.$bodySignature.$signature
.
The signature can be built as follows:
// We want to request 'https://api.shoptimiza.com/some_function
var urlWithouthProtocol = 'api.shoptimiza.com/some_function';
var bodySignature = ...// base64(sha1(body));
var apiKey = '123';
var unixTime = unixTimestamp();
var httpVerb = 'GET' // always use uppercase verbs
var signature = base64(hmacSHA256(apiKey + '.' + unixTime + '.' + httpVerb + '.' + urlWithouthProtocol + '.' + bodySignature);
var headerValue = apiKey + '.' + unixTime '.' +bodySignature + '.' + signature;
Any mismatch between the expected signature and the submitted signature will end up into a 403 error. The server will return also a JSON object with a reason like:
{ "reason": "invalid apiKey" }
We include a timestamp ($unix_time) in the signature in order to prevent header replay attacks.
Is up to the server to set a timeout but we recommend something around 2 seconds.
The server will respond with status 403 and {"reason":"timeout", "time": xxxxxxxxxx}
when the difference between server time and header $unix_time is bigger than timeout defined by the server. In this case the client will retry the request updating the header and adjusting the time.
The proper way to do this is to sum the time difference between client and server to the time client request at header.
For instance:
// pseudo code here
header = createHeader(apiKey, now, verb, url)
result = request(verb, url, header);
if result.statusCode === 403 && result.reason === 'timeout' {
delta = now - result.time
header(createHeader(apiKey, now + delta, verb, url);
result = request(url, header);
}
Remember to limit the number of retries!
{"reason": "missing header"}
Header X-Shoptimiza-Auth not present{"reason": "invalid apiKey"}
Action: Check the api key{"reason":"timeout", "time": xxxxxxxxxx}
Action: Apply time delta to your time{"reason": "invalid signature"}
Action: something is missing in your signature. Different url? lower case HTTP verb?Build your headers just before the request happens to prevent timeouts
Track the incoming request start time before any other action. Later you can check if the request has timedout against that parameter. Otherwise you are creating a non real delay. An example explains better why:
// current time : 1
handleRequest(request){
doSomethingReallySlow()
// current time : 100
checkSomethingImportant();
// current time : 200
now = time() // now = 200;
checkHeaderTimeout(now, headerTime) // wooops! You just introduced a 200 time units delay
}
If you track time from the very begin this does not happen
// current time : 1
handleRequest(request){
now = time() // now = 1
doSomethingReallySlow()
// current time : 100
checkSomethingImportant();
// current time : 200
checkHeaderTimeout(now, headerTime) // now = 1
}