I made a Python program that transfers music from YouTube playlists to your Spotify account and I wanted to turn it into a web app with a simple UI so that people could log into their yt/spotify accounts without having to run the program from the command line. I used Flask to make the interface, and have it working when I manually input the login info in my YouTubetoSpotify file, and now I am working on implementing Oauth2 on top of the UI so that when a user clicks one of the login buttons, it'll direct them to the corresponding authentication page (right now I'm just working on the Spotify login).
After some debugging, it seems that the client id and secret are properly obtained, but when they are used to get the token (in the getToken function in flask_spotify_auth), the post request returns an error. Here is the full error message, as well as all of my files.
{'error': 'invalid_client'}
[2020-07-08 14:09:13,121] ERROR in app: Exception on /callback/ [GET]
Traceback (most recent call last):
File "C:\Users\jared\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\jared\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\jared\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\jared\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\_compat.py", line 35, in reraise
raise value
File "C:\Users\jared\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\jared\AppData\Local\Programs\Python\Python37\lib\site-packages\flask\app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:/Users/jared/codingProjects/python_projects/Mixify/app.py", line 30, in upload
startup.getUserToken(request.args['code'])
File "C:\Users\jared\codingProjects\python_projects\Mixify\startup.py", line 29, in getUserToken
TOKEN_DATA = getToken(code, CLIENT_ID, CLIENT_SECRET, "{}:{}/callback/".format(CALLBACK_URL, PORT))
File "C:\Users\jared\codingProjects\python_projects\Mixify\flask_spotify_auth.py", line 33, in getToken
return handleToken(json.loads(post.text))
File "C:\Users\jared\codingProjects\python_projects\Mixify\flask_spotify_auth.py", line 38, in handleToken
auth_head = {"Authorization": "Bearer {}".format(response["access_token"])}
KeyError: 'access_token'
127.0.0.1 - - [08/Jul/2020 14:09:13] "GET /callback/?code=AQA9preCSj1r87JMxXt5XUBwC6LT-ZQM_By2NYTcdhi7wgUIwQKO6Q5Cm7ChcKdJ_Rd3IkCxdTKw7RlI4EYZNIZrp9RpZfOd1rXeE8v_2tY3A0Q-RKqP21jmRHMFOdTV4z1Lj6lQ1WXcOmbL5eP614ps8TfDPOzBwOfHMvILr-y3HsB30RlYJ6X2XQLUb5p3347YIsx4lfcja9unWa1SjyXw7pbYdOA HTTP/1.1" 500 -
app.py
# Jared Tewodros
# jmt5rg
from flask import *
import YoutubeToSpotify
import startup
app = Flask(__name__)
# @app.errorhandler(404)
# def page_not_found(e):
# return render_template('404.html'), 404
# home page
# @app.route("/")
# def upload():
# return render_template("file_upload.html")
@app.route("/")
def index():
response = startup.getUser()
return redirect(response)
@app.route('/callback/', methods=["GET", "POST"])
def upload():
startup.getUserToken(request.args['code'])
return render_template("file_upload.html")
# and that's all folks
@app.route("/convert", methods=["POST"])
def success():
global yt_list
global sp_list
global desc
yt_list = request.form['ytPL']
sp_list = request.form['spPL']
desc = request.form['desc']
cp = YoutubeToSpotify.YouTubePlaylist()
cp.add_song_to_playlist(yt_list, sp_list, desc)
return render_template("success.html")
if __name__ == "__main__":
app.run()
startup.py
# Jared Tewodros
# jmt5rg
from flask_spotify_auth import getAuth, refreshAuth, getToken
# Add your client ID
CLIENT_ID = "53754c6314ec4175b8dc3d23c864cb5a"
# aDD YOUR CLIENT SECRET FROM SPOTIFY
CLIENT_SECRET = "6677e13f615b4cfda88371b233b9ae08"
# Port and callback url can be changed or ledt to localhost:5000
PORT = "5000"
CALLBACK_URL = "http://localhost"
# Add needed scope from spotify user
# SCOPE = "streaming user-read-birthdate user-read-email user-read-private"
SCOPE = "user-read-email user-read-private"
# token_data will hold authentication header with access code, the allowed scopes, and the refresh countdown
TOKEN_DATA = []
def getUser():
return getAuth(CLIENT_ID, "{}:{}/callback/".format(CALLBACK_URL, PORT), SCOPE)
def getUserToken(code):
global TOKEN_DATA
TOKEN_DATA = getToken(code, CLIENT_ID, CLIENT_SECRET, "{}:{}/callback/".format(CALLBACK_URL, PORT))
def refreshToken(time):
time.sleep(time)
TOKEN_DATA = refreshAuth()
def getAccessToken():
return TOKEN
flask_spotify_auth.py
# Jared Tewodros
# jmt5rg
import base64, json, requests
SPOTIFY_URL_AUTH = 'https://accounts.spotify.com/authorize/?'
SPOTIFY_URL_TOKEN = 'https://accounts.spotify.com/api/token/'
RESPONSE_TYPE = 'code'
HEADER = 'application/x-www-form-urlencoded'
REFRESH_TOKEN = ''
def getAuth(client_id, redirect_uri, scope):
data = "{}client_id={}&response_type=code&redirect_uri={}&scope={}".format(SPOTIFY_URL_AUTH, client_id,
redirect_uri, scope)
return data
def getToken(code, client_id, client_secret, redirect_uri):
body = {
"grant_type": 'authorization_code',
"code": code,
"redirect_uri": redirect_uri,
"client_id": client_id,
"client_secret": client_secret
}
iden = "{}:{}".format(client_id, client_secret)
iden_bytes = iden.encode('ascii')
encoded = base64.b64encode(iden_bytes)
headers = {"Content-Type": HEADER, "Authorization": "Basic {}".format(encoded)}
post = requests.post(SPOTIFY_URL_TOKEN, params=body, headers=headers)
return handleToken(json.loads(post.text))
def handleToken(response):
print(response)
auth_head = {"Authorization": "Bearer {}".format(response["access_token"])}
REFRESH_TOKEN = response["refresh_token"]
return [response["access_token"], auth_head, response["scope"], response["expires_in"]]
def refreshAuth():
body = {
"grant_type": "refresh_token",
"refresh_token": REFRESH_TOKEN
}
post_refresh = requests.post(SPOTIFY_URL_TOKEN, data=body, headers=HEADER)
p_back = json.dumps(post_refresh.text)
return handleToken(p_back)