GDAX / Coinbase API authentication process: Unicode-objects must be encoded before hashing
Asked Answered
S

2

7

I have a lot of experience coding, but Python is new territory for me.

I'm using the CoinbaseExchangeAuth class to access the private endpoints of the GDAX API. I write some simple code...

api_url = 'https://public.sandbox.gdax.com/'
auth = CoinbaseExchangeAuth(API_KEY, API_SECRET, API_PASS)

(note that I have accurately defined the api key, secret and pass correctly before these lines of code - for the sandbox)

Then I write:

r = requests.get(api_url + 'accounts', auth=auth)

Run the code and get this error:

File "a:\PythonCryptoBot\Bot1.0\CoinbaseExhangeAuth.py", line 16, in call signature = hmac.new(hmackey, message, hashlib.sha256) File "C:\Users\Dylan\AppData\Local\Programs\Python\Python35-32\lib\hmac.py", line 144, in new return HMAC(key, msg, digestmod) File "C:\Users\Dylan\AppData\Local\Programs\Python\Python35-32\lib\hmac.py", line 84, in __init_ self.update(msg) File "C:\Users\Dylan\AppData\Local\Programs\Python\Python35-32\lib\hmac.py", line 93, in update self.inner.update(msg) TypeError: Unicode-objects must be encoded before hashing

Also note that I have tried to API_KEY.encode('utf-8') and same with others. - doesn't seem to do anything.

Sectorial answered 15/12, 2017 at 2:2 Comment(0)
A
20

The code you're using is written for Python2, you can't expect it to run as it is. I've modified some parts to make it Python3 compatible.

Original code:

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

# Create custom authentication for Exchange
class CoinbaseExchangeAuth(AuthBase):
    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):
        timestamp = str(time.time())
        message = timestamp + request.method + request.path_url + (request.body or '')
        hmac_key = base64.b64decode(self.secret_key)
        signature = hmac.new(hmac_key, message, hashlib.sha256)
        signature_b64 = signature.digest().encode('base64').rstrip('\n')

        request.headers.update({
            'CB-ACCESS-SIGN': signature_b64,
            'CB-ACCESS-TIMESTAMP': timestamp,
            'CB-ACCESS-KEY': self.api_key,
            'CB-ACCESS-PASSPHRASE': self.passphrase,
            'Content-Type': 'application/json'
        })
        return request

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

# Get accounts
r = requests.get(api_url + 'accounts', auth=auth)
print r.json()
# [{"id": "a1b2c3d4", "balance":...

# Place an order
order = {
    'size': 1.0,
    'price': 1.0,
    'side': 'buy',
    'product_id': 'BTC-USD',
}
r = requests.post(api_url + 'orders', json=order, auth=auth)
print r.json()

Modified code:

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

# Create custom authentication for Exchange
class CoinbaseExchangeAuth(AuthBase):
    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):
        timestamp = str(time.time())
        message = timestamp + request.method + request.path_url + (request.body or b'').decode()
        hmac_key = base64.b64decode(self.secret_key)
        signature = hmac.new(hmac_key, message.encode(), hashlib.sha256)
        signature_b64 = base64.b64encode(signature.digest()).decode()

        request.headers.update({
            'CB-ACCESS-SIGN': signature_b64,
            'CB-ACCESS-TIMESTAMP': timestamp,
            'CB-ACCESS-KEY': self.api_key,
            'CB-ACCESS-PASSPHRASE': self.passphrase,
            'Content-Type': 'application/json'
        })
        return request

api_url = 'https://api.gdax.com/'
auth = CoinbaseExchangeAuth(APIKEY, API_SECRET,  API_PASS)

# Get accounts
r = requests.get(api_url + 'accounts', auth=auth)
print(r.json())
# [{"id": "a1b2c3d4", "balance":...

# Place an order
order = {
    'size': 1.0,
    'price': 1.0,
    'side': 'buy',
    'product_id': 'BTC-USD',
}
r = requests.post(api_url + 'orders', json=order, auth=auth)
print(r.json())

Note that i've only "translated" the original code, i can't guarantee for it's functionality or security.

Applicant answered 15/12, 2017 at 2:57 Comment(7)
So, it is definitely working now, thank you a ton. But now I get this error. Traceback (most recent call last): File "a:\PythonCryptoBot\Bot1.0\CoinbaseExhangeAuth.py", line 88, in <module> print(r.json())Sectorial
Ok, that means that .json() failed to parse the response. Can you print r.text and r.status_code?Applicant
And r.text? Is it json?Applicant
when i try making a order r = requests.post(api_url + 'orders', json=order, auth=auth) i get the status code 405? which is not on the gdax api possible errors. huh...Sectorial
That's strange, I tested with python2 and python3 and the headers are identical. The only difference is that signature_b64 is bytes, but that shouldn't be a problem. I fixed it anyway. I'll run a couple more tests, if i find anything useful i'll let you knowApplicant
switched to 2.7 to prevent further confusion - it does that same thing when i print(r.text). Spits out an entire html doc. And when I try to print(r.json()) it gives me a "ValueError: No JSON object could be decoded"Sectorial
Then it must be about the url or post data, make sure they are correct. If you save r.text as .html file and open it in a browser it might give you a clue.Applicant
U
1

The modified code you posted didn't quite work for me, but this did!

import hmac, hashlib, time, requests, os
from requests.auth import AuthBase


API_KEY = os.environ.get('API_KEY')
API_SECRET = os.environ.get('API_SECRET')


# Create custom authentication for Coinbase API
class CoinbaseWalletAuth(AuthBase):
    def __init__(self, api_key, secret_key):
        self.api_key = api_key
        self.secret_key = secret_key

    def __call__(self, request):
        timestamp = str(int(time.time()))
        message = timestamp + request.method + request.path_url + (request.body or b'').decode()
        signature = hmac.new(bytes(self.secret_key,'utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()

        request.headers.update({
            'CB-ACCESS-SIGN': signature,
            'CB-ACCESS-TIMESTAMP': timestamp,
            'CB-ACCESS-KEY': self.api_key,
            'CB-VERSION': '2019-11-15'
        })
        return request


api_url = 'https://api.coinbase.com/v2/'
auth = CoinbaseWalletAuth(API_KEY, API_SECRET)

# Get current user
r = requests.get(api_url + 'accounts', auth=auth)
print(r.json())
Undernourished answered 21/9, 2020 at 6:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.