I started programming a few months ago and Im starting my first “big” project using python and I’m having trouble handling the info when a user logs in my web app. Even when a different user logs in, it always show my Spotify username and playlist infos. For example, if a user called “James” logs in it shows his profile when he is in the auth_url but when it actually access my webpage “getPlaylists”, all the info shown is mine. The Google/YouTube Oauth part is working perfectly, but Spotify Oauth is not.
I added the test users in the developer dashboard of Spotify but it didnt solve the problem. Any tips on what should be? Am I not handling the session correctly?
I dont know if I should paste all the code here, but most of it is what follows:
import spotipy
from spotipy.oauth2 import SpotifyOAuth
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow, Flow
from google.auth.transport.requests import Request
import google.oauth2.credentials
import json
import os
import os.path
from flask import Flask, request, url_for, session, redirect, render_template
from flask_session import Session
import clientinfo # File containing all the info necessary to login the web APP to the Spotify and youtube APIs
app = Flask(__name__)
if __name__ == "__main__":
app.run(debug=True)
app.secret_key = os.environ['FLASK_S_KEY']
app.config['SESSION_COOKIE_NAME'] = 'Spotify Cookie'
# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
TOKEN_INFO = "token_info"
@app.after_request
def after_request(response):
"""Ensure responses aren't cached"""
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
def create_spotify_oauth():
return SpotifyOAuth(
client_id = os.environ['SPOTIFY_CLIENT_ID'],
client_secret = os.environ['SPOTIFY_CLIENT_SECRET'],
redirect_uri = url_for('redirectSite', _external=True),
scope = "user-read-private playlist-read-private playlist-read-collaborative",
show_dialog=True
)
def get_token():
token_info = session.get(TOKEN_INFO, None)
if not token_info:
raise Exception("Token not found in session")
sp_oauth = create_spotify_oauth()
if sp_oauth.is_token_expired(token_info):
token_info = sp_oauth.refresh_access_token(token_info['refresh_token'])
session[TOKEN_INFO] = token_info
print(f"Token info: {token_info}") # Debug
return token_info
def credentialscheck():
# Disable OAuthlib's HTTPS verification when running locally.
# *DO NOT* leave this option enabled in production.
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
json_credentials = session.get('credentials')
if 'credentials' in session:
dict_credentials = json.loads(json_credentials)
credentials = google.oauth2.credentials.Credentials.from_authorized_user_info(dict_credentials)
if credentials.expired:
credentials.refresh(Request())
else:
return redirect(url_for('redirectYT'))
# Endpoint Index
@app.route('/', methods=["GET"])
def index():
if request.method == "GET":
return render_template("index.html")
# Log in user to the web app using Spotify OAuth
@app.route("/login")
def login():
# Forget any user_id
session.clear()
sp_oauth = create_spotify_oauth()
auth_url = sp_oauth.get_authorize_url()
return redirect(auth_url)
@app.route("/logout")
def logout():
session.clear()
return redirect(url_for("index", _external=True))
# After login, redirect user to the page with the correct authorization token
@app.route("/redirect")
def redirectSite():
sp_oauth = create_spotify_oauth()
code = request.args.get("code")
token_info = sp_oauth.get_access_token(code)
session[TOKEN_INFO] = token_info
return redirect(url_for("redirectYT", _external=True))
@app.route("/redirectYT")
def redirectYT():
# Disable OAuthlib's HTTPS verification when running locally.
# *DO NOT* leave this option enabled in production.
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
# Create OAuth flow object
flow = Flow.from_client_secrets_file(
'client_secrets.json',
scopes=["https://www.googleapis.com/auth/youtube.force-ssl"])
flow.redirect_uri = url_for('callback', _external=True)
authorization_url, state = flow.authorization_url(
access_type='offline',
prompt='select_account')
# Save the state so we can verify the request later
session['state'] = state
return redirect(authorization_url)
@app.route('/callback')
def callback():
# Verify the request state
if request.args.get('state') != session['state']:
raise Exception('Invalid state')
# Create the OAUth flow object
flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets.json',
scopes=["https://www.googleapis.com/auth/youtube.force-ssl"],
state=session['state'])
flow.redirect_uri = url_for('callback', _external=True)
# Exchange the authorization code for an access token
authorization_response = request.url
flow.fetch_token(authorization_response=authorization_response)
# Save credentials to the session
credentials = flow.credentials
session['credentials'] = credentials.to_json()
return redirect(url_for('getPlaylists'))
@app.route('/getPlaylists')
def getPlaylists():
try:
token_info = get_token()
except Exception as e:
print(f"Exception: {e}")
return redirect(url_for("login", _external=True))
sp = spotipy.Spotify(auth=token_info['access_token'])
credentialscheck()
user = sp.current_user()
username = user['display_name']
print(f"User: {username}") # Debug
# Getting all playlists (since the current_user_playlists max limit is 50, we need a 'for' loop)
allPlaylists = []
i = 0
while True:
fiftyplaylists = sp.current_user_playlists(limit=50, offset=i * 50)['items']
i += 1
allPlaylists += fiftyplaylists
if (len(fiftyplaylists)< 50):
break
# Filtering the data we actually need in each playlist and sorting them alphabetically)
playlists = [{'name': playlist['name'], 'id': playlist['id']} for playlist in allPlaylists]
playlists.sort(key=lambda x: x['name'])
return render_template("getPlaylists.html", playlists=playlists, username=username)
I already tried printing the token_info and in the getPlaylist it looks always the same, also the username is always the same. Im afraid Im not storing the token_info of the user correctly, but I cant see another way to fix it.I tried this other option of code I saw here on stackoverflow but its happenning the same using it.
import spotipy
from spotipy.oauth2 import SpotifyOAuth
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow, Flow
from google.auth.transport.requests import Request
import google.oauth2.credentials
import json
import os
import os.path
import time
from flask import Flask, request, url_for, session, redirect, render_template
from flask_session import Session
import clientinfo # File containing all the info necessary to login the web APP to the Spotify and youtube APIs
""" requirements [
Flask,
Spotipy,
OS,
time,
clientinfo
]"""
app = Flask(__name__)
if __name__ == "__main__":
app.run(debug=True)
app.secret_key = os.environ['FLASK_S_KEY']
app.config['SESSION_COOKIE_NAME'] = 'Spotify Cookie'
# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
@app.after_request
def after_request(response):
"""Ensure responses aren't cached"""
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
def create_spotify_oauth():
return SpotifyOAuth(
client_id = os.environ['SPOTIFY_CLIENT_ID'],
client_secret = os.environ['SPOTIFY_CLIENT_SECRET'],
redirect_uri = url_for('redirectSite', _external=True),
scope = "user-read-private playlist-read-private playlist-read-collaborative",
show_dialog=True
)
def credentialscheck():
# Disable OAuthlib's HTTPS verification when running locally.
# *DO NOT* leave this option enabled in production.
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
json_credentials = session.get('credentials')
if 'credentials' in session:
dict_credentials = json.loads(json_credentials)
credentials = google.oauth2.credentials.Credentials.from_authorized_user_info(dict_credentials)
if credentials.expired:
credentials.refresh(Request())
else:
return redirect(url_for('redirectYT'))
# Endpoint Index
@app.route('/', methods=["GET"])
def index():
if request.method == "GET":
return render_template("index.html")
# Log in user to the web app using Spotify OAuth
@app.route("/login")
def login():
# Forget any user_id
session.clear()
sp_oauth = create_spotify_oauth()
auth_url = sp_oauth.get_authorize_url()
return redirect(auth_url)
@app.route("/logout")
def logout():
session.clear()
return redirect(url_for("index", _external=True))
# After login, redirect user to the page with the correct authorization token
@app.route("/redirect")
def redirectSite():
sp_oauth = create_spotify_oauth()
session.clear()
code = request.args.get("code")
token_info = sp_oauth.get_access_token(code)
session["token_info"] = token_info
return redirect(url_for("redirectYT", _external=True))
@app.route("/redirectYT")
def redirectYT():
# Disable OAuthlib's HTTPS verification when running locally.
# *DO NOT* leave this option enabled in production.
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
# Create OAuth flow object
flow = Flow.from_client_secrets_file(
'client_secrets.json',
scopes=["https://www.googleapis.com/auth/youtube.force-ssl"])
flow.redirect_uri = url_for('callback', _external=True)
authorization_url, state = flow.authorization_url(
access_type='offline',
prompt='select_account')
# Save the state so we can verify the request later
session['state'] = state
return redirect(authorization_url)
@app.route('/callback')
def callback():
# Verify the request state
if request.args.get('state') != session['state']:
raise Exception('Invalid state')
# Create the OAUth flow object
flow = InstalledAppFlow.from_client_secrets_file(
'client_secrets.json',
scopes=["https://www.googleapis.com/auth/youtube.force-ssl"],
state=session['state'])
flow.redirect_uri = url_for('callback', _external=True)
# Exchange the authorization code for an access token
authorization_response = request.url
flow.fetch_token(authorization_response=authorization_response)
# Save credentials to the session
credentials = flow.credentials
session['credentials'] = credentials.to_json()
return redirect(url_for('getPlaylists'))
@app.route('/getPlaylists')
def getPlaylists():
session['token_info'], authorized = get_token(session)
session.modified = True
if not authorized:
print("User not authorized!")
return redirect('/')
credentialscheck()
sp = spotipy.Spotify(auth = session.get('token_info').get('access_token'))
user = sp.current_user()
username = user['display_name']
print(f"User: {username}") # Debug
# Getting all playlists (since the current_user_playlists max limit is 50, we need a 'for' loop)
allPlaylists = []
i = 0
while True:
fiftyplaylists = sp.current_user_playlists(limit=50, offset=i * 50)['items']
i += 1
allPlaylists += fiftyplaylists
if (len(fiftyplaylists)< 50):
break
# Filtering the data we actually need in each playlist and sorting them alphabetically)
playlists = [{'name': playlist['name'], 'id': playlist['id']} for playlist in allPlaylists]
playlists.sort(key=lambda x: x['name'])
return render_template("getPlaylists.html", playlists=playlists, username=username)
def get_token(session):
token_valid = False
token_info = session.get("token_info", None)
if not (session.get('token_info', False)):
token_valid = False
return token_info, token_valid
now = int(time.time())
is_token_expired = session.get('token_info').get('expires_at') - now < 60
if (is_token_expired):
sp_oauth = create_spotify_oauth()
token_info = sp_oauth.refresh_access_token(session.get('token_info').get('refresh_token'))
token_valid = True
return token_info, token_valid
Felipe Firmida is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.