How to Implement OAuth Flow in Express and Node.js

Build out Spotify's Authorization Code OAuth flow with Express

Project Source Code

Get the project source code below, and follow along with the lesson material.

Download Project Source Code

To set up the project on your local machine, please follow the directions provided in the README.md file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.

This lesson preview is part of the Build a Spotify Connected App course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.

This video is available to students only
Unlock This Course

Get unlimited access to Build a Spotify Connected App, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

Thumbnail for the \newline course Build a Spotify Connected App
  • [00:00 - 00:18] Now that we have a high level understanding of what OAuth is and how it works, let's take a closer look at how OAuth works with the Spotify API. So according to Spotify's authorization guide, there are four possible flows for obtaining app authorization.

    [00:19 - 00:32] First, there's the authorization code flow, then there's authorization code with PKCE or ProofKey for code exchange. There's client credentials and there's implicit grant.

    [00:33 - 00:45] Each of these flows provides a slightly different level of authorization due to the way it's granted. For example, the implicit grant flow can be implemented entirely client-side, but it doesn't provide a refresh token.

    [00:46 - 01:00] The client credentials flow is used for server-to-server authentication, but authorization doesn't grant permission to access user resources. They all follow the OAuth flow we learned in the last lesson, but each has its own variation.

    [01:01 - 01:18] 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 key, and provides an access token that can be refreshed. The ability to refresh an access token is a big advantage.

    [01:19 - 01:29] Users of our app will only need to grant permission once. Next, we're going to be going step-by-step through how to build out this authorization code flow with Node and Express.

    [01:30 - 01:51] Note that we'll be heavily referring to this authorization code flow guide in Spotify's documentation, and we'll also be pulling from some of their web API auth examples, which is available on GitHub. Before we make 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.

    [01:52 - 02:04] Once we've located these two values, we can add them to an environment file at the root of our project. So let's add a .m file.

    [02:05 - 02:28] And in this file, I'll create a client ID, based in that value, and a client secret. We'll be using this .m file to store sensitive information in other environment variables for our app.

    [02:29 - 02:42] 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 that our values are kept private. So let's add this .m file to our gitignore file.

    [02:43 - 03:00] Finally, we'll also create a .m.example file, which we won't get ignored. And in this file, we'll just replace our secret values with placeholder values.

    [03:01 - 03:10] Since our .m file should be kept secret, it's good practice to add an example . m file to your code base for others, or future you to reference.

    [03:11 - 03:22] Now that we've stored our app's client ID and secret in a .m file, we need a way to make our code aware of them. To do that, we'll be using an mpm module called .m.

    [03:23 - 03:36] .m lets us load environment variables from a .m file into process.m and object containing the user environment. In our terminal, let's install the .m module.

    [03:37 - 03:53] We'll say mpm install.m. Then at the very top of our index.js file, we'll add this line.

    [03:54 - 04:12] To make sure it's working, we can console log the value of one of our environment variables, like client ID. Now when we run mpm start in our terminal, we should see the client ID value printed.

    [04:13 - 04:25] Note that since node.js is server side, console.log shows up in the terminal, not the browser console. The last thing we need to do to prepare our environment is to set up our redirect URI.

    [04:26 - 04:39] The redirect URI is a route of our app that we want the Spotify account service to redirect the user to once they've authorized our app. So in our case, the redirect URI will be /callback.

    [04:40 - 04:50] We'll set up this route in our index.js file later. Back in the Spotify developer dashboard, go to your app and click edit settings .

    [04:51 - 05:02] As you can see here, I've added HTTP localhost 8888/callback as a redirect URI. Add that URL to your redirect URI's here and click add.

    [05:03 - 05:19] And then make sure you scroll down and hit save. Once the redirect URL has been added to your Spotify app in the dashboard, we can go back and add that as an environment variable in our .m file.

    [05:20 - 05:30] Make sure to add it to your .mv.example as well. In this case, since our localhost URL isn't sensitive, we can just leave it here.

    [05:31 - 05:54] 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 file for convenience. Awesome.

    [05:55 - 06:06] Now our development environment is all ready to go. Okay, so step one of the authorization code flow is to request authorization from Spotify.

    [06:07 - 06:22] In code terms, this means sending a GET request to the Spotify account service "authorize endpoint". 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.

    [06:23 - 06:33] Let's set up a route handler for the login endpoint in our index.js file. We can get rid of this awesome generator route handler and replace it with app.

    [06:34 - 06:56] get login. And we'll just send a string back for now. To make sure the route handler is working, we can visit the login route in our localhost.

    [06:57 - 07:08] Next, we want to set up our login route to hit the Spotify account service " authorize endpoint". To do that, we'll replace our res.send with a res.redirect.

    [07:09 - 07:24] We'll grab the URL here and put it in here. Now, when we refresh the login page, we'll be automatically redirected to the Spotify account service URL.

    [07:25 - 07:36] However, as you can see, there is an error saying there's a missing required parameter of client_id. This is because the authorized endpoint has required query parameters that we failed to include.

    [07:37 - 07:55] The three required query parameters on the authorized endpoint are client_id, response type, and redirect URI. There are also optional query parameters for things such as authorization sc opes and security that will eventually include.

    [07:56 - 08:05] Let's modify our res.redirect to include the required query parameters with template strings. So we'll change our single quotes to backticks.

    [08:06 - 08:16] And then after the slash authorized path, we'll add a question mark and then add our key value pairs. So we have client_id, response type, and redirect URI.

    [08:17 - 08:34] We also make sure to separate those key value pairs with ampersands. Give that a save, and now if we visit our localhost 8888/login, we'll be redirected to the Spotify account's login page.

    [08:35 - 08:49] Make sure you're not already logged in to Spotify on the web, because sometimes it will skip the screen. Once we log in with a username or password, or Facebook or Google or something like that, we'll hit login.

    [08:50 - 09:02] Unfortunately, since our app doesn't have a slash callback route handler yet, we'll see an error. But regardless, we can tell our request was successful because there is a code query parameter in the URL.

    [09:03 - 09:19] The value of the code query parameter is an authorization code that we'll be able to use to exchange for an access token. Before we exchange the authorization code for an access token, let's refactor our existing code to make handling query parameters easier and less error prone .

    [09:20 - 09:28] There's a built-in node module called query string that lets us parse and stringify query strings. Let's import it at the top of our index.js file.

    [09:29 - 09:59] The query string does stringify method, takes an object with keys and values, and serializes them into a query string. Let's update our login route handler to use that.

    [10:00 - 10:22] Now we can just append our query params variable to the end of our authorized endpoint. Now that we have an easier way of handling query params in our HTTP request, let's add the optional query params on the authorized endpoint that we didn't include before.

    [10:23 - 10:40] First, above the login route handler, we'll add this utility function called generate random string. Then below it, we'll add a state key variable called spotify-off-state.

    [10:41 - 11:00] Then at the top of our login route handler, we'll say const state equals generate random string, and we'll get a string of a length of 16. Then we'll set a cookie, so we'll say res.cookie, state key, and state.

    [11:01 - 11:17] All we're doing is setting a cookie with this spotify-off-state key, and our state is this random string. Then we can add that state query param to our object.

    [11:18 - 11:39] We can also add a scope query param, and we can declare that variable up here, and we'll just pass it user, read private, and user, read email first. Oop, scope notscape, and we'll add that to our query params as well.

    [11:40 - 11:54] These two scopes will let us access details about the currently logged in users account and their email. Great, now let's use the authorization code we received from the authorized endpoint to request an access token from spotify.

    [11:55 - 12:06] So this is step two in the OAuth flow, using the authorization code to request an access token. So currently, we have an error because we haven't created our callback route handler yet.

    [12:07 - 12:23] Let's stub it out. Great, so now if you look at the authorization guide, it says we have to send a post request to this /api/token endpoint.

    [12:24 - 12:43] Similar to the authorized endpoint, there are three query parameters required for this endpoint, grant type, code, and redirect URI. And when sent along in the body of our post request, they have to be encoded in the application /xwww.form.url encoded format.

    [12:44 - 13:00] The token endpoint also has a required header parameter authorization, which is a base64 encoded string containing the client ID and client secret key. Now, let's set up our post request to the token endpoint in our callback route handler.

    [13:01 - 13:12] Although it's possible to send a post request with no-ins 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.

    [13:13 - 13:22] Other than being easy to use, Axios also works both client-side and server-side . Let's install Axios as a dependency.

    [13:23 - 13:42] Then we'll require it at the top of our index.js file. Now, let's set up our post request in our callback route handler.

    [13:43 - 13:57] Let's break down this chunk of code a bit. So first, we store a code variable, which is the value of the authorization code we have on the query parameter.

    [13:58 - 14:09] So to access the code query parameter on our request, we can just use rec.query .code. And if it doesn't exist, we'll just default to null.

    [14:10 - 14:31] Next, we set up the post request to the /api/token endpoint on the Spotify server by passing a config object to the Axios function. In the data object, we use query.stringify to format the three required body params, the grant type, code, and redirect URI params.

    [14:32 - 14:42] The code variable we stored up here is also part of those query params. We also set up two request headers here, content type and authorization.

    [14:43 - 15:02] Since Axios is a promise-based library, we chain a then and a catch callback function to handle resolving the promise Axios returns. If our request is successful, meaning it returns a 200 status code with a response, the then callback will be invoked.

    [15:03 - 15:19] And here we return a stringified response.data object from the Axios response. It's important to note here that Axios stores the data returned by a request in a data property of the response object, not the response object itself.

    [15:20 - 15:50] 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. Now, if we start our server again with npm start, and we visit our login, route handler, you should see that the JSON data returned by the /api/token endpoint on the Spotify server included an access token, which we can use to request data from the Spotify API.

    [15:51 - 16:09] Now we can move on to step three, using the access token to request data from the Spotify API. Let's modify our then callback to send a request to the Spotify API's /me endpoint.

    [16:10 - 16:30] Notice that we're destructuring our access token and token type variables to pass into our authorization header. Now, when we visit our login route again, our callback route will display with the user profile JSON data returned from the /me endpoint.

    [16:31 - 16:45] Awesome, this opens up a bunch of new doors for us to interact with the Spotify API in interesting ways. 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.

    [16:46 - 17:05] If we take another look at the data returned by Spotify /api/token endpoint, you'll see that in addition to access token, there are also token type, scope, expires in, and refresh token properties. The expires in value is a number of seconds that the access token is valid.

    [17:06 - 17:15] This means after 3600 seconds, the access token will expire. Once the token is expired, there are two things that could happen.

    [17:16 - 17:32] One, we forced the user to login again, or two, we use the refresh token to retrieve another access token behind the scenes, not requiring the user to login again. The better user experience is definitely the latter, so let's make sure our app has a way to handle that.

    [17:33 - 17:43] So in our index.js file, we'll add another route handler. This will be a route handler for a get request to /refresh_token.

    [17:44 - 18:09] And in here, we'll use Axios again to send a post request to the Spotify /api/ token endpoint that we did in our callback handler. Except in here, instead of passing things like grant type, code, and redirect URI, we're going to pass grant type and refresh token.

    [18:10 - 18:27] To test this refresh token 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 /me endpoint in our callback route handler with a request to our local refresh token endpoint.

    [18:28 - 18:43] Now, if we visit our login route again, we'll see that the JSON data we receive from Spotify includes a new access token. Awesome, that's all we need to have set up for now.

    [18:44 - 18:55] Okay, 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.

    [18:56 - 19:09] First, we requested authorization from Spotify using the login route handler. Second, we used the authorization code to request an access token from Spotify using the callback route.

    [19:10 - 19:24] And third, we used 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 case our access token expires.

    [19:25 - 19:39] We accomplished implementing this flow by setting up multiple route handlers in our express app to handle sending requests to the Spotify account service. We also employed the help of the Axios library to easily construct HTTP requests.

    [19:40 - 19:51] Here's a diagram of the authorization code flow to help you visualize the back and forth between our app and Spotify. In the next module, we'll be setting up a React app for our front end.

    [19:52 - 19:59] We'll learn how 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.