Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing Spotify token in flask session using spotipy?

I may have a bad understanding of how the flask session works, but I am trying to generate a Spotify API access token using SpotiPY with the Authorization Code Flow, and store it in Flask's session storage.

The program doesn't seem to be able to store it, and therefore later runs in to an error when trying to call it. Here is a visual explanation with images and captions: https://imgur.com/a/KiYZFiQ

Here is the main server script:

from flask import Flask, render_template, redirect, request, session, make_response,session,redirect
import spotipy
import spotipy.util as util
from credentz import *
app = Flask(__name__)

app.secret_key = SSK

@app.route("/")
def verify():
    session.clear()
    session['toke'] = util.prompt_for_user_token("", scope='playlist-modify-private,playlist-modify-public,user-top-read', client_id=CLI_ID, client_secret=CLI_SEC, redirect_uri="http://127.0.0.1:5000/index")
    return redirect("/index")

@app.route("/index")
def index():
    return render_template("index.html")


@app.route("/go", methods=['POST'])
def go():
    data=request.form
    sp = spotipy.Spotify(auth=session['toke'])
    response = sp.current_user_top_artists(limit=data['num_tracks'], time_range=data['time_range'])
    return render_template("results.html",data=data)


if __name__ == "__main__":
    app.run(debug=True)

and here are the two html files: Index.html, Results.html

Some things worth noting:

  • Credentz stores all of the private info including SSK, CLI_SEC, and CLI_ID.

  • The spotipy request works fine if I do it in a non-flask environment with no web-browser interaction.

  • I am able to store other things in the session storage, and call it back later, just not the access token for some reason.

  • My best guess is that the page doesn't have time to store it before the Spotify api redirects the page, not sure though.

Any help is really appreciated, Thank You!

like image 410
Sam Tubb Avatar asked Aug 20 '19 19:08

Sam Tubb


People also ask

How long does Spotify OAuth token last?

Access tokens issued from the Spotify account service has a lifetime of one hour. If a longer session is desired Spotify account service supports the OAuth Code grant flow.

Does Spotify use flask?

Spotify-Flask is a very straightforward way to request data from Spotify API using flask with templates. You can also just use the Python API to make requests for your application.

Does Spotify use OAuth?

If you're interested in working with the Spotify API, you'll need an OAuth token in order to access most of its data. In this blog post, we'll explain what OAuth is, how it works, and break down each step in the context of Spotify's OAuth flow.


1 Answers

You are right, spotify is redirecting you before the token can be added therefore ending the execution of that function. So you need to add a callback step to grab the authentication token after you've been redirected.

Spotitpy's util.prompt_for_user_token method is not designed to be used in a web server so your best best is to implement the auth flow your self and then use spotipy once you have the token. Luckily the spotify authorization flow is pretty simple and easy to implement.

Method 1: Implementing auth flow our self:

from flask import Flask, render_template, redirect, request, session, make_response,session,redirect
import spotipy
import spotipy.util as util
from credentz import *
import requests
app = Flask(__name__)

app.secret_key = SSK

API_BASE = 'https://accounts.spotify.com'

# Make sure you add this to Redirect URIs in the setting of the application dashboard
REDIRECT_URI = "http://127.0.0.1:5000/api_callback"

SCOPE = 'playlist-modify-private,playlist-modify-public,user-top-read'

# Set this to True for testing but you probably want it set to False in production.
SHOW_DIALOG = True

# authorization-code-flow Step 1. Have your application request authorization; 
# the user logs in and authorizes access
@app.route("/")
def verify():
    auth_url = f'{API_BASE}/authorize?client_id={CLI_ID}&response_type=code&redirect_uri={REDIRECT_URI}&scope={SCOPE}&show_dialog={SHOW_DIALOG}'
    print(auth_url)
    return redirect(auth_url)

@app.route("/index")
def index():
    return render_template("index.html")

# authorization-code-flow Step 2.
# Have your application request refresh and access tokens;
# Spotify returns access and refresh tokens
@app.route("/api_callback")
def api_callback():
    session.clear()
    code = request.args.get('code')

    auth_token_url = f"{API_BASE}/api/token"
    res = requests.post(auth_token_url, data={
        "grant_type":"authorization_code",
        "code":code,
        "redirect_uri":"http://127.0.0.1:5000/api_callback",
        "client_id":CLI_ID,
        "client_secret":CLI_SEC
        })

    res_body = res.json()
    print(res.json())
    session["toke"] = res_body.get("access_token")

    return redirect("index")


# authorization-code-flow Step 3.
# Use the access token to access the Spotify Web API;
# Spotify returns requested data
@app.route("/go", methods=['POST'])
def go():
    data = request.form    
    sp = spotipy.Spotify(auth=session['toke'])
    response = sp.current_user_top_artists(limit=data['num_tracks'], time_range=data['time_range'])
    return render_template("results.html", data=data)

if __name__ == "__main__":
    app.run(debug=True)

On the other hand we can cheat a little bit and use the methods that spotipy itself is using in order to save us some code.

Method 2: Implementing auth flow (along with custom token management) using spotipy.oauth2.SpotifyOAuth method:

from flask import Flask, render_template, redirect, request, session, make_response,session,redirect
import spotipy
import spotipy.util as util
from credentz import *
import time
import json
app = Flask(__name__)

app.secret_key = SSK

API_BASE = 'https://accounts.spotify.com'

# Make sure you add this to Redirect URIs in the setting of the application dashboard
REDIRECT_URI = "http://127.0.0.1:5000/api_callback"

SCOPE = 'playlist-modify-private,playlist-modify-public,user-top-read'

# Set this to True for testing but you probaly want it set to False in production.
SHOW_DIALOG = True


# authorization-code-flow Step 1. Have your application request authorization; 
# the user logs in and authorizes access
@app.route("/")
def verify():
    # Don't reuse a SpotifyOAuth object because they store token info and you could leak user tokens if you reuse a SpotifyOAuth object
    sp_oauth = spotipy.oauth2.SpotifyOAuth(client_id = CLI_ID, client_secret = CLI_SEC, redirect_uri = REDIRECT_URI, scope = SCOPE)
    auth_url = sp_oauth.get_authorize_url()
    print(auth_url)
    return redirect(auth_url)

@app.route("/index")
def index():
    return render_template("index.html")

# authorization-code-flow Step 2.
# Have your application request refresh and access tokens;
# Spotify returns access and refresh tokens
@app.route("/api_callback")
def api_callback():
    # Don't reuse a SpotifyOAuth object because they store token info and you could leak user tokens if you reuse a SpotifyOAuth object
    sp_oauth = spotipy.oauth2.SpotifyOAuth(client_id = CLI_ID, client_secret = CLI_SEC, redirect_uri = REDIRECT_URI, scope = SCOPE)
    session.clear()
    code = request.args.get('code')
    token_info = sp_oauth.get_access_token(code)

    # Saving the access token along with all other token related info
    session["token_info"] = token_info


    return redirect("index")

# authorization-code-flow Step 3.
# Use the access token to access the Spotify Web API;
# Spotify returns requested data
@app.route("/go", methods=['POST'])
def go():
    session['token_info'], authorized = get_token(session)
    session.modified = True
    if not authorized:
        return redirect('/')
    data = request.form
    sp = spotipy.Spotify(auth=session.get('token_info').get('access_token'))
    response = sp.current_user_top_tracks(limit=data['num_tracks'], time_range=data['time_range'])

    # print(json.dumps(response))

    return render_template("results.html", data=data)

# Checks to see if token is valid and gets a new token if not
def get_token(session):
    token_valid = False
    token_info = session.get("token_info", {})

    # Checking if the session already has a token stored
    if not (session.get('token_info', False)):
        token_valid = False
        return token_info, token_valid

    # Checking if token has expired
    now = int(time.time())
    is_token_expired = session.get('token_info').get('expires_at') - now < 60

    # Refreshing token if it has expired
    if (is_token_expired):
        # Don't reuse a SpotifyOAuth object because they store token info and you could leak user tokens if you reuse a SpotifyOAuth object
        sp_oauth = spotipy.oauth2.SpotifyOAuth(client_id = CLI_ID, client_secret = CLI_SEC, redirect_uri = REDIRECT_URI, scope = SCOPE)
        token_info = sp_oauth.refresh_access_token(session.get('token_info').get('refresh_token'))

    token_valid = True
    return token_info, token_valid

if __name__ == "__main__":
    app.run(debug=True)

Either method is equally valid in my opinion, just go with whichever method you find easier to understand.

Make sure that you update the authorized Redirect URIs in your spotify application dashboard.

See the spotify docs for more info: https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow

like image 197
Dalton Pearson Avatar answered Oct 12 '22 23:10

Dalton Pearson