Oauth1.0 API issue with Python

强颜欢笑 提交于 2020-05-27 05:25:09

问题


I'm trying to get magiccardmarket.eu API authentication to work in Python, but no matter whether I'm using rauth or requests_oauthlib, I get 403.

My code is:

#!/usr/bin/python

import logging

import rauth
import requests_oauthlib

logging.basicConfig(level=logging.DEBUG)

mkm_app_token = 'B7VI9Qg2xh855WtR'
mkm_app_secret = '<cut>'
mkm_access_token = 'LQj2rUwOFUJsmuJvCTlny1UzGZSXzHjo'
mkm_token_secret = '<cut>'

url = 'https://sandbox.mkmapi.eu/ws/v1.1/account'

# session = rauth.OAuth1Session(
#   consumer_key=mkm_app_token,
#   consumer_secret=mkm_app_secret,
#   access_token=mkm_access_token,
#   access_token_secret=mkm_token_secret,
# )

session = requests_oauthlib.OAuth1Session(
    mkm_app_token,
    client_secret=mkm_app_secret,
    resource_owner_key=mkm_access_token,
    resource_owner_secret=mkm_token_secret,
)

r = session.get(url)
print(r)

When I look at debugging info, everything seems fine (of course besides 403 response):

DEBUG:requests_oauthlib.oauth1_auth:Signing request <PreparedRequest [GET]> using client <Client nonce=None, signature_method=HMAC-SHA1, realm=None, encoding=utf-8, timestamp=None, resource_owner_secret=****, decoding=utf-8, verifier=None, signature_type=AUTH_HEADER, rsa_key=None, resource_owner_key=LQj2rUwOFUJsmuJvCTlny1UzGZSXzHjo, client_secret=****, callback_uri=None, client_key=B7VI9Qg2xh855WtR>
DEBUG:requests_oauthlib.oauth1_auth:Including body in call to sign: False
DEBUG:oauthlib.oauth1.rfc5849:Collected params: [(u'oauth_nonce', u'87129670621454425921416648590'), (u'oauth_timestamp', u'1416648590'), (u'oauth_consumer_key', u'B7VI9Qg2xh855WtR'), (u'oauth_signature_method', u'HMAC-SHA1'), (u'oauth_version', u'1.0'), (u'oauth_token', u'LQj2rUwOFUJsmuJvCTlny1UzGZSXzHjo')]
DEBUG:oauthlib.oauth1.rfc5849:Normalized params: oauth_consumer_key=B7VI9Qg2xh855WtR&oauth_nonce=87129670621454425921416648590&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1416648590&oauth_token=LQj2rUwOFUJsmuJvCTlny1UzGZSXzHjo&oauth_version=1.0
DEBUG:oauthlib.oauth1.rfc5849:Normalized URI: https://sandbox.mkmapi.eu/ws/v1.1/account
DEBUG:oauthlib.oauth1.rfc5849:Base signing string: GET&https%3A%2F%2Fsandbox.mkmapi.eu%2Fws%2Fv1.1%2Faccount&oauth_consumer_key%3DB7VI9Qg2xh855WtR%26oauth_nonce%3D87129670621454425921416648590%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1416648590%26oauth_token%3DLQj2rUwOFUJsmuJvCTlny1UzGZSXzHjo%26oauth_version%3D1.0
DEBUG:oauthlib.oauth1.rfc5849:Signature: 291LTesHZR6W4bjZ1NqSW5hEgoM=
DEBUG:oauthlib.oauth1.rfc5849:Encoding URI, headers and body to utf-8.
DEBUG:requests_oauthlib.oauth1_auth:Updated url: https://sandbox.mkmapi.eu/ws/v1.1/account
DEBUG:requests_oauthlib.oauth1_auth:Updated headers: {'Accept': '*/*', 'Connection': 'keep-alive', 'Accept-Encoding': 'gzip, deflate', 'Authorization': 'OAuth oauth_nonce="87129670621454425921416648590", oauth_timestamp="1416648590", oauth_version="1.0", oauth_signature_method="HMAC-SHA1", oauth_consumer_key="B7VI9Qg2xh855WtR", oauth_token="LQj2rUwOFUJsmuJvCTlny1UzGZSXzHjo", oauth_signature="291LTesHZR6W4bjZ1NqSW5hEgoM%3D"', 'User-Agent': 'python-requests/2.4.3 CPython/2.7.8 Darwin/14.0.0'}
DEBUG:requests_oauthlib.oauth1_auth:Updated body: None
INFO:requests.packages.urllib3.connectionpool:Starting new HTTPS connection (1): sandbox.mkmapi.eu
DEBUG:requests.packages.urllib3.connectionpool:"GET /ws/v1.1/account HTTP/1.1" 403 None

This is not an issue of authentication details, which are provided on account profile page when you request dedicated application API access, since those details work fine with PHP example provided by the site: https://www.mkmapi.eu/ws/documentation/API:Auth_libcurl

When I go through site's documentation, nothing seems out of ordinary: https://www.mkmapi.eu/ws/documentation/API:Auth_Overview

