API Key authentication should only be used to access your own account. If your application requires access to other Shipl users’ accounts, do not use API Key.
API key authentication requires each request to be signed (enhanced security measure). You can create and activate new API keys in in the Shipl Console. Your API keys are assigned to access to testnet (kovan, ropsten, rinkeby) or to livenet (mainnet, poa and xDai).
Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.
All request requires three headers: date
, authorization
and signature
. If the HTTP request contains a body, the content-length
and content-type
headers are also required.
The date
header is a standard RFC-822 (updated in RFC-1123) date, as per RFC-7231.
The authorization
header is a standard as per RFC-2617 that, confusingly, is designed for authentication and not authorization. It should contain a string representation of the client's API key.
The signature
header contains the signature of the entire request, as well as a reference to the version of the protocol, and the algorithm used to generate the signature.
A correctly signed HTTP request should look like this:
POST https://api.shipl.co/orders/ordercontent-type: application/jsoncontent-length: 90date: Tue, 20 Apr 2016 18:48:24 GMTauthorization: api-key YOUR_API_KEYsignature: shipl-hmac-auth sha384 64b0a4bd0cbb45c5b2fe8b1e4a15419b6018a9a90eb19046247af6a9e8896bd3
Signature
To calculate the signature, your first needs to create a string representation of the request. When our server receives an authenticated request it computes the the signature and compares it with the signature that you provided. Therefore, the you must create a string representation of the request in the exact same way as the server. This is called "canonicalization."
The format of a canonical representation of a request is:
HTTP Verb + \nURI + \nCanonical query string + \nCanonically formatted signed headers + \nHashed body payload
Component | Format | Example |
HTTP Verb | upperCase(verb) | POST, GET or DELETE |
URI | encode(uri) | /items/test%20item |
Query String | encode(paramA) + '=' + encode(valueA) + '&' + encode(paramB) + '=' + encode(valueB) | paramA=valueA¶mB=value%20B |
Headers | lowerCase(keyA) + ':' + trim(valueA) + '\n' + lowerCase(keyB) + ':' + trim(valueB) | keyA:valueA keyB:value%20B |
Hashed payload | hex(hash('sha256', bodyData)) | ​ |
The HTTP verb must be upper case. The URI should be url-encoded. The query string elements should be alphabetically sorted. The header keys must all be lower case (as per RFC-2616) and alphabetically sorted. The only headers included in the signature should be: authorization
and date
- however content-length
and content-type
should be included if the HTTP body is not empty. The last line of the request string should be a hex representation of a SHA384 hash of the request body. If there is no request body, it should be the hash of an empty string.
Programmatically:
upperCase(method) + \npath + \nencode(paramA) + '=' + escape(valueA) + '&' + escape(paramB) + '=' + escape(valueB) + \nlowerCase(headerKeyA) + ':' + trim(headerValueA) + \n + lowerCase(headerKeyB) + ':' + trim(headerKeyB) + \nhex(hash('sha384', bodyData)) + \n
For Example
POST/orders/orderparamA=valueA¶B=value%20Bauthorization: api-key YOUR_API_KEYcontent-length:15content-type: application/jsondate:Tue, 20 Apr 2016 18:48:24 GMT8eb2e35250a66c65d981393c74cead26a66c33c54c4d4a327c31d3e5f08b9e1b
Then the HMAC signature of the entire request is generated by signing it with the secret, as a hex representation:
const signature = hex(hmacSha256(secret, requestString))
That value is then sent as the contents of the signature
header along with the algorithm used to generate it, as well as the version of the protocol the signature implements.
// protocol version + ' ' + algorithm + ' ' + signature;headers[signature] = 'shipk-hmac-auth sha384 ' + signature
'use strict'​const axios = require('axios')const crypto = require('crypto')​const canonicalize = require('../src/nodejs/canonicalize')​const body = {'metaNonce': '0x9','metaSignedTx': 'f9022980843b9a3180b90204bc425976cabe699ec8292f95c3eb9555a01c8080','blockchain': 'rinkeby','appId': 'YOUR_SHIPL_APP_ID','jsonRpcReponse': true,'id': 1555341488002065}​const shiplConfig = {API_KEY: 'YOUR_SHIPL_API_KEY_ID',API_SECRET: 'YOUR_SHIPL_API_KEY_SECRET',HOST_URL: 'https://testnet.api.shipl.co'}​const buildSign = (data, config) => {return crypto.createHmac('sha384', config.API_SECRET).update(data).digest('hex')}​const privateRequest = async ({ httpMethod, path, queryString, body }) => {const dataQueryString = qs.stringify(queryString)const headers = {Authorization: `api-key ${shiplConfig.API_KEY}`,date: new Date().toUTCString()}const canonical = canonicalize(httpMethod, path, queryString, headers, JSON.stringify(body))const signature = buildSign(canonical, shiplConfig)const requestConfig = {method: httpMethod,url: shiplConfig.HOST_URL + path + '?' + dataQueryString,headers,data: body}requestConfig.headers.signature = 'shipl-hmac-auth sha-384 ' + signature​try {const response = await axios(requestConfig)console.log(response.data)return response} catch (error) {console.log(error.response.data)throw error}}​privateRequest({ httpMethod: 'POST', path: '/orders/order', queryString: '', body })​
# Requires python-requests.# Install with pip: pip install requests# or, with easy-install: easy_install requests​import json, hmac, hashlib, time, requests, base64from requests.auth import AuthBase​# Create custom authentication for Exchangeclass ShiplAuth(AuthBase):def __init__(self, api_key, secret_key, params):self.api_key = api_keyself.secret_key = secret_keyself.params = params​def __call__(self, request):timestamp = str(time.time())​request.headers.update({'date': timestamp,'Authorization': 'api-key ' + self.api_key})​def canonicalize(method, uri, queryString, headers, data):headerWhitelist = ['authorization','date','content-length','content-type']​method = method.upper()if (queryString == None or len(queryString) == 0):queryString = ''if (data == None or len(queryString) == 0):data = ''​headerString = ''​for index, key in enumerate(sorted(headers)):key = key.lower()if (key not in headerWhitelist):continueif (key == 'content-length' and headers[key] == '0'):continueheaderString += key + ':' + headers[key].strip()if (index != (len(headers) - 1)):headerString += '\n'​string = method + '\n'string += uri + '\n'string += queryString + '\n'string += headerString + '\n'string += data​return string​canonicalRequest = canonicalize(request.method, '/' + request.path_url, (self.params or ''), request.headers, (request.body or ''))signature = hmac.new(bytes(self.secret_key, 'utf-8'), bytes(canonicalRequest, 'utf-8'), hashlib.sha384).hexdigest()request.headers.update({ 'signature': 'shipl-hmac-auth sha-384 ' + signature })return request​api_url = 'https://testnet.api.shipl.co'api_key = 'YOUR_SHIPL_API_KEY_ID'api_secret = 'YOUR_SHIPL_API_KEY_SECRET'params = ''auth = ShiplAuth(api_key, api_secret, '')​# Get accountsr = requests.get(api_url + '/orders/order', auth=auth)​
​