Converting curl to python for accessing an api - python

I'm having trouble converting curl code to python in order to access a token to an API.
The given code is:
curl -k -d "grant_type=client_credentials&scope=PRODUCTION" -H "Authorization :Basic <long base64 value>, Content-Type: application/x-www-form-urlencoded" https://api-km.it.umich.edu/token
I know that -H indicates a header, however Im not sure what to do with -d. So far I have:
authorizationcode = 'username:password'
authorizationcode = base64.standard_b64encode(authorizationcode)
header = {'Authorization ': 'Basic ' + authorizationcode, 'Content-Type': 'application/x-www-form-' + authorizationcode}
r = requests.post('https://api-km.it.umich.edu/token',
    data = 'grant_type=client_credentials&scope=PRODUCTION',
    headers = header)
Also, these are the instructions:
Obtain your consumer key and consumer secret from the API Directory. These are generated on the Subscriptions page after an application is successfully subscribed an API.
Combine the consumer key and consumer secret keys in the format: consumer-key:consumer-secret. Encode the combined string using base64. Most programming languages have a method to base64 encode a string. For an example of encoding to base64. Visit the base64encode site for more information.
Execute a POST call to the token API to get an access token.
Our data is correct however we are getting a 415 error from the server.
Assistance would be greatly appreciated.

A 415 Error is described in http://www.checkupdown.com/status/E415.html as "Unsupported media type"
As #krock mentioned, the content-type is not specified as application/x-www-form-urlencoded, rather it is being set to x-www-form- + your auth code.

You are setting an incorrect Content-Type header:
'Content-Type': 'application/x-www-form-' + authorizationcode
That should be 'application/x-www-form-urlencode'. You do not, however, have to set it at all as requests does this for you automatically if you pass in a dictionary to the data argument.
requests will also handle the Authorization header for you; pass in the username and password to the auth argument as a tuple:
auth = ('username', 'password')
params = {'grant_type': 'client_credentials', 'scope': 'PRODUCTION'}
r = requests.post('https://api-km.it.umich.edu/token', data=params, auth=auth)
where user and password are the parts before and after the colon. requests will produce the correct Basic base64-encoded header for you from those two strings.

Related

Python OAuth Client Credentials Grant Type Returned 400, [error: unsupported grant] type but Curl Works Fine

I am very new to APIs (still learning) and I encountered a very weird issue with Python requests library when trying to initiate an OAuth Authentication flow with Client Credentials Grant Type.
For some reason, whenever I used my Python script (with the help of requests library) to send the HTTP request to the authentication endpoint, I always get
Response Status Code: 400
Response Body/Data returned: {"error":"unsupported_grant_type"}
However, if I tried using curl command line tool to send the request, I will get a successful response with status code 200 with the access token in the response body like this:
{'access_token': 'some access token',
'expires_in': 'num_of_seconds',
'token_type': 'Bearer'}
As a matter of fact, if I tried sending the request using Curl command line tool WITHIN my Python Script (with subprocess.Popen function), I can get the response with status code 200 and the access token with no problem.
Now, with that said, here's the Python script that I used to send the request to initiate the OAuth authentication flow:
import requests
import os
import base64
clientCredentialEndpoint = "https://base_url/path/token"
client_id = os.environ.get('CLIENT_ID')
client_secret = os.environ.get('CLIENT_SECRET')
# -- Encode the <client_id:client_secret> string to base64 --
auth_value = f'{client_id}:{client_secret}'
auth_value_bytes = auth_value.encode('ascii')
auth_value_b64 = base64.b64encode(auth_value_bytes).decode('ascii')
queryParams ={
'grant_type':'client_credentials',
'scope':'get_listings_data'
}
headers = {
'Authorization':f'Basic {auth_value_b64}',
'Content-Type':'application/x-www-form-urlencoded'
}
# send the post request to Authorisation server
response = requests.post(
clientCredentialEndpoint,
params=queryParams,
headers=headers,
)
print(response.status_code)
print(response.text)
whereas the curl command that I used (and worked) to send the request is:
curl -X POST -u '<client_id>:<client_secret>' \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'grant_type=client_credentials&scope=get_listings_data' \
'https://base_url/path/token'
Again, like I said, if I execute this curl command inside a Python script, it will successfully return the access token with no issue.
Does anyone know what I did wrong in my Python script which caused my request to always fail?
Thanks in advance!
My goodness me, I just realised that the -d in the curl command does not correspond to query params, it stands for 'data'.
Hence, I just need to change my Python script requests.post() a bit so that it looks like this:
response = requests.post(
clientCredentialEndpoint,
data=queryParams,
headers=headers,
)
Hope this helps others.

How to send POST request with each payload on its own line using Python requests

