Python Forbidden API Request - python

I'm trying to use Python 3.10 to retrieve data from an API endpoint using OAUTH2. The authorization call is working correctly and I am receiving the token. However, when I try to retrieve the data I need, I'm getting a 403 message that states
{"code":-1,
"message":"Forbidden Request",
"allowedScopes":{"oauthSystem":{"scopes":["read"]},"oauthCode":{"scopes":["read"]}},
"requestScopes":[]}
My call works in Postman. I've added the scope to the call, but still receive the same message. What am I missing from the call? Here's my code:
# import client libraries
import json
import mysql.connector
import requests
import sys
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# create database connection
con = mysql.connector.connect(
host='servername',
user='username',
password='password',
database='db_name',
auth_plugin='mysql_native_password')
cursor = con.cursor()
# authentication stuff
def get_new_token():
auth_server_url = "https://URL_here"
client_id = "the client ID"
client_secret = "the client secret"
token_req_payload = {'grant_type': 'client_credentials'}
token_response = requests.post(auth_server_url,
data=token_req_payload, verify=False, allow_redirects=False,
auth=(client_id, client_secret))
if token_response.status_code !=200:
print("Failed to get token", file=sys.stderr)
sys.exit(1)
else:
print("Obtained Token")
tokens = json.loads(token_response.text)
return tokens['access_token']
# use the function above to get the token before calling the API
token = get_new_token()
print("The token is ", token)
# call the API using the token
api_headers = {'Authorization': 'Bearer ' + token, 'Host_name)': 'host','Scope': 'read', 'User-Agent': 'PostmanRuntime/7/30.0', 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive'}
api_response = requests.get('https://api_endpoint_url', headers=api_headers, verify=False)
if api_response.status_code == 401:
print("Failed to get token.")
token = get_new_token()
else:
print(api_response.text)
print(api_response.status_code)
I've tried adding the scope to the headers, but it still returns the same forbidden status. I tried Googling the error message with no results. The MySQL connection stuff is for additional functionality I will add later.

I solved this by adding the scope to the authentication request instead of the data request, so now the authentication request is:
def get_new_token():
auth_server_url = "https://URL_here"
client_id = "the client ID"
client_secret = "the client secret"
token_req_payload = {'grant_type': 'client_credentials', 'scope': 'read'}
token_response = requests.post(auth_server_url,
data=token_req_payload, verify=False, allow_redirects=False,
auth=(client_id, client_secret))
if token_response.status_code !=200:
print("Failed to get token", file=sys.stderr)
sys.exit(1)
else:
print("Obtained Token")
tokens = json.loads(token_response.text)
return tokens['access_token']
# use the function above to get the token before calling the API
token = get_new_token()
print("The token is ", token)

Related

Spotify Error 401 even though I, supposedly, have access_token (Python)

import requests
import base64
# Request an access token
def request_access_token():
AUTH_URL = 'https://accounts.spotify.com/api/token'
# POST
auth_response = requests.post(AUTH_URL, {
'grant_type': 'client_credentials',
'client_id': "<client ID>",
'client_secret': "<client Secret>",
})
# convert the response to JSON
auth_response_data = auth_response.json()
# save the access token
print(auth_response_data)
access_token = auth_response_data['access_token']
return access_token
# Check if the access token is valid
def check_access_token_validity(access_token):
headers = {
"Authorization": f"Bearer {access_token}"
}
response = requests.get("https://api.spotify.com/v1/me", headers=headers)
print(response)
if response.status_code == 200:
return True
else:
return False
# Run the code
access_token = request_access_token()
if access_token:
if check_access_token_validity(access_token):
print("Access token is valid.")
else:
print("Access token is not valid.")
else:
print("Could not request access token.")
I get a "access_token" but it doesn't work. It gives Error 401. I also got the same issue in javascript. I tried everything and I dont know what is wrong. Here's the output:
output
Which shows that I did recieve a token. But when I try to check it's validity, it is [Invalid 401]. I have also rotated my client secret and same issue. Both Client ID and Client Secret are correct.
As mentioned in the docs, your access token request is different
Try this
import requests
import base64
client_id = 'CLIENT_ID';
client_secret = 'CLIENT_SECRET';
response = requests.post(
'https://accounts.spotify.com/api/token',
data={'grant_type':'client_credentials'},
headers={'Authorization': 'Basic ' + base64.urlsafe_b64encode((CLIENT_ID + ':' + CLIENT_SECRET).encode())},
)

Python API KeyError

I'm new to both python and APIs. lol. But google isn't helping. I have the below simple authentication request and am getting a KeyError: 'token' from the line, authorization = {"Authorization": "Bearer " + token['token']}
I appreciate any guidance you can provide!
#!/usr/bin/python3
import requests
import argparse
import math
import os
import json
export_format_map = {
"csv": "zip",
"json": "json",
"kml": "kml"
}
def download_logs(args):
"""
NOTE: Requires user credentials
Downloads log files from user-specified host in user-specified file format.
"""
rest_api = "https://address.here/api/1.0"
# Step 1. Login and get token to authenticate requests
print(f"Logging into {rest_api}...")
# Get username and password from environment variables
username = os.environ.get('USERNAME')
password = os.environ.get('PASSWORD')
# Put the credentials into a dictionary object
credentials = {"email": username, "password": password}
r = requests.post(f"{rest_api}/authentication/login", json=credentials)
# Build the JWT compliant authorization dictionary
token = json.loads(r.text)
authorization = {"Authorization": "Bearer " + token['token']}
# Step 2. Gather a list of contacts filtered to be within the min/max timestamps
def get_agents(params):
"""Sends GET request to API with provided parameters; Returns response."""
response = requests.get(
url=f"{rest_api}/agent",
params=params,
headers=authorization
).json()
return response
#code continues.....
I have also tried setting up the token request like this with the same error:
TOKEN = requests.post(
url=f"{rest_api}/authentication/login",
data={
"email": input("\nusername/email: "),
"password": getpass.getpass(),
}
).json()
authorization = {"Authorization": "Bearer " + TOKEN['token']}

Spotify API Authorization Code Flow with Python

I am trying to do the authorization code flow using Spotify's API to ultimately add songs to a playlist. I am building this from scratch, and not using any libraries such as Spotipy.
I am able to successfully hit the authorize endpoint, but I am having some issues with the token endpoint. Here is the code I have so far:
# URLS
AUTH_URL = 'https://accounts.spotify.com/authorize'
TOKEN_URL = 'https://accounts.spotify.com/api/token'
BASE_URL = 'https://api.spotify.com/v1/'
# Make a request to the /authorize endpoint to get an authorization code
auth_code = requests.get(AUTH_URL, {
'client_id': CLIENT_ID,
'response_type': 'code',
'redirect_uri': 'https://open.spotify.com/collection/playlists',
'scope': 'playlist-modify-private',
})
print(auth_code)
auth_header = base64.urlsafe_b64encode((CLIENT_ID + ':' + CLIENT_SECRET).encode('ascii'))
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic %s' % auth_header.decode('ascii')
}
payload = {
'grant_type': 'authorization_code',
'code': auth_code,
'redirect_uri': 'https://open.spotify.com/collection/playlists',
#'client_id': CLIENT_ID,
#'client_secret': CLIENT_SECRET,
}
# Make a request to the /token endpoint to get an access token
access_token_request = requests.post(url=TOKEN_URL, data=payload, headers=headers)
# convert the response to JSON
access_token_response_data = access_token_request.json()
print(access_token_response_data)
# save the access token
access_token = access_token_response_data['access_token']
When I run my script, I get this output in Terminal:
{'error': 'invalid_grant', 'error_description': 'Invalid authorization code'}
Traceback (most recent call last):
File "auth.py", line 48, in <module>
access_token = access_token_response_data['access_token']
KeyError: 'access_token'```
Can anyone explain to me what I might be doing wrong here?
By now, you probably have figure out what the problem was, but I am answering for future readers as well.
When you encode the auth_header, you should encode it to bytes not ascii. The str.encode() does this by default, so you can call it without arguments. Your code changes to this:
auth_header = base64.urlsafe_b64encode((CLIENT_ID + ':' + CLIENT_SECRET).encode()).
Now you must have a working example.
The code below worked for me to create a playlist and add a song.
The prerequisite is that you have a client_id and client_secret.
Run the code below and browse to your base URL (in my case, that was localhost), then you will see an authorize hyperlink that forwards you to the spotify authorization page.
Note that the redirect_url needs to be whitelisted in the spotify developer dashboard.
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
import requests
import uvicorn
# client_id = "YOUR_CLIENT_ID"
# client_secret = "YOUR_CLIENT_SECRET"
# redirect_uri = "YOUR_REDIRECT_URI" # e.g. http://localhost:8000/callback/ --> you will have to whitelist this url in the spotify developer dashboard
app = FastAPI()
def get_access_token(auth_code: str):
response = requests.post(
"https://accounts.spotify.com/api/token",
data={
"grant_type": "authorization_code",
"code": auth_code,
"redirect_uri": redirect_uri,
},
auth=(client_id, client_secret),
)
access_token = response.json()["access_token"]
return {"Authorization": "Bearer " + access_token}
#app.get("/")
async def auth():
scope = ["playlist-modify-private", "playlist-modify-public"]
auth_url = f"https://accounts.spotify.com/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri}&scope={' '.join(scope)}"
return HTMLResponse(content=f'Authorize')
#app.get("/callback")
async def callback(code):
headers = get_access_token(code)
response = requests.get("https://api.spotify.com/v1/me", headers=headers)
user_id = response.json()["id"]
name = "Name of your playlist"
description = "Description of your playlist"
params = {
"name": name,
"description": description,
"public": True,
}
url = f"https://api.spotify.com/v1/users/{user_id}/playlists"
response = requests.post(url=url, headers=headers, json=params)
playlist_id = response.json()["id"]
track_uri = "spotify:track:319eU2WvplIHzjvogpnNc6"
response = requests.post(
f"https://api.spotify.com/v1/playlists/{playlist_id}/tracks",
headers=headers,
json={"uris": [track_uri]},
)
if response.status_code == 201:
return {"message": "Track added successfully!"}
else:
return {"error": response.json()}
if __name__ == "__main__":
uvicorn.run(app, debug=True)
You're missing the CLIENT_ID and CLIENT_SECRET set within the code if I'm not mistaken.
This means Spotify will return invalid access token, which results in you not being able to continue.
You can also use Spotipy library for Python to make things easier.
https://spotipy.readthedocs.io/en/2.16.1/

Got invalid client secret, missing parameters client_secret, client_assertion when trying to get token in MS Azure

My goal is to get token then querying graph resources.
I wrote these codes to get token from my app. First code, acquire token with login/pass:
from adal import AuthenticationContext
auth_context = AuthenticationContext('https://login.microsoftonline.com/g***************r.onmicrosoft.com')
token = auth_context.acquire_token_with_username_password('https://graph.microsoft.com',
't***************n.fr',
'***************', 'de8bc8b5-***************-b748da725064')
and got these :
adal.adal_error.AdalError: Get Token request returned http error: 401 and server response: {"error":"invalid_client","error_description":"AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'.\r\nTrace ID: e9ffa752-a258-4e32-9164-156e50912a00\r\nCorrelation ID: afb3c52f-8f36-4904-9926-90ba2aaba0ab\r\nTimestamp: 2020-12-08 14:02:58Z","error_codes":[7000218],"timestamp":"2020-12-08 14:02:58Z","trace_id":"e9ffa752-a258-4e32-9164-156e50912a00","correlation_id":"afb3c52f-8f36-4904-9926-90ba2aaba0ab","error_uri":"https://login.microsoftonline.com/error?code=7000218"}
Second code, acquire token with client credentials:
from adal import AuthenticationContext
auth_context = AuthenticationContext('https://login.microsoftonline.com/g*************r.onmicrosoft.com')
token = auth_context.acquire_token_with_client_credentials('https://graph.microsoft.com',
'de8bc8b5-************-b748da725064',
'****************************')
And got these:
adal.adal_error.AdalError: Get Token request returned http error: 401 and server response: {"error":"invalid_client","error_description":"AADSTS7000215: Invalid client secret is provided.\r\nTrace ID: 79fe51c5-2987-4855-acf5-88fee0592a00\r\nCorrelation ID: cdfc9215-b7a6-4ceb-9d90-2de371c3178c\r\nTimestamp: 2020-12-08 14:21:13Z","error_codes":[7000215],"timestamp":"2020-12-08 14:21:13Z","trace_id":"79fe51c5-2987-4855-acf5-88fee0592a00","correlation_id":"cdfc9215-b7a6-4ceb-9d90-2de371c3178c","error_uri":"https://login.microsoftonline.com/error?code=7000215"}
I don't know if the client secret is wrong or something I did?
I didn't need those libs.
Lib requests was enough.
def authenticate_oauth():
validation_url = 'https://login.microsoftonline.com/******************.com/oauth2/v2.0/token'
client_id = '1a******************************b7'
client_secret = '-*****************************r'
grant_type = 'client_credentials'
scope = 'https://graph.microsoft.com/.default'
params = {
'client_id': client_id,
'client_secret': client_secret,
'grant_type': grant_type,
'scope': scope,
}
response = requests.post(validation_url, data=params)
return json.loads(response.content)
if __name__ == '__main__':
token = authenticate_oauth()
if 'access_token' in token:
graph_data = requests.get(
'https://graph.microsoft.com/v1.0/users',
headers={'Authorization': 'Bearer ' + token['access_token']})
data_users = json.loads(graph_data.content)
for data in data_users['value']:
print(data)

Retrieve Google Sites's Domain Index feed using OAuth 2.0 with Service Account

I need to start crawling this url: https://sites.google.com/a/domain.com/sites/system/app/pages/meta/domainIndex using python script.
How to authorize this Google Site URL using OAuth2.0 with Service Account.
In the case of OAuth1.0, We have send the request to https://www.google.com/accounts/ClientLogin and extract the token which received as a token and authorized the url.
OAuth 1.0 Authenication
url = 'https://www.google.com/accounts/ClientLogin'
request = urllib.urlencode({
'accountType': 'HOSTED',
'Email': 'admin#dmian.com',
'Passwd': 'userPassword',
'service': 'jotspot'})
#.. Fetch the url: https://www.google.com/accounts/ClientLogin and extract the token
headers = {
'Authorization': 'GoogleLogin auth=' + token,
'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15 ( .NET CLR 3.5.30729)',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Host': 'sites.google.com',
'Connection': 'keep-alive'
}
# .. Fetch the Google Site url with below headers
Some time ago I wrote a class for myself to handle OAuth2 authentication with Google APIs. It might serve as an example for you. And, yes, you need to register an "application", but that is just to get the client id/secret.
Some notes:
The class uses the 'offline' access type to obtain credentials that can be stored and re-used in the future.
The settings variable holds an instance of a class storing all my settings, in this case, the previously obtained google api credentials.
GAuthDialog is a dialog that presents the user with user/password login and reads the code google produces.
The execute method wraps any method that requires access to the API and authentication.
The class can be used as follows, e.g. for google drive:
self._gapi = GApi(settings, 'https://www.googleapis.com/auth/drive.readonly', 'drive', 'v2')
self._gapi.execute(self.get_file_list)
and then we have:
def get_file_list(self):
query="query"
children = self._gapi.service.files().list(q=query).execute()
And here comes the code of the class:
from oauth2client.client import OAuth2WebServerFlow, Credentials, AccessTokenRefreshError
from apiclient.discovery import build
from googleapiclient.errors import HttpError
import httplib2
class GApi():
class CredentialsError(Exception):
pass
# App credentials from developers console
__client_id = ''
__client_secret = ''
# Redirect URI for installed (non-web) apps
__redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
def __init__(self, settings, scopes, service_name, version):
self.__settings = settings
self.__scopes = scopes
self.__service_name = service_name
self.__version = version
self.__service = None
self.__credentials = None
# Try restoring credentials from settings
if self.__settings.get_gapi_credentials(self.__service_name):
self.__credentials = Credentials.new_from_json(
self.__settings.get_gapi_credentials(self.__service_name))
#property
def service(self):
return self.__service
def execute(self, method, *args, **kwargs):
self.__setup()
try:
return method(*args, **kwargs)
except AccessTokenRefreshError:
pass # Will re-authenticate below
except HttpError as err:
# Rethrow since HttpError has a bug in str()
raise Exception("Response: %s, Content: %s" %
(str(err.resp), str(err.content)))
# Try re-authenticating
self.__reauthenticate()
try:
return method(**kwargs)
except HttpError as err:
# Rethrow since HttpError has a bug in str()
raise Exception("Response: %s, Content: %s" %
(str(err.resp), str(err.content)))
def __obtain_credentials(self):
# Initialize the flow
flow = OAuth2WebServerFlow(self.__client_id, self.__client_secret,
self.__scopes, redirect_uri=self.__redirect_uri)
flow.params['access_type'] = 'offline'
# Run through the OAuth flow and retrieve credentials
uri = flow.step1_get_authorize_url()
# Get code from dialog
dialog = GAuthDialog(uri)
if dialog.exec() == QtWidgets.QDialog.Accepted and dialog.auth_code:
# Get the new credentials
self.__credentials = flow.step2_exchange(dialog.auth_code)
# Set them in settings
self.__settings.set_gapi_credentials(
self.__service_name, self.__credentials.to_json())
else:
self.__credentials = None
self.__settings.set_gapi_credentials(self.__service_name, None)
def __reauthenticate(self):
self.__credentials = None
self.__service = None
self.__setup()
def __setup(self):
# Do we have credentials?
if not self.__credentials:
self.__obtain_credentials()
# Check if we got credentials
if self.__credentials:
# Do we have service?
if not self.__service:
# Create an httplib2.Http object and authorize it with our credentials
http = httplib2.Http()
http = self.__credentials.authorize(http)
self.__service = build(self.__service_name,
self.__version, http=http)
else:
raise GApi.CredentialsError

Categories