Implementing the Authorization Code Flow - Build a Spotify Connected App (2023)

Now that we have a high-level understanding of what OAuth is for and how it works, let's take a closer look at how OAuth works with the Spotify API.

Spotify's Authorization Flows#

According to Spotify's Authorization Guide, there are four possible flows for obtaining app authorization:

  1. Authorization Code Flow

  2. Authorization Code Flow With Proof Key for Code Exchange (PKCE)

  3. Implicit Grant Flow

  4. Client Credentials Flow

Each of these flows provides a slightly different level of authorization due to the way it is granted. For example, the Implicit Grant Flow can be implemented entirely client-side (no server), but it does not provide a refresh token. The Client Credentials Flow is used for server-to-server authentication, but authorization does not grant permission to access user resources. They all follow the OAuth flow we learned in the last lesson, but each has its own variation.

Out of all four of these flows, the Authorization Code Flow is the only one that lets the client access user resources, requires a server-side secret key (an extra layer of security), and provides an access token that can be refreshed. The ability to refresh an access token is a big advantage — users of our app will only need to grant permission once.

Below, we'll be going step-by-step through how to build out this flow with Node and Express. Note that we'll be heavily referring to Spotify's official documentation of the Authorization Code Flow. We'll also be pulling from the Web API Auth Examples, which is referred to in Spotify's official web API tutorial.

Set up environment variables#

Before making any requests to Spotify, we first need to make sure we have our app's client ID and client secret from the Spotify Developer Dashboard.

Implementing the Authorization Code Flow - Build a Spotify Connected App (1)

(Client secret is obfuscated in the screenshot above, since it should stay a secret.)

Once we've located these two values, we'll create a .env file at the root of our project and add the CLIENT_ID and CLIENT_SECRET variables. (Make sure to replace the placeholder values with your own client ID and secret.)

1

CLIENT_ID=XXX

2

CLIENT_SECRET=XXX

We'll be using this .env file to store sensitive information and other environment variables for our app. In case this code ends up in a place where others can see it, such as a public GitHub repository, we want to make sure those values are kept private.

To ensure our .env is kept private, let's add it to our .gitignore file.

1

.DS_Store

2

node_modules

3

.env

Finally, we'll also create a .env.example file, which will not be gitignored.

1

CLIENT_ID=XXX

2

CLIENT_SECRET=XXX

Since your .env file should be kept secret (and not checked into source control) it's good practice to add an example .env file to your codebase for others (or future you) to reference.

Install and use dotenv#

Now that we've stored our app's client ID and secret in a .env file, we need a way to make our code aware of them. To do that, we'll be using an npm module called dotenv, which lets us load environment variables from a .env file into process.env, an object containing the user environment.

In our terminal, let's install the dotenv module.

1

npm install dotenv

Then, at the very top of our index.js file, add the following line.

1

require('dotenv').config();

To make sure it's working, we can console.log() the value of process.env.CLIENT_ID.

1

require('dotenv').config();

2

3

console.log(process.env.CLIENT_ID);

Now, when we run npm start in our terminal, we should see the CLIENT_ID value we have stored in our .env.

Implementing the Authorization Code Flow - Build a Spotify Connected App (2)

Since Node.js is server-side, console.log() shows up in the terminal, not the browser console.

Set up the redirect URI#

The last thing we need to do to prepare our environment is set up our redirect URI. The redirect URI is a route of our app that we want the Spotify Accounts Service to redirect the user to once they've authorized our app (i.e. successfully logged into Spotify).

