Аутентификация с помощью Coinbase Exchange API (HMAC) с использованием запросов в Python

Я реализую API обмена Coinbase, используя пользовательскую аутентификацию в request-python. Следующий код работает со всеми (аутентифицированными) вызовами на основе GET, но не работает для всех аутентифицированных вызовов на основе POST (я не пробовал с глаголами DELETE или UPDATE). Я не понимаю, почему подпись не будет работать для обоих, потому что полезная нагрузка timestamp + method + path для GET и timestamp + method + path + body для PUT, поэтому пользовательская аутентификация кажется правильной. Что-то не так с добавлением тела и изменением GET на POST. Спасибо!

Вы можете получить ключи API для опробования здесь: 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()

И вывод:

ПОЛУЧИТЬ вызов: 200: [{u'available': .......}]

Почтовый вызов: 400: {u'message': u'invalid signature'}

РЕДАКТИРОВАТЬ: POSTing 'a' вместо действительных данных в кодировке JSON приводит к той же ошибке подписи (а не к ошибке декодирования JSON с сервера), поэтому я не думаю, что это то, как я формирую данные. Примечательно, что если я опускаю тело -- request.post(..., data='',...) --- сервер отвечает соответствующим образом {u'message': u'Missing product_id'}.


person Streblo    schedule 27.01.2015    source источник


Ответы (3)


Я не знаю почему, но если я изменю аргумент ключевого слова data на requests.post() на json, он сработает:

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

РЕДАКТИРОВАТЬ: Единственное, что меняется, AFAICT, это то, что тип содержимого в заголовке изменяется с текста на JSON. Так что вполне вероятно, что это или проблема с кодировкой Unicode и ASCII. Вот проблема с библиотекой, которая недавно добавила эту функцию: https://github.com/kennethreitz/requests/issues/2025#issuecomment-46337236

person Streblo    schedule 27.01.2015
comment
Между вопросом и этим приказом мне действительно кажется, что вы изменились немного больше, чем это. data=json.dumps(order) становится json = order - person mardlin; 26.05.2015

Я считаю, что содержимое должно быть строкой json без пробелов (в любом случае это то, что делает пример узла). Может быть, попробуйте это:

message += json.dumps(content).replace(' ', '')
person bhelx    schedule 27.01.2015
comment
Спасибо за предложение. Я думал, что это какой-то странный случай JSON-кодирования node и python. Я пытался отправить короткую бессмысленную строку, например 'a', которая не может быть декодирована на сервере, но, по крайней мере, подпись должна быть действительной. Увы, это было не так. Я обновлю пост, чтобы включить ваше предложение! - person Streblo; 27.01.2015

У меня была точно такая же проблема, пока я не посмотрел общедоступный API gdax для nodeJS и не обнаружил, что они используют некоторые дополнительные заголовки, которые не упоминались в документах API GDAX. Я добавил их, и тогда он начал работать. См. мой ответ на следующее: точно/48417766#48417766">GDAX API всегда возвращает неверную подпись Http 400, хотя я делаю это точно так же, как в документе API

person Calicoder    schedule 24.01.2018