Trouble with signature string (in python)

Ave81

New Member
Hello,

We're creating an integration with a platform that is built on Mailwizz and are unable to get the correct signature string; in PHP this is $signatureString but we're attempting to write this in python.

Here's what we currently have (which returns an API error):

GET http://dashboard.sendreach.com/api/index.php/lists?page=1&per_page=10&X-MW-TIMESTAMP=1444407416&X-MW-PUBLIC-KEY=XXXX"
for GET request: http://dashboard.sendreach.com/api/index.php/lists?page=1&per_page=10

With the following headers:
'Accept-Encoding': 'gzip,deflate',
'Accept': '*/*',
'User-Agent': 'python-requests/2.8.0',
'Connection': 'keep-alive',
'X-MW-PUBLIC-KEY': 'XXXX',
'X-MW-SIGNATURE': 'ZZZZ',
'X-MW-TIMESTAMP': '1444407416'

Anyone out there experience something similar or have suggestions? Thanks!
 

twisted1919

Administrator
Staff member
Hey,

While i don't have any experience with python, maybe adding the code that generates the actual signature would help.

Thanks.
 

Ave81

New Member
Ok, here's the code that generate the signature:
Code:
import time
import logging
import urllib
import hashlib
import hmac

from google.appengine.api import urlfetch # this is substitute for "requests" for GAE


def create_signature(method, url, headers, private_key):
params = headers
separator = '&' # hardcoded for testing - because the url already contains GET query
signature_string = ''.join([method.upper(), ' ', url, separator, urllib.urlencode(params)])
logging.debug(signature_string)
return hmac.new(str(private_key), signature_string, hashlib.sha1).hexdigest()[/INDENT]


def get_lists():
method = 'GET' # hardcoded for testing
action = 'lists' # hardcoded for testing
url = "http://api.sendreach.com/index.php?%s" % (action + '?page=1&per_page=2')[/INDENT]

public_key = 'YYY'

private_key = 'XXX'

headers = {
'X-MW-PUBLIC-KEY': public_key,
'X-MW-TIMESTAMP': str(int(time.time())),
# 'X-MW-REMOTE-ADDR': '', # not mandatory ??
}
signature = create_signature(method, url, headers, private_key)

headers['X-MW-SIGNATURE'] = signature

response = urlfetch.fetch(
method=method,
url=url,
headers=headers
)
return response
 
Last edited by a moderator:

twisted1919

Administrator
Staff member
@Ave81 - From what i see, the creation of signature is not correctly done.

Let's recap a bit, in order to generate the signature you need to follow these steps:
0) Define a big array to hold the data, let's call it params, in python i guess this is a dict:
Code:
params  = {}
1) Add the special headers in the params array
Code:
  'X-MW-PUBLIC-KEY': $publicKey,
  'X-MW-TIMESTAMP': $timestamp,
  'X-MW-REMOTE-ADDR': isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null,
All those 3 are required.
2) Next we have to add POST/PUT/DELETE params in the big param dict.
Not sure how that is done in python, but make sure you add them
3) Once we have this big params dict, we need to sort it by key
http://stackoverflow.com/questions/9001509/how-can-i-sort-a-dictionary-by-key
4)
Code:
$separator = $client->paramsGet->count > 0 && strpos($requestUrl, '?') !== false ? '&' : '?';
Here $client->paramsGet is actually the $_GET global array. We need to see if there are elements in it and if the url we will reques already contains the question mark in it. If it contains the question mark, then the query args separator will be a &, otherwise a ?
Code:
$signatureString = strtoupper($client->method) . ' ' . $requestUrl . $separator . http_build_query($params, '', '&');
This should translate in something like:
Code:
GET http://api.sendreach.com/index.php/lists?page=1&per_page=2
Code:
$signature = hash_hmac('sha1', $signatureString, $privateKey, false);
This i think you got right.

5) You put this resulted signature in the headers, like you already did and then you do the actual request, like you did.


This is pretty much as i can explain it, for more info, you'll have to look at https://github.com/twisted1919/mailwizz-php-sdk/blob/master/MailWizzApi/Http/Request.php#L218 as the code is very self explanatory.
 

twisted1919

Administrator
Staff member
You're missing 'X-MW-REMOTE-ADDR' from that string, as i said, all that is required can be found here: https://github.com/twisted1919/mailwizz-php-sdk/blob/master/MailWizzApi/Http/Request.php#L218 ;)

Keep in mind that if you're doing a post request, your sig string will transform into smth like:
Code:
POST http://dashboard.sendreach.com/api/index.php/lists/_UNIQUE_ID_/subscribers?EMAIL=a@a.com&FNAME=b&LNAME=c&&X-MW-PUBLIC-KEY=XXXX&X-MW-REMOTE-ADDR=xyz.xyz.xyz.zyx&X-MW-TIMESTAMP=1444407416
It's important to order the params, asc. by their name.
 

KenKen

New Member
Hi twisted,

I was able to get GET working but POST is something I am still figuring out in Python.
I am trying to subscribe this test user but I get this error message.
{"status":"error","error":"Please provide the subscriber email address."}
and here is my post address for creating special signature.
POST http://mydomain.com/api/index.php/l...DDR=328.199.125.204&X-MW-TIMESTAMP=1458185041

and here's actual url
http://mydomain.com/api/index.php/l...bscribers?EMAIL=abcd@40gmail.com.com&FNAME=kk

Thank you in advance!
 

BirdyUK

Member
@KenKen - Can you provide the full code, If you cant provide it here please use pastebin.

Code:
 If you can provide it here then please use "[CODE]"   "[/ CODE] "  but without the double quotes "" or spaces to ensure the code is structred correctly when posting it in this forum.
Thanks
 

KenKen

New Member
Just revised this reply with correct code. Please see the code below.

For Python users, just make sure that you use post function in Requests. I was sending params instead of data which was not correct.

Thank you twisted and BirdyUK. Figured out after looking at your php code.

Cheers,

Code:
def subscribe_user_mail():
    #Required POST values
    method = 'POST'
    action = 'lists'
    uid = '1234uid'
    url = "http://mydomain.com/api/index.php/%s" % (action+'/'+uid+'/subscribers?EMAIL=johnkikiki%40gmail.com')
    public_key = '1234publickey'
    private_key = '1234privatekey'
    current_time = str(int(time.time()))
    remote_addr = '190.190.105.200'
 
    #Add three values into the header
    headers_o = {
        'X-MW-PUBLIC-KEY': public_key,
        'X-MW-TIMESTAMP': current_time,
        'X-MW-REMOTE-ADDR': remote_addr
    }
 
    #Order header keys by asc
    headers = collections.OrderedDict(sorted(headers_o.items()))

    #Create special signature
    signature_string = ''.join([method.upper(), ' ', url, '&', urllib.urlencode(headers)])
    signature = hmac.new(str(private_key), signature_string, hashlib.sha1).hexdigest()
 
    #Add special signature in the header
    headers['X-MW-SIGNATURE'] = signature
 
    #Send POST request
    response = requests.post(
        url=url,
        headers=headers,
        data={'EMAIL':'johnkikiki%40gmail.com'}
    )
    #Print output
    return response
    #Print POST url and url accessed via API
    return response.url +','+signature_string
 
Last edited:
Top