Ошибка аутентификации API - python - PullRequest
0 голосов
/ 28 июня 2019

Документы API: здесь

Единственный пример кода на Java, здесь

Каждый раз, когда я пытаюсь аутентифицироваться, я получаю:

{
    "error": "Authorization field missing, malformed or invalid"
}

Я много раз проходил аутентификационные документы и все еще не повезло.

Вот мой код:

import requests
import secrets
import codecs

from wsgiref.handlers import format_date_time
from datetime import datetime
from time import mktime

import hashlib
import hmac
import base64

import urllib.parse

key     = '<API_KEY>'
secret  = '<API_SECRET>'

# Getting current time
now = datetime.now()
stamp = mktime(now.timetuple())

# Formats time into this format --> Mon, 25 Jul 2016 16:36:07 GMT
formated_time = format_date_time(stamp)

# Generates a secure random string for the nonce
nonce = secrets.token_urlsafe(30)

# Combines date and nonce into a single string that will be signed
signature_string = 'date' + ':' + formated_time + '\n' + 'x-mod-nonce' + ':' + nonce

# Expected output example --> date: Mon, 25 Jul 2016 16:36:07 GMT\nx-mod-nonce: 28154b2-9c62b93cc22a-24c9e2-5536d7d

# Encodes secret and message into a format that can be signed
secret = bytes(secret, encoding='utf-8')
message = bytes(signature_string,encoding='utf-8')

# Signing process
digester = hmac.new(secret, message, hashlib.sha1)

# Converts to hex
hex_code = digester.hexdigest()

# Decodes the signed string in hex into base64
b64 = codecs.encode(codecs.decode(hex_code, 'hex'), 'base64').decode()

# Encodes the string so it is safe for URL
url_safe_code = urllib.parse.quote(b64,safe='')

# Adds the key and signed response
authorization = f'Signature keyId="{key}",algorithm="hmac-sha1",headers="date x-mod-nonce",signature="{url_safe_code}"'

account_id = 'A120BU48'

url = f'https://api-sandbox.modulrfinance.com/api-sandbox/accounts/{account_id}'

headers = {
    'Authorization': authorization, # Authorisation header
    'Date' : formated_time,         # Date header
    'x-mod-nonce': nonce,           # Addes nonce
    'accept': 'application/json',
    }

response = requests.get(url,headers=headers)

print(response.text)

Я не уверен, гдепроцесс идет не так, насколько я знаю, подпись подписывается правильно, как я добавил в тестовые данные из примера аутентификации, и я получаю ожидаемую строку.

Если вы хотите попробовать с реальными ключами API, зарегистрируйтесь для доступа здесь

Документы для конечной точки API, которую я пытаюсь вызвать, здесь

1 Ответ

0 голосов
/ 28 июня 2019

В связанных документах есть пробел между двоеточием и значениями.

signature_string = 'date' + ':' + formated_time + '\n' + 'x-mod-nonce' + ':' + nonce

должно быть:
signature_string = 'date' + ': ' + formated_time + '\n' + 'x-mod-nonce' + ': ' + nonce

или (проще):
signature_string = 'date: ' + formated_time + '\n' + 'x-mod-nonce: ' + nonce

Обновление
Я зарегистрировался, чтобы увидеть, что происходит. Я также запустил ваш код на примере, приведенном в документации, и увидел, что подпись не совсем верна.
В дополнение к изменению, которое я предложил выше, необходимы дальнейшие изменения.

После смены строки

b64 = codecs.encode(codecs.decode(hex_code, 'hex'), 'base64').decode()

до

b64 = codecs.encode(codecs.decode(hex_code, 'hex'), 'base64').decode().strip()

подпись примера соответствует.

После этого я смог подключиться к API своими собственными ключами.

Вот полный рабочий код:

import codecs
import hashlib
import hmac
import secrets
import urllib.parse
from datetime import datetime
from time import mktime
from wsgiref.handlers import format_date_time

import requests

key = '<key>'
secret = '<secret>'
account_id = '<account id>'
url = f'https://api-sandbox.modulrfinance.com/api-sandbox/accounts/{account_id}'

# Getting current time
now = datetime.now()
stamp = mktime(now.timetuple())

# Formats time into this format --> Mon, 25 Jul 2016 16:36:07 GMT
formatted_time = format_date_time(stamp)

# Generates a secure random string for the nonce
nonce = secrets.token_urlsafe(30)

# Combines date and nonce into a single string that will be signed
signature_string = 'date' + ': ' + formatted_time + '\n' + 'x-mod-nonce' + ': ' + nonce

# Encodes secret and message into a format that can be signed
secret = bytes(secret, encoding='utf-8')
message = bytes(signature_string, encoding='utf-8')

# Signing process
digester = hmac.new(secret, message, hashlib.sha1)

# Converts to hex
hex_code = digester.hexdigest()

# Decodes the signed string in hex into base64
b64 = codecs.encode(codecs.decode(hex_code, 'hex'), 'base64').decode().strip()

# Encodes the string so it is safe for URL
url_safe_code = urllib.parse.quote(b64, safe='')

# Adds the key and signed response
authorization = f'Signature keyId="{key}",algorithm="hmac-sha1",headers="date x-mod-nonce",signature="{url_safe_code}"'

headers = {
    'Authorization': authorization,  # Authorisation header
    'Date': formatted_time,  # Date header
    'x-mod-nonce': nonce,  # Adds nonce
    'accept': 'application/json',
}

response = requests.get(url, headers=headers)

print(response.text)
...