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']}
Related
I'm working on a simple python script to help me retrieve email in office365 user mailbox based on the following parameters, sentdatetime, sender or from address and subject.
As of current, am able to get the access token using msal, however the email api call does not work. I get an error 401. From graph explorer the query works however in the script it's not working.
My app registration is assigned application permission for mail, i selected everything under mail permissions. see below permissions
Below is my script so far, what am i doing wrong.
import msal
import json
import requests
def get_access_token():
tenantID = '9a13fbbcb90fa2'
authority = 'https://login.microsoftonline.com/' + tenantID
clientID = 'xxx'
clientSecret = 'yyy'
scope = ['https://outlook.office365.com/.default']
app = msal.ConfidentialClientApplication(clientID, authority=authority, client_credential = clientSecret)
access_token = app.acquire_token_for_client(scopes=scope)
return access_token
# token block
access_token = get_access_token()
token = access_token['access_token']
# Set the parameters for the email search
date_sent = "2023-01-22T21:13:24Z"
mail_subject = "Test Mail"
sender = "bernardberbell#gmail.com"
mailuser = "bernardmwanza#bernardcomms.onmicrosoft.com"
# Construct the URL for the Microsoft Graph API
url = "https://graph.microsoft.com/v1.0/users/{}/mailFolders/Inbox/Messages?$select=id,sentDateTime,subject,from&$filter=contains(subject, '{}') and from/emailAddress/address eq '{}' and SentDateTime gt '{}'".format(mailuser, mail_subject, sender, date_sent)
# Set the headers for the API call
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
# Send the API request and get the response
response = requests.get(url, headers=headers)
print(response)
# # Parse the response as JSON
# data = json.loads(response.text)
# print(data)
Below is the error
Your scope is wrong for the Graph API this
scope = ['https://outlook.office365.com/.default']
Will give you a token that has an audience of outlook.office365.com which is okay for IMAP4 but not for the Graph which requires the audience to be https://graph.microsoft.com
so your scope for the graph should be
scope = ['https://graph.microsoft.com/.default']
You can check your token use jwt.io and verify it.
Am trying to make a program that finds the current song playing and then to put it an one of my playlists. But am getting a 404 response when a trying to obtain the current playing song.
This is how I am getting my access token:
def get_token():
auth_str = client_id + ":" + client_secret
auth_bytes = auth_str.encode("utf-8")
auth_base64 = str(base64.b64encode(auth_bytes), "utf-8")
url = "https://accounts.spotify.com/api/token"
headers = {
"Authorization": "Basic " + auth_base64,
"Content-Type": "application/x-www-form-urlencoded"
}
data = {"grant_type": "client_credentials"}
result = post(url, headers=headers, data=data)
json_result = json.loads(result.content)
token = json_result["access_token"]
return token
This is how I am creating the headers:
def get_headers(token):
return {"Authorization": "Bearer " + token}
This is how I am trying to get the current playing song uri:
def get_current_track_and_post_in_playlist(token):
headers = get_headers(token)
result = get('https://api.spotify.com/v1/me/player/currently-playing', headers=headers)
playlist_id = "0p4d0akcszc87kcf0pym6qws1"
print(result)
json_result = json.loads(result.content)["item"]["uri"]
print(json_result)
playlist_put = post(f'https://api.spotify.com/v1/playlists/{playlist_id}/tracks?uris={json_result}', headers=headers)
When am printing the result in get_current_track_and_post_in_playlist function i get a <Response [404]>. Can someone help me resolve this problem?
A reasons is required a user authentication for those two REST APIs.
Get Currently Playing Track
https://api.spotify.com/v1/me/player/currently-playing
Add Items to Playlist
https://api.spotify.com/v1/playlists/{playlist_id}/tracks
It needs to get token by Authorization Code Flow
So I will demo by local server with Flask
This overall steps
# Step 1
I play a song by browser. it will add into my playlist
# Step 2
Running local redirect server with this code with add-song.py file name.
You needs to config the Redirect URIs of your application in your dashboard.
https://developer.spotify.com/dashboard/applications
from flask import Flask, request, redirect
from requests_oauthlib import OAuth2Session
from requests.auth import HTTPBasicAuth
import requests
import json
app = Flask(__name__)
AUTH_URL = 'https://accounts.spotify.com/authorize'
TOKEN_URL = 'https://accounts.spotify.com/api/token'
REDIRECT_URI = 'http://localhost:3000/callback' # your redirect URI
CLIENT_ID = "<your client ID>"
CLIENT_SECRET = "<your client Secret>"
SCOPE = [
"user-read-playback-state",
"app-remote-control",
"user-modify-playback-state",
"playlist-read-private",
"playlist-read-collaborative",
"user-read-currently-playing",
"user-read-playback-position",
"user-library-modify",
"playlist-modify-private",
"playlist-modify-public",
"user-read-recently-played",
"user-read-private",
"user-library-read"
]
def get_headers(token):
return {"Authorization": "Bearer " + token}
#app.route("/login")
def login():
spotify = OAuth2Session(CLIENT_ID, scope=SCOPE, redirect_uri=REDIRECT_URI)
authorization_url, state = spotify.authorization_url(AUTH_URL)
return redirect(authorization_url)
# your redirect URI's path
#app.route("/callback", methods=['GET'])
def callback():
code = request.args.get('code')
res = requests.post(TOKEN_URL,
auth=HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET),
data={
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': REDIRECT_URI
})
access_token = res.json()['access_token']
listObj = []
listObj.append(res.json())
# get current playing
headers = get_headers(access_token)
result1 = requests.get(url='https://api.spotify.com/v1/me/player/currently-playing', headers=headers)
current_song = result1.json()
listObj.append(current_song)
# Add new song into playlist with current playing song
playlist="<your playlist ID>"
url = "https://api.spotify.com/v1/playlists/{0}/tracks".format(playlist)
# current_song['item']['uri'] = 'spotify:track:xxxxxxxxxxxxxxxx'
params = {'uris': current_song['item']['uri']}
result2 = requests.post(url,
params=params,
headers={'Content-Type':'application/json',
'Authorization': 'Bearer {0}'.format(access_token)})
added_song = result2.json()
listObj.append(added_song)
return listObj
if __name__ == '__main__':
app.run(port=3000,debug=True) # your redirect URI's port
$ python add-song.py
#3 Login by Browser
http://localhost:3000/login
login your Spotify credential
Before add Song
After added Song
In the browser, will display current play song information and added song's snapshot_id.
The key is playing song's track URI needs to set a parameter of add playlist.
current_song['item']['uri']
#Step 4~6
It is running by inside code.
I hope to this demo your are looking for solution.
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())},
)
The account that is running cloudrun has been linked to the billing account, but still the cloud billing python apis throw errors. What else needs to be done in the service account ?
#1. Created json token for service account and passed into the access_bills() method
#2. The service account has role for Billing Access View
#3. Copied these methods as advised in comments from John Hanley's blog:
def load_private_key(json_cred):
''' Return the private key from the json credentials '''
return json_cred['private_key']
def create_signed_jwt(pkey, pkey_id, email, scope):
'''
Create a Signed JWT from a service account Json credentials file
This Signed JWT will later be exchanged for an Access Token
'''
# Google Endpoint for creating OAuth 2.0 Access Tokens from Signed-JWT
auth_url = "https://www.googleapis.com/oauth2/v4/token"
expires_in = 3600
issued = int(time.time())
expires = issued + expires_in # expires_in is in seconds
# Note: this token expires and cannot be refreshed. The token must be recreated
# JWT Headers
additional_headers = {
'kid': pkey_id,
"alg": "RS256", # Google uses SHA256withRSA
"typ": "JWT"
}
# JWT Payload
payload = {
"iss": email, # Issuer claim
"sub": email, # Issuer claim
"aud": auth_url, # Audience claim
"iat": issued, # Issued At claim
"exp": expires, # Expire time
"scope": scope # Permissions
}
# Encode the headers and payload and sign creating a Signed JWT (JWS)
sig = jwt.encode(payload, pkey, algorithm="RS256", headers=additional_headers)
return sig
def exchangeJwtForAccessToken(signed_jwt):
'''
This function takes a Signed JWT and exchanges it for a Google OAuth Access Token
'''
auth_url = "https://www.googleapis.com/oauth2/v4/token"
params = {
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": signed_jwt
}
r = requests.post(auth_url, data=params)
if r.ok:
return(r.json()['access_token'], '')
return None, r.text
def access_bills(sa_json):
cred = json.loads(sa_json)
private_key = load_private_key(cred)
# scopes = "https://www.googleapis.com/auth/cloud-platform" # this does not work, gets 404
scopes = "https://www.googleapis.com/auth/cloud-billing.readonly"
s_jwt = create_signed_jwt(
private_key,
cred['private_key_id'],
cred['client_email'],
scopes)
token, err = exchangeJwtForAccessToken(s_jwt)
if token is None:
logger.error("Error: {}".format(err))
exit(1)
logger.info("Token response: {}".format(token))
# the token is obtained and prints in the log
headers = {
"Host": "www.googleapis.com",
"Authorization": "Bearer " + token,
"Content-Type": "application/json"
}
try:
url = "https://cloudbilling.googleapis.com/v1/billingAccounts/01C8DC-336472-E177E1" # account name is "Billing Core"
response = requests.get(url=url, headers=headers)
logger.info("Response: {}".format(response))
# logs -> app - INFO - Response: <Response [404]>
return {
'statusCode': 200,
'body': 'Success'
}
except Exception as e:
logger.error("Error")
raise e
It gives 404 error as shown in the comment log after trying on that url.
Okay I found that the only way it works as of now is via big query export from billing account, sort out dataViewer permission and run the corresponding sql from python application, it works.
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/