I have to send a POST request to the /batch endpoint of : 'https://www.google-analytics.com'.
As mentioned in the Documentation I have to send the request to /batch endpoint and specify each payload on its own line.
I was able to achieve this using POSTMAN as follows:
My query is to make a POST request using Python's requests library
I tried something like this :
import requests
text = '''v=1&cid=43223523&tid=UA-XXXXXX-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=bookmarks&ev=13
v=1&cid=43223523&tid=UA-XXXXXX-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=upvotes&ev=65
v=1&cid=43223523&tid=UA-XXXXXX-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=questions&ev=15
v=1&cid=43223523&tid=UA-XXXXXX-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=postviews&ev=95'''
response = requests.post('https://www.google-analytics.com/batch', data=text)
but it doesn't works.
UPDATE
I Tried this and it works !
import http.client
conn = http.client.HTTPSConnection("www.google-analytics.com")
payload = "v=1&cid=43223523&tid=UA-200248207-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=bookmarks&ev=13\r\nv=1&cid=43223523&tid=UA-200248207-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=upvotes&ev=63\r\nv=1&cid=43223523&tid=UA-200248207-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=questions&ev=11\r\nv=1&cid=43223523&tid=UA-200248207-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=postviews&ev=23"
headers = {
'Content-Type': 'text/plain'
}
conn.request("POST", "/batch", payload, headers)
res = conn.getresponse()
But the question remains open, what's the issue with requests here.
You don't need to double-escape the newline symbol.
Moreover, you don't need the newline symbol at all for the multi-line string.
And also the indentations you put in your multi-line string are counted:
test = '''abc
def
ghi'''
print(test)
Here's an SO answer that explains this with some additional ways to make long stings: https://stackoverflow.com/a/10660443/4570170
Now the request body.
The documentation says
payload_data – The BODY of the post request. The body must include exactly 1 URI encoded payload and must be no longer than 8192 bytes.
So try uri-encoding your payload:
text = '''v=1&cid=43223523&tid=UA-XXXXXX-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=bookmarks&ev=13
v=1&cid=43223523&tid=UA-XXXXXX-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=upvotes&ev=65
v=1&cid=43223523&tid=UA-XXXXXX-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=questions&ev=15
v=1&cid=43223523&tid=UA-XXXXXX-1&t=event&ec=aggregated_stats&ea=daily_kpi&el=postviews&ev=95'''
text_final = requests.utils.quote(text)
response = requests.post('https://www.google-analytics.com/batch', data=text_final)
Finally , I figured out the solution myself.
Updating for others help.
The problem was I was working on AWS Cloud9 and as mentioned in the documentation
Some environments are not able to send hits to Google Analytics directly. Examples of this are older mobile phones that can't run JavaScript or corporate intranets behind a firewall.
So we just need to include the User Agent parameter
ua=Opera/9.80
in each of our payloads
It works !

Python JSON Web token (JWT) GET request 401 error with Apple Store Connect

In order to generate the token for API Requests, apple outlines the following steps.
The key, kid, and iss have all been verified to work. However in the following python script,
import jwt
import requests
# pseudo, removed secret info
# read the file, currently binary but have tried string too
with open('AuthKey_4..._.p8', 'r+b') as keyfile:
secret = keyfile.read()
expir = round(time.time() + 20 * 60)
# sign the token with the iss, time, key, and kid with the correct alg
token = jwt.encode({'iss': '6...',
'exp': f'{expir}',
'aud': 'appstoreconnect-v1'},
secret, algorithm='ES256',
headers={'alg': 'ES256', 'kid': '4...', 'typ': 'JWT'})
# decode the bytes and create the get request header
s_token = token.decode('utf-8')
headers = {'Authorization': f'Bearer {s_token}'}
# send the get request
r = requests.get('https://api.appstoreconnect.apple.com/v1/salesReports',
headers=headers)#, params=params)
r.json() simply returns
{'errors': [{'status': '401',
'code': 'NOT_AUTHORIZED',
'title': 'Authentication credentials are missing or invalid.',
'detail': 'Provide a properly configured and signed bearer token, and make sure that it has not expired. Learn more about Generating Tokens for API Requests https://developer.apple.com/go/?id=api-generating-tokens'}]}
In addition the link in the error message appears to be broken as well.
I have tried reading in the .p8 file in both binary and the regular string representation. I have tried passing different values in the token, removing certain values etc. I also have tried not passing the payload parameters into the GET request which also results in a 401 error. The payload information is listed here. Any help appreciated.
exp cannot be a string...
import jwt
import requests
# pseudo, removed secret info
# read the file, currently binary but have tried string too
with open('AuthKey_4..._.p8', 'r+b') as keyfile:
secret = keyfile.read()
expir = round(time.time() + 20 * 60)
# sign the token with the iss, time, key, and kid with the correct alg
token = jwt.encode({'iss': '6...',
'exp': expir,
'aud': 'appstoreconnect-v1'},
secret, algorithm='ES256',
headers={'alg': 'ES256', 'kid': '4...', 'typ': 'JWT'})
# decode the bytes and create the get request header
s_token = token.decode('utf-8')
headers = {'Authorization': f'Bearer {s_token}'}
# send the get request
r = requests.get('https://api.appstoreconnect.apple.com/v1/salesReports',
headers=headers)#, params=params)

