Implementing the client credentials flow

The client credential flow is quite simple. Let's break down all the steps until we get the access_token:

  1. Our application will request the access token from the Spotify accounts service; remember that in our configuration file, we have the api_access_token. That's the URL we need to send the request to get hold of an access token. There are three things that we will need to send the request, the client id, the client secret, and the grant type, which in this case is client_credentials.
  2. The Spotify account service will validate that request, check if the keys match with the keys of the app that we register to the developer's site, and return an access token.
  3. Now, our application has to use this access token in order to consume data from the REST APIs.
  4. The Spotify REST API will return the data we requested.

Before we start implementing the functions that will make the authentication and get the access token, we can add a custom exception that we will throw if we get a bad request (HTTP 400) from the Spotify account service.

Let's create a file named exceptions.py in the musicterminal/pytify/core directory with the following contents:

class BadRequestError(Exception):
    pass

This class doesn't do much; we simply inherit from Exception. We could have just thrown a generic exception, but it is a good practice to create your own custom exceptions with good names and descriptions when developing frameworks and libraries that other developers will make use of.

So, instead of throwing an exception like this:

raise Exception('some message')

We can be more explicit and throw a BadRequestError, like so:

raise BadRequestError('some message')

Now, developers using this code can handle this kind of exception properly in their code.

Open the __init__.py file in the musicterminal/pytify/core directory and add the following import statement:

from .exceptions import BadRequestError

Perfect! Now, it is time to add a new file called auth.py in the musicterminal/pytify/auth directory, and the first thing we are going to add to this file is a few imports:

import requests
import base64
import json

from .authorization import Authorization
from pytify.core import BadRequestError
I usually put all the imports from standard library modules first and function imports in files from my applications last. It is not a requirement, but it is just something I think makes the code cleaner and more organized. This way, I can easily see which are standard library items and which aren't.

Now, we can start adding the functions that will send the request the to the Spotify account service and return the access token. The first function that we are going to add is called get_auth_key:

def get_auth_key(client_id, client_secret):
    byte_keys = bytes(f'{client_id}:{client_secret}', 'utf-8')
    encoded_key = base64.b64encode(byte_keys)
    return encoded_key.decode('utf-8')

The client credential flow requires us to send the client_id and the client_secret, which has to be base 64-encoded. First, we convert the string with the client_id:client_secret format to bytes. After that, we encode it using base 64 and then decode it, returning the string representation of that encoded data so we can send it with the request payload.

The other function that we are going to implement in the same file is called _client_credentials:

def _client_credentials(conf):

auth_key = get_auth_key(conf.client_id, conf.client_secret)

headers = {'Authorization': f'Basic {auth_key}', }

options = {
'grant_type': 'client_credentials',
'json': True,
}

response = requests.post(
'https://accounts.spotify.com/api/token',
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)

return Authorization(access_token, token_type, expires_in,
scope, None)

This function gets an argument as the configuration and uses the get_auth_key function to pass the client_id and the client_secret to build a base 64-encoded auth_key. This will be sent to Spotify's accounts service to request an access_token.

Now, it is time to prepare the request. First, we set the Authorization in the request header, and the value will be the Basic string followed by the auth_key. The payload for this request will be grant_type, which in this case is client_credentials, and json will be set to True, which tells the API that we want the response in JSON format.

We use the requests package to make the request to Spotify's account service, passing the headers and the data that we configured.

When we get a response, we first decode and load the JSON data into the variable content.

If the HTTP status code is 400 (BAD_REQUEST) we raise a BadRequestError; otherwise, we get the values for access_token, token_type, expires_in, and scope, and finally create an Authorization tuple and return it.

Note that we are setting None to the last parameter when creating an Authenticationnamedtuple. The reason for this is that Spotify's account service doesn't return a  refresh_token when the type of authentication is CLIENT_CREDENTIALS.

All the functions that we have created so far are meant to be private, so the last function that we are going to add is the authenticate function. This is the function that developers will invoke to start the authentication process:

def authenticate(conf):
    return _client_credentials(conf)

This function is pretty straightforward; the function gets an argument as an instance of the Config, namedtuple, which will contain all the data that has been read from the configuration file. We then pass the configuration to the _client_credentials function, which will obtain the access_token using the client credentials flow.

Let's open the __init__.py file in the musicterminal/pytify/auth directory and import the authenticate and get_auth_key functions:

from .auth import authenticate
from .auth import get_auth_key

Nice! Let's try this out in the Python REPL:

Python 3.6.2 (default, Oct 15 2017, 01:15:28)
[GCC 6.3.0 20170516] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pytify.core import read_config
>>> from pytify.auth import authenticate
>>> config = read_config()
>>> auth = authenticate(config)
>>> auth
Authorization(access_token='BQDM_DC2HcP9kq5iszgDwhgDvq7zm1TzvzXXyJQwFD7trl0Q48DqoZirCMrMHn2uUml2YnKdHOszAviSFGtE6w', token_type='Bearer', expires_in=3600, scope=None, refresh_token=None)
>>>

Exactly what we expected! The next step is to start creating the functions that will consume Spotify's REST API.