I honestly don't know where to go from here...


回答1:


I realized that the code above with requests_oauthlib didn't build the header like it was layed out in the documentation, so I ended up inventing the wheel again and building the header myself, following the steps outlined in the documentation: https://www.mkmapi.eu/ws/documentation/API:Auth_OAuthHeader

The following script is not very beautiful, but it does its job.

import requests
from urllib import quote_plus as rawurlencode
import time
import string
import random
import operator
from hashlib import sha1
from hmac import new as hmac


def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

# personal Info - taken from https://www.mkmapi.eu/ws/documentation/API:Auth_Overview
mkmAppToken = 'bfaD9xOU0SXBhtBP'
mkmAppSecret = 'pChvrpp6AEOEwxBIIUBOvWcRG3X9xL4Y'
mkmAccessToken = 'lBY1xptUJ7ZJSK01x4fNwzw8kAe5b10Q'
mkmAccessSecret = 'hc1wJAOX02pGGJK2uAv1ZOiwS7I9Tpoe'

# Url to access on mkm
# note that this deviates from the example in the header documentation (https://www.mkmapi.eu/ws/documentation/API:Auth_OAuthHeader) which uses
#accessUrl = 'https://www.mkmapi.eu/ws/v1.1/account'
accessUrl = 'https://www.mkmapi.eu/ws/v1.1/output.json/account'

#Method for access

MyMethod = "GET"

baseString = MyMethod + "&" + rawurlencode(accessUrl) + "&"

# create a random string
# the documentation in https://www.mkmapi.eu/ws/documentation/API:Auth_OAuthHeader uses
#nonce = 53eb1f44909d6
nonce = id_generator(8)

# what time is it?
# the documentation in https://www.mkmapi.eu/ws/documentation/API:Auth_OAuthHeader uses
#now = 1407917892

now = str(int(time.time()))

MyOauthmethod = "HMAC-SHA1"
MyOauthver = "1.0"

# define Parameters and values, order doesn't matter
paramDict ={"oauth_consumer_key":mkmAppToken, "oauth_token" :mkmAccessToken, "oauth_nonce":nonce, "oauth_timestamp":now, "oauth_signature_method":MyOauthmethod, "oauth_version":MyOauthver}

# sorting of parameters is done here
sorted_paramDict = sorted(paramDict.items(), key=operator.itemgetter(0))

#collect the full parameters string
paramStr = ''

for kv in sorted_paramDict:
    paramStr = paramStr + kv[0] + "=" + kv[1] + "&"


# and get rid of the trailing ampersand
paramStr = paramStr[:-1]


#concatenate request and oauth parameters
baseString = baseString + rawurlencode(paramStr)

# concatenate both keys
signingKey = rawurlencode(mkmAppSecret) + "&" + rawurlencode(mkmAccessSecret)
# and create a hased signature with the key and the baseString
Signature = hmac(signingKey, baseString, sha1).digest().encode('base64')[:-1]


# construct the header from the parameters and the URL and the signature
MyHeader = 'OAuth ' + 'realm="' + accessUrl + '", ' 

for kv in sorted_paramDict:
    MyHeader += kv[0] + '="' + kv[1] + '",'

MyHeader += 'oauth_signature="' + Signature +'"'

headers = {'Authorization': MyHeader}

# and now requests can do its magic (pun intended)

r = requests.get(accessUrl, headers=headers)

outjson = r.json()



回答2:


You need to provide the realm as an argument to the OAuth1Session, like so:

session = requests_oauthlib.OAuth1Session(
    mkm_app_token,
    client_secret=mkm_app_secret,
    resource_owner_key=mkm_access_token,
    resource_owner_secret=mkm_token_secret,
    realm=url
)

Other things I have run into in the past include the fact that the mkm api doesn't (or at least didn't) accept URI-escaped parameters, so you may need to unescape them.




回答3:


For anyone who's reading in 2020, there's no need to reinvent the wheel, just pass the Oauth header and the parameters to requests, here's an example with metaproducts/find:

import requests
from requests_oauthlib import OAuth1
import json 
import passwords

card_name = 'Tarmogoyf'

output = 'output.json'
base_url = 'https://api.cardmarket.com/ws/v2.0/' + output + '/'

url = base_url + 'metaproducts/find'
params={'search': card_name}

headeroauth = OAuth1(
    realm = url,
    client_key = passwords.mkm_app_token,
    client_secret = passwords.mkm_app_secret,
    resource_owner_key = passwords.mkm_access_token,
    resource_owner_secret = passwords.mkm_token_secret,
)

response = requests.get(
    url,
    params,
    auth = headeroauth
    )


if (response.ok == True):
    json_response = response.json()
    print(json.dumps(json_response, indent=4, sort_keys=True))
else:
    print(str(response.status_code) + " " + response.reason)
    exit()

The /output.json/ part of the string makes the output JSON instead of XML



来源:https://stackoverflow.com/questions/27075944/oauth1-0-api-issue-with-python

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!