I am using an API for a portfolio management software and have been provided some documentation on how to implement it through Postman. The results come clean through Postman, but when I try to replicate the script through Python, I am unable to authenticate. I am not well versed in any other language besides Python and I am no API expert (especially when it comes to authentication). I was hoping that someone might be able to look through this code and see if anything seems incorrect with respect to creating a unique signature. Or, if anyone has had experience with creating an HMAC signature using the python requests structure.
import requests
import uuid
import time
import hmac
import base64
import hashlib
import math
url = "url"
payload = "{\n\t\"firm\": \"XXXXXXXX\",\n\t\"id\": \"#######\",\n\t\"data\": {\n\t\t\"tracking_preference\": 2\n\t} \n}\n"
apikey = 'apikey'
uuid = str(uuid.uuid4())
ts = math.floor(time.time())
timestamp = str(ts)
signature = timestamp+uuid
#signature_bytes = signature.encode('UTF8')
#secret_bytes = base64.standard_b64decode(apikey)
signature_bytes = bytes(signature, 'UTF8')
secret_bytes = bytes(apikey, 'UTF8')
signature_hash = hmac.new(secret_bytes, signature_bytes, hashlib.sha256).digest()
hmac = base64.b64encode(signature_hash).decode()
headers = {
'X-SL-UUID': uuid,
'X-SL-Timestamp': timestamp,
'X-SL-HMAC': hmac,
'Content-Type': "application/json",
'User-Agent': "PostmanRuntime/7.18.0",
'Accept': "*/*",
'Cache-Control': "no-cache",
'Postman-Token': "unique token",
'Host': "xxxxxx-xxx.smartleaf.com",
'Accept-Encoding': "gzip, deflate",
'Content-Length': "89",
'Connection': "keep-alive",
'cache-control': "no-cache"
}
response = requests.request("POST", url, data=payload, headers=headers)
print(response.text)
Anything with "XXX" or "###" is information I have blurred out. Thanks in advance!
Here is the pre-request script on Postman using JS:
var apikey = '##########';
var uuidlib = require('uuid');
var uuid = uuidlib.v4();
var timestamp = Math.floor(Date.now() / 1000).toString();
var hash = CryptoJS.HmacSHA256(timestamp.concat(uuid), apikey);
postman.setEnvironmentVariable('timestamp', timestamp);
postman.setEnvironmentVariable('uuid', uuid);
postman.setEnvironmentVariable('hmac', hash);
directly translating that Javascript code results in:
import hmac
import hashlib
import time
import uuid
import requests
apikey = b'##########'
url = 'https://xxxxxx-xxx.smartleaf.com/'
payload = {'firm': 'XXXXXXXX', 'id': '#######', 'data': {'tracking_preference': 2}}
reqid = str(uuid.uuid4())
reqts = str(int(time.time()))
key = hmac.new(apikey, f'{reqts}{reqid}'.encode('ascii'), hashlib.sha256).hexdigest()
headers = {
'X-SL-UUID': reqid,
'X-SL-Timestamp': reqts,
'X-SL-HMAC': key,
}
response = requests.post(url, json=payload, headers=headers)
the main this is you were encoding the digest in base64 rather than hexadecimal
obviously I can't test this, but hopefully it's about right