In our case, the redirect URI will be the /callback route (http://localhost:8888/callback). We'll set up this route handler later.

In the Spotify Developer Dashboard, click the Edit Settings button and add http://localhost:8888/callback as a Redirect URI.

Implementing the Authorization Code Flow - Build a Spotify Connected App (3)

Once the redirect URI has been added, make sure to scroll down and hit the Save button.

Implementing the Authorization Code Flow - Build a Spotify Connected App (4)

Then, in our .env and .env.example files, we'll add that redirect URI as an environment variable.

1

REDIRECT_URI=http://localhost:8888/callback

Now that we have our CLIENT_ID, CLIENT_SECRET, and REDIRECT_URI environment variables all set up, let's store those as constants at the top of our index.js for convenience.

1

const CLIENT_ID = process.env.CLIENT_ID;

2

const CLIENT_SECRET = process.env.CLIENT_SECRET;

3

const REDIRECT_URI = process.env.REDIRECT_URI;

Sweet! Now our development environment is all ready to go.

Step 1: Request authorization from Spotify#

The first step in Spotify's Authorization Code Flow is having our app request authorization from the Spotify Accounts Service. In code terms, this means sending a GET request to the Spotify Accounts Service /authorize endpoint.

1

GET https://accounts.spotify.com/authorize

To trigger that HTTP request, we can set up a route in our Express app that can eventually be hooked up to a button on the front end with an anchor link like so:

1

<a href="http://localhost:8888/login">Log in to Spotify</a>

Set up the /login route handler#

Let's set up a route handler for the /login endpoint in our index.js

1

app.get('/login', (req, res) => {

2

 res.send('Log in to Spotify');

3

});

To make sure the route handler is working, let's visit http://localhost:8888/login and make sure we see the text Log in to Spotify.

Implementing the Authorization Code Flow - Build a Spotify Connected App (5)

Next, we want to set up our /login route to hit the Spotify Accounts Service https://accounts.spotify.com/authorize endpoint. To do that, we'll replace our res.send() with a res.redirect().

(Video) Getting Started with Spotify API (Complete Overview)

1

app.get('/login', (req, res) => {

2

 res.redirect('https://accounts.spotify.com/authorize');

3

});

Now, when we refresh the /login page, we'll be automatically redirected to the Spotify Accounts Service URL (https://accounts.spotify.com/authorize). However, there will be an error saying that there is a missing required parameter.

Implementing the Authorization Code Flow - Build a Spotify Connected App (6)

This is because the /authorize endpoint has required query parameters which we failed to include. (Check out all required and optional query parameters for the endpoint in the documentation.)

The three required query parameters for the /authorize endpoint are: client_id, response_type, and redirect_uri. There are also other optional query params for things such as authorization scopes and security that we'll eventually include.

Let's modify our res.redirect() to include the required query params with template strings:

1

app.get('/login', (req, res) => {

2

 res.redirect(`https://accounts.spotify.com/authorize?client_id=${CLIENT_ID}&response_type=code&redirect_uri=${REDIRECT_URI}`);

3

});

If we visit the http://localhost:8888/login route again, we'll now be redirected to the Spotify Accounts Service login page instead of seeing an error. Progress!

Implementing the Authorization Code Flow - Build a Spotify Connected App (7)

In the general OAuth flow, this is step 2 — Spotify authorizes access to client.

Once we log in with our username and password, we'll be redirected to a consent page where we can agree to let Spotify access our data.

Implementing the Authorization Code Flow - Build a Spotify Connected App (8)

In the general OAuth flow, this is step 3 — User grants app access their Spotify data

Once we hit Agree, Spotify will then redirect us to the REDIRECT_URI (http://localhost:8888/callback) we provided in the query params of the /authorize request.

Implementing the Authorization Code Flow - Build a Spotify Connected App (9)

Unfortunately, since our app doesn't have a /callback route handler yet, we'll see an error. Regardless, we can tell our request was successful because there is a code query param in our /callback route.

The value of the code query param is an authorization code that we will be able to exchange for an access token. We're getting closer to our goal of getting an access token from Spotify!

Refactor with the querystring module#

Before we exchange the authorization code for an access token, let's refactor our existing code to make handling query params easier and less error-prone.

There's a built-in Node module called querystring that lets us parse and stringify query strings. Let's import it at the top of our index.js:

1

const querystring = require('querystring');

querystring.stringify() takes an object with keys and values and serializes them into a query string. No more having to keep track of ampersands and equal signs!

1

app.get('/login', (req, res) => {

2

 const queryParams = querystring.stringify({

3

 client_id: CLIENT_ID,

4

 response_type: 'code',

5

 redirect_uri: REDIRECT_URI,

6

 });

7

8

 res.redirect(`https://accounts.spotify.com/authorize?${queryParams}`);

9

});

In the snippet above, queryParams evaluates to something like client_id=abc123&response_type=code&redirect_uri=http://localhost:8888/callback. Then, all we have to do is append ?${queryParams} to the end of the /authorize endpoint template string.

Add state and scope query params#

Now that we have an easier way of handling query params in our HTTP requests, let's add the optional query params on the /authorize endpoint that we didn't include before.

1

/**

2

 * Generates a random string containing numbers and letters

3

 * @param {number} length The length of the string

4

 * @return {string} The generated string

5

 */

6

const generateRandomString = length => {

7

 let text = '';

8

 const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

9

 for (let i = 0; i < length; i++) {

10

 text += possible.charAt(Math.floor(Math.random() * possible.length));

11

 }

12

 return text;

13

};

14

15

16

const stateKey = 'spotify_auth_state';

17

18

app.get('/login', (req, res) => {

19

 const state = generateRandomString(16);

20

 res.cookie(stateKey, state);

21

22

 const scope = 'user-read-private user-read-email';

23

24

 const queryParams = querystring.stringify({

25

(Video) Postman Tutorial - Getting started with Spotify API, OAUTH 2.0 Authorization and Create Playlists

 client_id: CLIENT_ID,

26

 response_type: 'code',

27

 redirect_uri: REDIRECT_URI,

28

 state: state,

29

 scope: scope,

30

 });

31

32

 res.redirect(`https://accounts.spotify.com/authorize?${queryParams}`);

33

});

In the snippet above, we first update the /login handler to use the generateRandomString() utility function to generate a random string for the state query param and cookie. The state query param is kind of a security measure — it protects against attacks such as cross-site request forgery.

We also add the scope query param, which is a space-separated list of Spotify's pre-defined authorization scopes. Let's pass two scopes for now: user-read-private and user-read-email. These scopes will let us access details about the currently logged-in user's account and their email.

Now, when we visit the /login route and log into Spotify, we should see a consent page with updated scope details:

Implementing the Authorization Code Flow - Build a Spotify Connected App (10)

Great! Now let's use the authorization code we received from Spotify's /authorize endpoint to request an access token.

Step 2: Use authorization code to request access token#

Currently, once the user logs into Spotify and gets redirected back to our app, they hit an error page. This is because we haven't set up our /callback route handler yet.

Implementing the Authorization Code Flow - Build a Spotify Connected App (11)

Set up the /callback route handler#

Let's stub it out:

1

app.get('/callback', (req, res) => {

2

 res.send('Callback');

3

});

To exchange the authorization code for an access token, we need this route handler to send a POST request to the Spotify Accounts Service /api/token endpoint.

1

POST https://accounts.spotify.com/api/token

Similar to the /authorize endpoint, the /api/token endpoint has required body parameters. The three params required for the /api/token endpoint are grant_type, code, and redirect_uri. When sent along in the body of the POST request, they need to be encoded in the application/x-www-form-urlencoded format.

  1. grant_type: authorization_code

  2. code: The authorization code (the code query param on the /callback URL)

  3. redirect_uri: The REDIRECT_URI (http://localhost:8888/callback)

The token endpoint also has a required Authorization header, which needs to be a base 64 encoded string in this format: Authorization: Basic <base 64 encoded client_id:client_secret>.

Set up the POST request with Axios#

Now, let's create our POST request in our /callback route handler to send the authorization code back to the Spotify server.

Although it's possible to send a POST request with Node's built-in modules, it can get pretty verbose and clunky. A popular abstraction we can use instead is the Axios library, which provides a simpler API. Other than being easy to use, Axios also works both client-side (in the browser) and server-side (in our Express app).

Let's install axios as a dependency.

1

npm install axios

Then require it at the top of our index.js file.

1

const axios = require('axios');

Now let's set up the POST request to https://accounts.spotify.com/api/token with the axios() method in our /callback route handler.

1

app.get('/callback', (req, res) => {

2

 const code = req.query.code || null;

3

4

 axios({

5

 method: 'post',

6

 url: 'https://accounts.spotify.com/api/token',

7

 data: querystring.stringify({

8

 grant_type: 'authorization_code',

9

 code: code,

10

 redirect_uri: REDIRECT_URI

11

 }),

12

 headers: {

13

 'content-type': 'application/x-www-form-urlencoded',

14

 Authorization: `Basic ${new Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64')}`,

15

 },

16

 })

17

 .then(response => {

18

 if (response.status === 200) {

19

 res.send(`<pre>${JSON.stringify(response.data, null, 2)}</pre>`);

20

 } else {

21

 res.send(response);

22

 }

23

 })

24

 .catch(error => {

25

 res.send(error);

26

 });

27

});

There's a bunch of things happening in this chunk of code, so let's break it down a bit.

(Video) Developer Day at Spotify - Building apps for Spotify Connect

First, we store the value of our authorization code which we got from the code query param in the code variable.

1

const code = req.query.code || null;

In Express, req.query is an object containing a property for each query string parameter a route. For example, if the route was /callback?code=abc123&state=xyz789, req.query.code would be abc123 and req.query.state would be xyz789. If for some reason the route doesn't have a code query param, we set null as a fallback.

Next, we set up the POST request to https://accounts.spotify.com/api/token by passing a config object to the axios() method, which will send the request when invoked.

1

 axios({

2

 method: 'post',

3

 url: 'https://accounts.spotify.com/api/token',

4

 data: querystring.stringify({

5

 grant_type: 'authorization_code',

6

 code: code,

7

 redirect_uri: REDIRECT_URI

8

 }),

9

 headers: {

10

 'content-type': 'application/x-www-form-urlencoded',

11

 Authorization: `Basic ${new Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64')}`,

12

 },

13

 })

In the data object, we use querystring.stringify() to format the three required body params. The code variable we declared above is passed as one of the params here.

We also set two header params in the headers object: a content-type header and an Authorization header.

Then, we chain .then() and .catch() callback functions to handle resolving the promise the axios() method returns. If you're unfamiliar with promises, learn more about them in the MDN docs.

1

 .then(response => {

2

 if (response.status === 200) {

3

 res.send(`<pre>${JSON.stringify(response.data, null, 2)}</pre>`);

4

 } else {

5

 res.send(response);

6

 }

7

 })

8

 .catch(error => {

9

 res.send(error);

10

 });

If our request is successful (i.e. returns with a 200 status code), the .then() callback will be invoked, where we return the stringified response.data object from the Axios response. (It's important to note here that Axios stores the data returned by requests in the data property of the response object, not the response object itself.)

On the other hand, if our request fails, the error will be caught in the .catch() callback, in which case we just return the error message.

Our use of the JSON.stringify() method (<pre>${JSON.stringify(response.data, null, 2)}</pre>) is a just a handy way to format JSON nicely in the browser. Simply returning response.data would also work, but the JSON displayed in the browser wouldn't be formatted.

Once we visit the http://localhost:8888/login endpoint again, we should see the JSON data returned by Spotify's /api/token endpoint, which includes the special access_token we've been waiting for! 🎉

Implementing the Authorization Code Flow - Build a Spotify Connected App (12)

In the general OAuth flow, this is step 4 — client receives access token from Spotify.

Step 3: Use access token to request data from the Spotify API#

Now that we finally have an access token, we can test out requesting some user data from the Spotify API.

Let's modify the .then() callback function to send a GET request to the https://api.spotify.com/v1/me endpoint, which will return detailed profile information about the current user.

In the general OAuth flow, this is step 5 — client uses access token to request data from Spotify.

1

 .then(response => {

2

 if (response.status === 200) {

3

4

 const { access_token, token_type } = response.data;

5

6

 axios.get('https://api.spotify.com/v1/me', {

7

 headers: {

8

 Authorization: `${token_type} ${access_token}`

9

 }

10

 })

11

 .then(response => {

12

 res.send(`<pre>${JSON.stringify(response.data, null, 2)}</pre>`);

13

 })

14

 .catch(error => {

15

 res.send(error);

16

 });

17

18

 } else {

19

 res.send(response);

20

(Video) Postman get OAuth 2.0 access token from Spotify
 }

21

 })

22

 .catch(error => {

23

 res.send(error);

24

 });

Notice that we use destructuring to store the access_token and token_type as variables to pass into the Authorization header.

Now, when we visit our /login route again, our /callback route will display the user profile JSON data returned from Spotify's /me endpoint.

Implementing the Authorization Code Flow - Build a Spotify Connected App (13)

Sweet! This opens up a bunch of new doors for us to interact with the Spotify API in interesting ways. We'll get to this soon!

Bonus step: Refresh access token#

Before we move on to making more requests to the Spotify API, there's one last thing we need to do to cover our bases with the authorization code flow.

If we take another look at the data returned by Spotify's /api/token endpoint, we'll see that in addition to the access_token, there are also a token_type, scope, expires_in, and refresh_token values.

1

{

2

 "access_token": "NgCXRK...MzYjw",

3

 "token_type": "Bearer",

4

 "scope": "user-read-private user-read-email",

5

 "expires_in": 3600,

6

 "refresh_token": "NgAagA...Um_SHo"

7

}

The expires_in value is the number of seconds that the access_token is valid. This means after 3600 seconds, or 60 minutes, our access_token will expire.

Once the token is expired, there are two things that could happen: 1) we force the user to log in again, or 2) we use the refresh_token to retrieve another access token behind the scenes, not requiring the user log in again. The better user experience is definitely the latter, so let's make sure our app has a way to handle that.

Set up the /refresh_token route handler#

Let's set up a route handler to handle requesting a new access token with our refresh token.

1

app.get('/refresh_token', (req, res) => {

2

 const { refresh_token } = req.query;

3

4

 axios({

5

 method: 'post',

6

 url: 'https://accounts.spotify.com/api/token',

7

 data: querystring.stringify({

8

 grant_type: 'refresh_token',

9

 refresh_token: refresh_token

10

 }),

11

 headers: {

12

 'content-type': 'application/x-www-form-urlencoded',

13

 Authorization: `Basic ${new Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString('base64')}`,

14

 },

15

 })

16

 .then(response => {

17

 res.send(response.data);

18

 })

19

 .catch(error => {

20

 res.send(error);

21

 });

22

});

This route handler is almost the same as the /callback handler, except for a few small differences. The grant_type is refresh_token instead of authorization_code, and we're sending along a refresh_token instead of an authorization code.

To test the route handler, we'll have to go through the login flow again. For testing purposes, let's replace the GET request we made to the https://api.spotify.com/v1/me endpoint in the /callback route handler with a GET request to our local /refresh_token endpoint (http://localhost:8888/refresh_token).

1

 const { refresh_token } = response.data;

2

3

 axios.get(`http://localhost:8888/refresh_token?refresh_token=${refresh_token}`)

4

 .then(response => {

5

 res.send(`<pre>${JSON.stringify(response.data, null, 2)}</pre>`);

6

 })

7

 .catch(error => {

8

 res.send(error);

9

 });

Now if we visit our /login endpoint again, we'll see that the JSON data we receive from Spotify includes a new access_token.

Implementing the Authorization Code Flow - Build a Spotify Connected App (14)

Awesome! That's all we need to have set up for now. In the next module, we'll learn how to use the access and refresh tokens on the front end and make requests to the Spotify API in a React app.

Lesson recap#

That was a ton to take in for one lesson, so let's sum up what we've learned. From the perspective of our app, there were three main steps in the Spotify authorization code OAuth flow:

  1. Request authorization from Spotify (using the /login route handler)

  2. Use the authorization code to request an access token from Spotify (using the /callback route handler)

  3. Use the access token to request data from the Spotify API

There was also a bonus step, where we used the refresh token to request another access token from Spotify in the case that our access token expires.

We accomplished implementing this flow by setting up multiple route handlers in our Express app to handle sending requests to the Spotify Accounts Service. We also employed the help of the Axios library to easily construct HTTP requests.

Here's a diagram of the authorization code flow to help you visualize the back and forth between our app and Spotify:

Implementing the Authorization Code Flow - Build a Spotify Connected App (15)

Sources#

Learning Checkpoint#

Awesome job making it to the end of Module 2! That was a ton of information to take in. Feel free to go back over these last few lessons to make sure everything makes sense in your head.

In this module, we've covered...

  • How to set up an app in the Spotify developer dashboard

  • What OAuth is and how it works

  • The basics of Spotify’s different authorization flows

  • How to implement Spotify's Authorization Code Flow with Express route handlers

    (Video) How to Authenticate and use Spotify Web API

In the next module, we'll be setting up a React app for our front end. We'll learn how to to pass the access token we've acquired from the server to the front end so we can fetch data from the Spotify API through our React app.

Videos

1. Web API Kit: OAuth - Connecting to Spotify
(haptixgames)
2. How to set up OAuth on Bubble using Spotify Part 2 (via API Connector) [API Connector Tutorial 5/7]
(Airdev)
3. How to Use Spotify's API with Python | Write a Program to Display Artist, Tracks, and More
(Linode)
4. Automatically Refresh OAuth2.0 Access Tokens | Postman Level Up
(Postman)
5. Spotify Client Credentials Tutorial
({Exception Thrown})
6. Using the Spotify Web API with Python
(Andrew Golightly)
Top Articles
Latest Posts
Article information

Author: Prof. An Powlowski

Last Updated: 19/04/2023

Views: 5863

Rating: 4.3 / 5 (64 voted)

Reviews: 87% of readers found this page helpful

Author information

Name: Prof. An Powlowski

Birthday: 1992-09-29

Address: Apt. 994 8891 Orval Hill, Brittnyburgh, AZ 41023-0398

Phone: +26417467956738

Job: District Marketing Strategist

Hobby: Embroidery, Bodybuilding, Motor sports, Amateur radio, Wood carving, Whittling, Air sports

Introduction: My name is Prof. An Powlowski, I am a charming, helpful, attractive, good, graceful, thoughtful, vast person who loves writing and wants to share my knowledge and understanding with you.