This is what I have so far. No real success. Trying to retrieve a token, but nothing seems to work. Just returns a giant mess of characters.
import requests
import json
auth_url = "http://learn.ZZZZZZZ.com/oauth2/authorize"
#credential
auth_client_id = "BBBBBBBBBBBBBBBBBBBBBBBB"
auth_client_secret = "YYYYYYYYYYYYYYYYYYYYY"
payload={'grant_type':'client_credentials', 'client_id':auth_client_id,'client_secret':auth_client_secret}
headers={'Accept':'application/json', 'Content-Type':'application/x-www-form-urlencoded'}
response = requests.post(auth_url,headers=headers,data=payload)
response.text
Use the token endpoint to obtain an access token.
auth_url = "http://learn.ZZZZZZZ.com/oauth2/token"
Docebo API documentation
Related
Context:
I'm working on a side project to pull data from the Spotify API into a
Microsoft SQL Server database as part of a refreshing ETL job. I need
to use the "Authorization Code Flow" so I can authorize/authenticate
programmatically, so my table will populate each day.
I'm using the Python requests library for this, and I don't want to
make an Object Oriented Solution for this if possible (not my
preference).
Problem:
I'm having trouble getting the Access Token after authenticating.
Looking at similar issues, it's very similar to this one:
Spotify API Authorization Code Flow with Python
.
I'm not sure why I'm getting a Response 400 (Bad Request) from this.
Can someone please advise here?
Code:
# used to to encode byte string from CLIENT_ID : CLIENT_SECRET, then decode for Authentication Header
import base64
# used to make HTTP requests from Spotify API
import requests
# used to access the environment variables
import os
def request_user_authorization():
'''
HTTP GET request to gain access to data (Authorization Code Flow)
HTTP POST request to send the code and receive an Authorization Token (current issue)
https://developer.spotify.com/documentation/general/guides/authorization/code-flow/
'''
# URLs
AUTH_URL = 'https://accounts.spotify.com/authorize'
TOKEN_URL = 'https://accounts.spotify.com/api/token'
BASE_URL = 'https://api.spotify.com/v1'
SPOTIFY_URI = 'https://api.spotify.com/v1/me/player/recently-played'
# sensitive items
CLIENT_ID = os.environ.get('SPOTIFY_CLIENT_ID_ENV')
CLIENT_SECRET = os.environ.get('SPOTIFY_CLIENT_SECRET_ENV')
# make a request to the /authorize endpoint to get an authorization code
user_authorization_code = requests.get(
AUTH_URL, {
'client_id': CLIENT_ID,
'response_type': 'code',
'redirect_uri': SPOTIFY_URI,
'scope': 'user-read-recently-played',
}
)
# Code 200 = "OK"
print(user_authorization_code)
#----------------------------------------------------------#
api_header_string = base64.urlsafe_b64encode((CLIENT_ID + ':' + CLIENT_SECRET).encode('ascii'))
api_headers={
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic %s' % api_header_string.decode('ascii')
}
api_payload = {
'grant_type': 'authorization_code',
'code': user_authorization_code,
'redirect_uri': SPOTIFY_URI,
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET
}
#issue here:
# Make a request to the /token endpoint to get an access token
access_token_request = requests.post(url=TOKEN_URL, data=api_payload, headers=api_headers)
# returns <Response [400]>
# https://datatracker.ietf.org/doc/draft-ietf-httpbis-semantics/
# 15.5.1. 400 Bad Request
# The _400 (Bad Request)_ status code indicates that the server cannot
# or will not process the request due to something that is perceived to
# be a client error (e.g., malformed request syntax, invalid request
# message framing, or deceptive request routing).
# print(access_token_request)
#----------------------------------------------------------#
request_user_authorization()
You seem to have misunderstood how the Authorizatuon Code Flow works.
The redirect_uri in this kind of flow is used by the provider api (here spotify) as a callback to give you the authorization code.
The spotify API will call this url with a code parameter that you can use to ask for a token.
Meaning that for this flow to work you need a web server ready to receive requests on the uri that you have given in your code request (and specified when creating your app on the spotify developer portal). You might be better off using the Client Credentials Flow for your use case.
Also you should always use the name of the keywords arguments when using requests.get, requests.post ... It makes the code clearer and the order of the arguments differ for each method so it can get confusing if you don't.
#Speedlulu you're correct, that was the problem.
For anyone in the future reading this question: this is what I learned since posting the question:
What I misunderstood was the flow of data, and that Client Credentials Flow (Application to Spotify only) was the better choice because I don't need to have a "User" portion to this program.
Spotify's Client Credentials Flow Documentation: https://developer.spotify.com/documentation/general/guides/authorization/client-credentials/
# used to access environment variables securely (sensitive data)
import os
# used to encode strings into bytes and back
import base64
# used to convert JSON data into strings
import json
# endpoint that I'm connecting to on Spotify's servers
token_request_url = "https://accounts.spotify.com/api/token"
CLIENT_ID = os.environ.get('SPOTIFY_CLIENT_ID_ENV')
CLIENT_SECRET = os.environ.get('SPOTIFY_CLIENT_SECRET_ENV')
# encode credentials into bytes, then decode into a string for the HTTP POST request to Spotify to authenticate
BASE64_ENCODED_HEADER_STRING = base64.b64encode(bytes(f"{CLIENT_ID}:{CLIENT_SECRET}", "ISO-8859-1")).decode("ascii")
#initializing dictionaries for HTTP POST request
headers = {}
data = {}
headers['Authorization'] = f"Basic {BASE64_ENCODED_HEADER_STRING}"
data['grant_type'] = "client_credentials"
data['json'] = True
data['scope'] = 'user-read-recently-played'
r = requests.post(url=token_request_url, headers=headers, data=data)
# prints the response from the server regarding the access token data (formatted to be easier to read)
print(json.dumps(r.json(), indent=2))
# store the token value in a variable for HTTP GET request
token = r.json()['access_token']
What was unclear is that I first need to POST my request with the credentials to get the token (using the specific URL to do), store the r.json()['access_token'] value in a variable, then use that as part of the following GET request to access my specific data.
I would like to make a HTTP call to this resource :
https://bigquery.googleapis.com/bigquery/v2/projects/{projectId}/jobs
As I read to the documentation I use an API key generated from my GCP project to be authenticated. So with requests I make a simple call like this:
import requests
params = {'key': 'MY_API_KEY'}
base_url = 'https://bigquery.googleapis.com'
project_id = 'MY_PROJECT_ID'
r = requests.get(f'{base_url}/bigquery/v2/projects/{project_id}/jobs', params=params)
Unfortunately it returns a response 401 and I can't figure out why.
Thanks a lot and have a nice day !
Update code after guillaume blaquiere reply :
from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account
base_url = 'https://bigquery.googleapis.com'
project_id = 'project_id'
credentials = service_account.Credentials.from_service_account_file(
'service_account.json',
scopes=['https://www.googleapis.com/auth/bigquery',
'https://www.googleapis.com/auth/cloud-platform'],
)
authed_session = AuthorizedSession(credentials)
response = authed_session.request('GET', f'{base_url}/bigquery/v2/projects/{project_id}/jobs')
print(response.json())
# this returns : {'etag': 'tAZvk1k2f2GY8yHaQF7how==', 'kind': 'bigquery#jobList'}
The API Key no longer works for a large number of Google API. Only some legacy continue to accept an API key.
Now, you need an authenticated request. You can find exemple in the google-auth python library documentation. Look at Refresh and Authorized_session.
Don't hesitate to comment if you need help about the credential obtention, I can also help you on this.
EDIT
When you perform the request, it's, by default, only on the current user. In your case, it's the service account when you use the Python code, and your User account when you use the API Explorer (the swagger like in the Google Documentation).
In your case, I guess that your service account has never performed a job (query or load job) and thus, there is no entry for it.
According with the documentation, is you want to see all the user jobs, you have to add the param ?allUsers=true at the end of your URL
response = authed_session.request('GET', f'{base_url}/bigquery/v2/projects/{project_id}/jobs?allUsers=true')
I am trying to use the cloudant python library (http://cloudant-labs.github.io/cloudant-python/) in a Flask application but I can't see where you would use API keys to connect to your cloudant account
I tried this:
import cloudant
account = cloudant.Account('account_name')
login = account.login('public_key', 'private_key')
But it doesn't do anything
It looks like you posted this to github. I wanted others to be able to find the answer.
https://github.com/cloudant-labs/cloudant-python/issues/43:
import cloudant
account = cloudant.Account(USERNAME, auth=(API_KEY, API_SECRET))
not cloud ant but raw code
import requests
import json
#basic authentication
auth = ('account', 'private_key')
post_url = "https://account_name.cloudant.com/database".format(auth[0])
r = requests.put(post_url, auth=auth)
print json.dumps(r.json(), indent=1)
We're using Rauth to connect to various OAuth 1 APIs. It works fine for a single request, but trying to do 2 or more requests against the given session results in 401 not authorized errors from the APIs.
Twitter API example:
import requests
from rauth import OAuth1Service
from rauth import OAuth1Session
consumer_key = {the consumer key}
consumer_secret = {the consumer secret}
access_token = {the access token}
access_token_secret = {the access token secret}
oauth_service = OAuth1Service(consumer_key = consumer_key,
consumer_secret = consumer_secret)
oauth_session = oauth_service.get_session(token = (access_token, access_secret))
url = 'https://api.twitter.com/1.1/statuses/home_timeline.json'
params = {'include_rts': 'true'}
r = oauth_session.get(url, params=params) # THIS WORKS
r = oauth_session.get(url, params=params) # THIS RETURNS 401 ERROR
This happens on both Twitter and LinkedIn APIs. How do we execute multiple requests against a single OAuth1Session object?
VERSIONS:
rauth==0.5.4
requests==1.1.0
UPDATE:
Strangely, if the params argument is not included then multiple requests can be made- but once params are included, even if it is an empty dict, we get 401s.
Example 1:
r = oauth_session.get(url) # THIS WORKS
r = oauth_session.get(url) # THIS WORKS
Example 2:
r = oauth_session.get(url, params={}) # THIS WORKS
r = oauth_session.get(url, params={}) # THIS RETURNS 401 ERROR
Carrying over from the comments, using session.get(..., header_auth=True) should do the trick. It's hard to say exactly why it doesn't work without this, but for the record, header-based authentication is preferred by the spec and given Twitter's position, I wouldn't be surprised if they also prefer it as a provider.
A quick search reveals dozens upon dozens of reports of their API failing where it ostensibly should work and one remedy is to prefer header authentication. From what I can tell, rauth is signing appropriately, so perhaps this is something to do with the way the provider is showing preference and handling non-header authenticated requests.
Update
It looks like either rauth or Requests was not properly handling params. It's odd because the signature base string and oauth_signature seemed to be correct, in that they were appropriately different on each respective request and the data they operated on seemed to checkout. So it seems like it should have validated the request.
At any rate, to correct this, we need to deepcopy elements of the request parameters that are mutable types, e.g. dictionaries. I've got a patch that should correct this, so you should be able to use this without header_auth. However, header authentication is the preferred method so I would still recommend it.
I am trying to use python to consume some adwords soap API, I am able to get the auth token but when I try to make a get request I got the authenticationerror.login_cookie_required error. Any ideas?
from suds.client import Client
auth_data = {'accountType':'GOOGLE', 'Email':'xxx#xxx.com', 'Passwd':'xxxxxxxx', 'service':'adwords', 'source':'xxxxxxxxxx'}
auth_data = urllib.urlencode(auth_data)
auth_request = urllib2.Request('https://www.google.com/accounts/ClientLogin', auth_data)
auth_response = urllib2.urlopen(auth_request)
auth_response = auth_response.read()
split = auth_response.split('=')
auth_token = split[len(split)-1]
url = 'https://adwords-sandbox.google.com/api/adwords/cm/v201109/CampaignService?wsdl'
client = Client(url)
authToken = auth_token
developerToken = 'xxx#xxx.com++NZD'
userAgent = 'jameslin-python'
client.set_options(soapheaders=(authToken,developerToken,userAgent))
client.service.get()
Have you tried using the Python client library for the AdWords API?
http://code.google.com/p/google-api-ads-python/
authToken isn't a SOAP header. RequestHeader is the soap header and authToken is a member of that header. See http://code.google.com/apis/adwords/docs/headers.html and http://code.google.com/apis/adwords/docs/#soap for more details.
I also wish to point out that AdWords API official forum is http://groups.google.com/group/adwords-api, where we regularly answer questions on AdWords API. If you have any followup questions, feel free to ask on the official forum.
Cheers,
Anash