Python Requests HTTP POST response cookie missing / hiding fields

new to using Python requests lib, and looking for a little help around accessing cookies...
I am unable to get access to all of the fields within a cookie that I get from the following code using the Requests library - similar code from GoLang or Postman all work fine, but for some reason i am missing a few key fields that I need from Python-land. Sample code is as follows:
import requests
# Host base URL
host = 'sampleurl.link/endpoint'
# Username and password for login to API endpoint
credentials = "username=sausage123%40spaniel.org&password=Passw0rd"
ses = requests.Session()
authString = ''
def auth(host):
payload = credentials
headers = {
'Content-Type': "application/x-www-form-urlencoded",
}
ses.post("https://" + host + "/auth", data=payload, headers=headers)
cookies = ses.cookies.get_dict()
print(cookies.items())
authString = "; ".join([str(x)+"="+str(y) for x,y in cookies.items()])
print(authString)
auth(host)
The output is as follows:
[('systemid3', 'eSgRCbaH2EWyBVbRyfBS7xfftYCAqE-BRaon1Uc350pi14qTVgsmDXLrK9TDJvPsKmAzgw==')]
However, from the same API call in GoLang or Postman equiv. i get all of the required fields including path and expires:
systemid3=eSgRCbaH2EWyBVbRyfBS7xfftYCAqE-BRaon1Uc350pi14qTVgsmDXLrK9TDJvPsKmAzgw==; Path=/; Expires=Sat, 21 Sep 2019 09:37:46 GMT
Note that Postman also gives me the same 'Path' and 'Expires' fields etc.
How do i get Requests lib to give me access to all of the fields in the cookie, not just the first 2? Why is it hiding / removing the othe cookie fields?
Thanks in advance!
It's because you're using .items(), it only gets cookie name and value
You can access other parts like this:
for cookie in s.cookies:
print(cookie.name)
print(cookie.value)
print(cookie.expires)
print(cookie.path)

Using GET and POST with Authorization HTTP header in Python

I am trying to get the list of Maps created by me in Google Maps, and the Maps API says the following:
Retrieving a List of Maps
The Maps Data API provides a feed that lists the maps created by a particular user; this feed is known as a "metafeed". A typical Maps Data API metafeed is a GET request of the following form:
The default feed requests all maps associated with the authenticated user
GET http://maps.google.com/maps/feeds/maps/default/full
Authorization: GoogleLogin auth="authorization_token"
The standard metafeed requests all maps associated with the associated userID
GET http://maps.google.com/maps/feeds/maps/userID/full
Authorization: GoogleLogin auth="authorization_token"
Note that both GET requests require an Authorization HTTP header, passing an AuthSub or GoogleLogin token, depending on which authentication scheme you've implemented. (The GoogleLogin token corresponds to the ClientLogin authentication process.)
I have no idea how to create HTTP request with Authorization HTTP headers. I already have code to get the authorization_token, which is as follows:
# coding: utf-8
import urllib, re, getpass
# http://code.google.com/intl/pt-BR/apis/maps/documentation/mapsdata/developers_guide_protocol.html#ClientLogin
username = 'heltonbiker'
senha = getpass.getpass('Senha do usuário ' + username + ':')
dic = {
'accountType': 'GOOGLE',
'Email': (username + '#gmail.com'),
'Passwd': senha,
'service': 'local',
'source': 'helton-mapper-1'
}
url = 'https://www.google.com/accounts/ClientLogin?' + urllib.urlencode(dic)
output = urllib.urlopen(url).read()
authid = output.strip().split('\n')[-1].split('=')[-1]
I also took a look at httplib docs, but didn't understand much (I am not a professional programmer).
Any clue?
Using urllib2 will make everything easier:
import urllib2
request = urllib2.Request('http://maps.google.com/maps/feeds/maps/default/full')
request.add_header('Authorization', 'GoogleLogin auth=%s' % authorization_token)
urllib2.urlopen(request).read()
BTW, isn't the Google Maps Data API deprecated? http://googlegeodevelopers.blogspot.com/2010/11/maps-data-api-deprecation-announcement.html

Categories