使用Python中的请求使用Coinbase的Exchange API(HMAC)进行身份验证

时间:2015-01-27 02:24:02

标签: python authentication python-requests coinbase-api

我在requests-python中使用自定义身份验证实现Coinbase's exchange API。以下代码适用于所有(经过身份验证的)基于GET的调用,但对于所有经过身份验证的基于POST的调用都失败(我没有尝试使用DELETE或UPDATE谓词)。我不明白为什么签名对两者都不起作用,因为GET的有效载荷为timestamp + method + path,PUT的有效载荷为timestamp + method + path + body,因此自定义auth似乎是正确的。添加正文并将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()

输出:

GET电话:200: [{u'available': .......}]

POST电话:400: {u'message': u'invalid signature'}

编辑:发布'a'而不是有效的JSON编码数据导致相同的签名错误(而不是来自服务器的JSON解码错误),所以我不认为这是我形成数据的方式。值得注意的是,如果我省略了正文 - request.post(..., data='',...) ---服务器会对{u'message': u'Missing product_id'}做出适当的响应。

3 个答案:

答案 0 :(得分:3)

我不知道为什么,但如果我将data关键字参数更改为requests.post()json则可行:

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

编辑:唯一改变的是AFAICT,标题中的内容类型从文本更改为JSON。所以很可能是unicode vs ASCII编码问题。以下是最近添加此功能的库的问题:https://github.com/kennethreitz/requests/issues/2025#issuecomment-46337236

答案 1 :(得分:0)

我认为内容需要是一个没有空格的json字符串(这就是节点示例所做的事情)。也许试试这个:

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

答案 2 :(得分:0)

在查看nodeJS的公共gdax API之前,我遇到了同样的问题,并发现他们正在使用GDAX API文档中未提及的一些其他标头。我添加了它们然后开始工作了。请参阅我对以下内容的回答:GDAX API Always Returns Http 400 "Invalid Signature" Even though I do it exactly like in the API Doc