Authorizing our application with authorization code flow
In order to make our Spotify terminal client work properly, we need special access rights to manipulate the user's playback. We do that by using the authorization code and we need to specifically request for the user-modify-playback-state access right.
There are a few more access rights which would be a good idea to add from the beginning if you intend to add more functionalities to this application; for example, if you want to be able to manipulate a user's private and public playlists, you may want to add the playlist-modify-private and playlist-modify-public scope.
You might also want to display a list of artists that the user follows on the client application, so you need to include user-follow-read to the scope as well.
It will suffice to request user-modify-playback-state access rights for the functionalities that we are going to implement in the client application.
The idea is to authorize our application using the authorization code flow. We are going to create a simple web application using the framework Flask that will define two routes. The / root will just render a simple page with a link that will redirect us to the Spotify authentication page.
The second root will be /callback, which is the endpoint that Spotify will call after the users of our application give authorization for our application to access their Spotify data.
Let's see how this is implemented, but first, we need to install Flask. Open a terminal and type the following command:
pip install flask
After you have installed it you can even include it in the requirements.txt file such as:
$ pip freeze | grep Flask >> requirements.txt
The command pip freeze will print all the installed packages in the requirements format. The output will return more items because it will also contain all the dependencies of the packages that we already installed, which is why we grep Flask and append it to the requirements.txt file.
Next time you are going to set up a virtual environment to work on this project you can just run:
pip install -r requirements.txt
Great! Now, we can start creating the web application. Create a file called spotify_auth.py.
First, we add all necessary imports:
from urllib.parse import urlencode
import requests
import json
from flask import Flask
from flask import render_template
from flask import request
from pytify.core import read_config
from pytify.core import BadRequestError
from pytify.auth import Authorization
from pytify.auth import get_auth_key
We are going to use the urlencode function in the urllib.parse module to encode the parameters that are going to be appended to the authorize URL. We are also going to use the requests to send a request to get the access_token after the user authorizes our app and use the json package to parse the response.
Then, we will import Flask-related things, so we can create a Flask application, render_template, so we can return a rendered HTML template to the user, and finally the request, so we can access the data sent back to us by Spotify's authorization service.
We will also import some functions that we included in the core and auth submodules of the pytify module: the read_config to load and read the YAML config file and the _authorization_code_request. The latter will be explained in more detail in a short while.
We will create a Flask app and the root route:
app = Flask(__name__)
@app.route("/")
def home():
config = read_config()
params = {
'client_id': config.client_id,
'response_type': 'code',
'redirect_uri': 'http://localhost:3000/callback',
'scope': 'user-read-private user-modify-playback-state',
}
enc_params = urlencode(params)
url = f'{config.auth_url}?{enc_params}'
return render_template('index.html', link=url)
Great! Starting from the top, we read the configuration file so we can get our client_id and also the URL for Spotify's authorization service. We build the parameters dictionary with the client_id; the response type for the authorization code flow needs to be set to code; the redirect_uri is the callback URI which Spotify's authorization service will use to send us the authorization code back. And finally, since we are going to send instructions to the REST API to play a track in the user's active device, the application needs to have user-modify-playback-state permissions.
Now, we encode all the parameters and build the URL.
The return value will be a rendered HTML. Here, we will use the render_template function, passing a template as a first argument. By default, Flask will search this template in a directory called templates. The second argument to this function is the model. We are passing a property named link and setting the value of the variable URL. This way, we can render the link in the HTML template such as: {{link}}.
Next, we are going to add a function to acquire the access_token and the refresh_token for us after we get the authorization code back from Spotify's account service. Create a function called _authorization_code_request with the following content:
def _authorization_code_request(auth_code):
config = read_config()
auth_key = get_auth_key(config.client_id, config.client_secret)
headers = {'Authorization': f'Basic {auth_key}', }
options = {
'code': auth_code,
'redirect_uri': 'http://localhost:3000/callback',
'grant_type': 'authorization_code',
'json': True
}
response = requests.post(
config.access_token_url,
headers=headers,
data=options
)
content = json.loads(response.content.decode('utf-8'))
if response.status_code == 400:
error_description = content.get('error_description', '')
raise BadRequestError(error_description)
access_token = content.get('access_token', None)
token_type = content.get('token_type', None)
expires_in = content.get('expires_in', None)
scope = content.get('scope', None)
refresh_token = content.get('refresh_token', None)
return Authorization(access_token, token_type, expires_in,
scope, refresh_token)
This function is pretty much the same as the _refresh_access_token function that we previously implemented in the auth.py file. The only thing to note here is that in the options, we are passing the authorization code, and the grant_type is set to authorization_code:
@app.route('/callback')
def callback():
config = read_config()
code = request.args.get('code', '')
response = _authorization_code_request(config, code)
file = open('.pytify', mode='w', encoding='utf-8')
file.write(response.refresh_token)
file.close()
return 'All set! You can close the browser window and stop the
server.'
Here, we define the route that will be called by Spotify's authorization service to send back the authorization code.
We start off by reading the configuration, parsing the code from the request data, and calling the _authorization_code_request, passing the code we have just obtained.
This function will send another request using this code, and it will acquire an access token that we can use to send requests, along with a refresh token that will be stored in a file called .pytify in the musicterminal directory.
The access token that we obtain to make the requests to the Spotify REST API is valid for 3,600 seconds, or 1 hour, which means that within one hour, we can use the same access token to make requests. After that, we need to refresh the access token. We can do that by using the refresh token that is stored in the .pytify file.
Lastly, we send a message to the browser with a success message.
Now, to finish our Flask application, we need to add the following code:
if __name__ == '__main__':
app.run(host='localhost', port=3000)
This tells Flask to run the server on the localhost and use port 3000.
The home function of our Flash application will, as a response, return a templated HTML file called index.html. We haven't created that file yet, so let's go ahead and create a folder called musicterminal/templates and inside the newly created directory, add a file called index.html with the following contents:
<html>
<head>
</head>
<body>
<a href={{link}}> Click here to authorize </a>
</body>
</html>
There's not much to explain here, but note that we are referencing the link property that we passed to the render_template function in the home function of the Flask application. We are setting the href attribute of that anchor element to the value of the link.
Great! There is only more thing before we try this out and see if everything is working properly. We need to change the settings of our Spotify app; more specifically, we need to configure the callback function for the application, so we can receive the authorization code.
With that said, head to the https://beta.developer.spotify.com/dashboard/ website and log in with your credentials. The dashboard will show the pytify app that we created at the beginning of this chapter. Click on the app name and then click on the EDIT SETTINGS button on the top right of the page.
Scroll down until you find Redirect URIs, and in the text field, enter http://localhost:3000/callback and click on the ADD button. Your configuration should look as follows:
Great! Scroll down to the bottom of the dialog and click the SAVE button.
Now, we need to run the Flask application that we just created. On the terminal, in the projects root folder, type the following command:
python spotify_auth.py
You should see an output similar to this:
* Running on http://localhost:3000/ (Press CTRL+C to quit)
Open the browser of your choice and go to http://localhost:3000; you will see a simple page with the link that we created:
Click on the link and you will be sent to Spotify's authorization service page.
A dialog will be displayed asking to connect the Pytify app to our account. Once you authorize it, you will be redirected back to http://localhost:3000/callback. If everything goes well, you should see the All set! You can close the browser window and stop the server message on the page.
Now, just close the browser, and you can stop the Flask application.
Note that we now have a file named .pytify in the musicterminal directory. If you look at the contents, you will have an encrypted key similar to this one:
AQB2jJxziOvuj1VW_DOBeJh-uYWUYaR03nWEJncKdRsgZC6ql2vaUsVpo21afco09yM4tjwgt6Kkb_XnVC50CR0SdjWrrbMnr01zdemN0vVVHmrcr_6iMxCQSk-JM5yTjg4
Now, we are ready to start coding the player.
Next up, we are going to add some functions that will perform requests to Spotify's Web API to search for artists, get a list of an artist's albums and a list of tracks in an album, and play the selected track.