简体   繁体   中英

Authenticating with Coinbase's Exchange's API (HMAC) using requests in Python

I am implementing Coinbase's exchange API using custom auth in requests-python. The following code works with all the (authenticated) GET-based calls, but fails for all the authenticated POST-based calls (I haven't tried with DELETE or UPDATE verbs). I don't understand why the signature wouldn't work for both, because the payload is timestamp + method + path for GETs and timestamp + method + path + body for PUTs, so custom auth seems correct. Something is going wrong with adding the body and changing GET to POST. Thanks!

You can get your API keys for trying it out here: https://gdax.com/settings

import json, hmac, hashlib, time, requests, base64
from requests.auth import AuthBase

class CoinbaseAuth(AuthBase):
    SIGNATURE_HTTP_HEADER = 'CB-ACCESS-SIGN'
    TIMESTAMP_HTTP_HEADER = 'CB-ACCESS-TIMESTAMP'
    KEY_HTTP_HEADER = 'CB-ACCESS-KEY'
    PASSPHRASE_HTTP_HEADER = 'CB-ACCESS-PASSPHRASE'

    def __init__(self, api_key, secret_key, passphrase):
        self.api_key = api_key
        self.secret_key = secret_key
        self.passphrase = passphrase

    def __call__(self, request):
        #Add headers        
        request.headers[CoinbaseAuth.KEY_HTTP_HEADER] = self.api_key
        request.headers[CoinbaseAuth.PASSPHRASE_HTTP_HEADER] = self.passphrase
        timestamp = str(time.time())        
        request.headers[CoinbaseAuth.TIMESTAMP_HTTP_HEADER] = timestamp

        #add signature
        method = request.method
        path = request.path_url
        content = request.body
        message = timestamp + method + path        
        if content:
            message += content 
        hmac_key = base64.b64decode(self.secret_key)
        sig = hmac.new(hmac_key, message, hashlib.sha256)
        sig_b64 = sig.digest().encode("base64").rstrip("\n")

        #Add signature header
        request.headers[CoinbaseAuth.SIGNATURE_HTTP_HEADER] = sig_b64
        return request

#Get your keys here: https://gdax.com/settings
key = 'KEY GOES HERE'
secret = 'SECRET GOES HERE'
passphrase = 'PASSPHRASE GOES HERE'

api_url = 'https://api.gdax.com:443/'
auth = CoinbaseAuth(API_KEY, API_SECRET, API_PASS)

#GETs work, shows account balances
r = requests.get(api_url + 'accounts', auth=auth)
print r.json()

#POSTs fail: {message: 'invalid signature'}
order = {}        
order['size'] = 0.01
order['price'] = 100
order['side'] = 'buy'
order['product_id'] = 'BTC-USD'
r = requests.post(api_url + 'orders', data=json.dumps(order), auth=auth)
print r.json()

And the output:

GET call: 200: [{u'available': .......}]

POST call: 400: {u'message': u'invalid signature'}

EDIT: POSTing 'a' instead of valid JSON-encoded data results in the same signature error (rather than a JSON decoding error from the server), so I don't think it is the way I'm forming the data. Notably, if I omit the body -- request.post(..., data='',...) --- the server responds appropriately with {u'message': u'Missing product_id'} .

I don't know why, but if I change the data keyword argument to requests.post() to json it works:

r = requests.post(api_url + 'orders', json=order, auth=auth)

EDIT: The only thing that changes, AFAICT, is the content-type in the header is changed from text to JSON. So it is likely that or a unicode vs ASCII encoding issue. Here's the issue for the library that added this feature recently: https://github.com/kennethreitz/requests/issues/2025#issuecomment-46337236

I believe the content needs to be a json string with no spaces (this is what the node example does anyway). Maybe try this:

message += json.dumps(content).replace(' ', '')

I had the same exact problem until I looked at the public gdax API for nodeJS and found that they are using some additional headers that were not mentioned in the GDAX API docs. I added them and then it started working. See my answer to the following: GDAX API Always Returns Http 400 "Invalid Signature" Even though I do it exactly like in the API Doc

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM