使用Spotipy在Flask会话中存储Spotify令牌?

时间:2019-08-20 19:30:57

标签: python html flask spotipy

我可能对Flask会话的工作方式不太了解,但是我尝试使用带有授权代码流的SpotiPY来生成Spotify API访问令牌,并将其存储在Flask的会话存储中。

该程序似乎无法存储,因此以后在尝试调用它时会出错。这是带有图像和标题的直观说明:https://imgur.com/a/KiYZFiQ

这是主要的服务器脚本:

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)

这是两个html文件:Index.htmlResults.html

一些值得注意的事情:

  • Credentz存储所有私有信息,包括SSKCLI_SECCLI_ID

  • 如果我在没有Web浏览器交互的非烧瓶环境中执行Spotipy请求,则该请求会正常工作。

  • 我能够将其他内容存储在会话存储中,并稍后再调用,只是由于某种原因而不是访问令牌。

  • 我最好的猜测是,在Spotify api重定向页面之前,页面没有时间存储它。

非常感谢您的帮助,谢谢!

1 个答案:

答案 0 :(得分:3)

您是正确的,在可以添加令牌之前,spotify正在重定向您,因此结束了该功能的执行。因此,在重定向之后,您需要添加一个回调步骤来获取身份验证令牌。

Spotitpy的util.prompt_for_user_token方法并非设计用于Web服务器,因此,最好的办法是实现自己的身份验证流,并在获得令牌后使用Spotipy。幸运的是,spotify授权流程非常简单且易于实现。

方法1:实现自我认证流:

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)

另一方面,我们可以作弊一点,并使用Spotipy本身正在使用的方法来节省一些代码。

方法2:使用spotipy.oauth2.SpotifyOAuth方法实现身份验证流程(以及自定义令牌管理):

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)

在我看来,任何一种方法都同样有效,只要使用您认为更容易理解的方法即可。

确保在Spotify应用程序仪表板上更新授权的重定向URI。

有关更多信息,请参见Spotify文档:https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow