Building an Ionic Spotify App - Part 1: OAuth Last update: 2018-05-29

Building an Ionic Spotify App - Part 1: OAuth

After a recent comment under a video of my YouTube channel I was challenged to build an app with Spotify integration - and the process was everything but easy.

Years ago I already wrote about the flow for an Ionic v1 App but many things have changed since then including my understanding of the OAuth login flow. Therefore this 2 part series will help you to build a proper login flow using Spotify OAuth with your own little server and finally Ionic Spotify app!

Also, the previous article was using free 30 second snippets rather than the full power of the Spotify API (due to the used login back then)!

Part 2: Spotify API

This article is inspired by the great work of cordova-spotify-oauth and their implementation plus we’ll also use the mentioned plugin which needs a little server to work correctly. At the end of this part we’ll have a fully working setup and Spotify access token so we can build the actual functionality of the app against the Spotify API in the second part.

Spotify App Setup

We begin our journey by creating a new Spotify app inside their developer dashboard. Go ahead and create an account there if you don’t already have and then hit the ”Create a Client ID” button to create a new app.

Fill out the mandatory fields and save the app. Then navigate into the app and select ”Edit Settings” and set add a redirect URI like this ”devdacticspotify://callback

This URI is needed when the login flow returns back to your app and will open the app using a custom URL scheme. The scheme I picked is devdacticspotify and we’ll talk more about the custom scheme later when we build the Ionic app, for now just keep in mind that you might want to change that URI to match your company/ apps name a bit more!

So if you pick a different one (what I recommend yo do) make sure you remember it later in all the spots where I use this URI.

Spotify OAuth Server Prerequisite

The OAuth login is not super easy and especially if you want a robust setup including access and refresh tokens you might need your own server. There’s already one server inside the cordova-spotify-oauth repo but it’s for AWS hosting and I also wanted to get a better understanding of what’s happening so I rebuilt it using NodeJS as we can then deploy it to Heroku in seconds.

Therefore, the next step is to go into your Heroku account (create if you don’t have, it’s free) and hit ”New -> Create new app“.

Enter the information as you wish and then head over to your command line as we will use the Heroku CLI to connect our local server with the created app.

Go ahead and run these commands in an empty folder of your choice:

# Answer the question with enter to set up a new package.json
npm init

# Install dependencies
npm i body-parser cors crypto-js dotenv express http request

# Start git for Heroku
git init
heroku git:remote -a yourherokuappname

First, go through all the questions of the npm init and simply hit enter. Then, install a bunch of dependencies that we need for our server.

Finally, initialise a Git repository in your folder and add your Heroku app as a remote to that repository. Of course use the name of your Heroku app in here so you can later deploy your app with one command!

To tell Heroku what should be started, created a new file, call it Procfile and simply insert:

web: node index.js

Awesome, now we need some environment variables and the easiest way to add them is using the CLI again, so run these commands but replace it with your values:

heroku config:set CLIENT_CALLBACK_URL=devdacticspotify://callback
heroku config:set CLIENT_ID=your-spotify-app-id
heroku config:set CLIENT_SECRET=your-spotify-app-secret
heroku config:set ENCRYPTION_SECRET=my-secret!

By setting them here they will be secretly available on Heroku to our app as well and we don’t have to define them anywhere else. You can find the Spotify ID and secret inside your previously created app, the callback URI is what you specified and the encryption key can be whatever you want, it’s simply a key to make sure the communication of the refresh token is secure. This token could be used to obtain new access keys and it’s kinda bad if this token get’s lost in the wild, so do your best to protect it.

Building the OAuth Server

Now we can finally get to the actual coding after a lot of setup. Our NodeJS server has 2 routes:

  • /exchange: Get a new access token after login
  • /refresh: Get a new access token using a refresh token

But before we get to this, open a new file index.js and insert the general server bootstrapping stuff:

var
  cors = require('cors'),
  http = require('http'),
  express = require('express'),
  dotenv = require('dotenv'),
  bodyParser = require('body-parser'),
  request = require('request'),
  CryptoJS = require('crypto-js');

var app = express();
dotenv.load();

const API_URL = "https://accounts.spotify.com/api/token";
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const CLIENT_CALLBACK_URL = process.env.CLIENT_CALLBACK_URL;
const ENCRYPTION_SECRET = process.env.ENCRYPTION_SECRET;

app.use(bodyParser.urlencoded({
  extended: true
}));
app.use(bodyParser.json());
app.use(cors({
  origin: true,
  credentials: true
}));

We need to load our environment variables and also use the API url for the Spotify token Endpoint as specified in their documentation.

To make a request to the Spotify servers we need to add the headers and parameters in a special format, therefore we add a new function spotifyRequest that our two following routes can use. This function will make an appropriate request and then return the result in a meaningful way to the caller, so add this to your index.js:

const spotifyRequest = params => {
  return new Promise((resolve, reject) => {
      request.post(API_URL, {
        form: params,
        headers: {
          "Authorization": "Basic " + new Buffer(CLIENT_ID + ":" + CLIENT_SECRET).toString('base64')
        },
        json: true
      }, (err, resp) => err ? reject(err) : resolve(resp));
    })
    .then(resp => {
      if (resp.statusCode != 200) {
        return Promise.reject({
          statusCode: resp.statusCode,
          body: resp.body
        });
      }
      return Promise.resolve(resp.body);
    })
    .catch(err => {
      return Promise.reject({
        statusCode: 500,
        body: JSON.stringify({})
      });
    });
};

Now we can also add the request which is called after our app user went through the OAuth login inside the app. This route returns an object with access_token, expires_in and refresh_token if everything went well. Here we’ll also encrypt the refresh token as discussed earlier, the functions for this will follow inside the last snippet.

The only interesting thing in here is the information we pass to our previous spotifyRequest function, especially that we are using the grant type authorization_code which is the best way to interact with the Spotify API later on. The authorization code flow gives us full access to the user and the API but it’s also the trickiest to implement. But we are here to learn, right?

The second route will be called if the user is working with our app again and the access token stored in the app is expired. The plugin (which we use later) will then automatically make a call to the /refresh route to exchange the refresh token for a new access token!

This is the standard OAuth flow, and I guess this server could be build into a nice little OAuth helper package as well for other services if anyone is interested in doing this ;)

Now go ahead and add the new route below your previous code inside the index.js:

// Route to obtain a new Token
app.post('/exchange', (req, res) => {

  const params = req.body;
  if (!params.code) {
    return res.json({
      "error": "Parameter missing"
    });
  }

  spotifyRequest({
      grant_type: "authorization_code",
      redirect_uri: CLIENT_CALLBACK_URL,
      code: params.code
    })
    .then(session => {
      let result = {
        "access_token": session.access_token,
        "expires_in": session.expires_in,
        "refresh_token": encrypt(session.refresh_token)
      };
      return res.send(result);
    })
    .catch(response => {
      return res.json(response);
    });
});

// Get a new access token from a refresh token
app.post('/refresh', (req, res) => {
  const params = req.body;
  if (!params.refresh_token) {
    return res.json({
      "error": "Parameter missing"
    });
  }

  spotifyRequest({
      grant_type: "refresh_token",
      refresh_token: decrypt(params.refresh_token)
    })
    .then(session => {
      return res.send({
          "access_token": session.access_token,
          "expires_in": session.expires_in
      });
    })
    .catch(response => {
      return res.json(response);
    });
});

These are the only 2 routes our app needs for the login. But of course we need some helper functions and make sure to start the server. It’s important to use the PORT from the environment as otherwise your app will result in a crash once you deploy it to Heroku!

Finish your server by adding this to the index.js:

// Helper functions
function encrypt(text) {
  return CryptoJS.AES.encrypt(text, ENCRYPTION_SECRET).toString();
};

function decrypt(text) {
  var bytes = CryptoJS.AES.decrypt(text, ENCRYPTION_SECRET);
  return bytes.toString(CryptoJS.enc.Utf8);
};

// Start the server
var server = http.createServer(app);

server.listen(process.env.PORT || 5000, function (err) {
  console.info('listening in http://localhost:8080');
});

In the beginning you’ve already (hopefully) created the Git repository as described, so now you only need to add all your files, commit them and then push the new code to the Heroku remote which will trigger a build for your app:

ngit add .
git commit -am 'My Heroku deployment commit message.'
git push heroku master

Done!

Your app is now deployed to Heroku and you can find the URL for the app either in the CLI output or inside your Heroku Dashboard. The login might work locally as well but we need the app on a device anyway so it’s a lot easier to have this server available from anywhere without the stress of setting up a local testing environment.

Also, testing the 2 routes is not working well as we need a code from Spotify that is only returned after a user went through the authorization dialog of the app, so let’s better finish this tutorial with the real Ionic app to make some progress!

The Ionic App Setup

We start like always with a blank Ionic app and add the before mentioned oauth plugin which will take care of showing the Spotify login plus a plugin to set a custom URL scheme for our app.

Remember, you don’t have to use exactly my wording in here but make sure it’s the same you used in the beginning or your login will never return to your app and you’ll spend hours debugging (I already did that for your..):

ionic start devdacticSpotify blank
cd devdacticSpotify
ionic cordova plugin add cordova-spotify-oauth
ionic cordova plugin add cordova-plugin-customurlscheme --variable URL_SCHEME=devdacticspotify

With our new app we now just need to create a little configuration and then call our Cordova plugin, so let’s add the code to the pages/home/home.ts like this before we go into details:

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

declare var cordova: any;

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  result = {};

  constructor(public navCtrl: NavController) { }

  authWithSpotify() {
    const config = {
      clientId: "your-spotify-client-id",
      redirectUrl: "devdacticspotify://callback",
      scopes: ["streaming", "playlist-read-private", "user-read-email", "user-read-private"],
      tokenExchangeUrl: "https://spotifyoauthserver.herokuapp.com/exchange",
      tokenRefreshUrl: "https://spotifyoauthserver.herokuapp.com/refresh",
    };

    cordova.plugins.spotifyAuth.authorize(config)
      .then(({ accessToken, encryptedRefreshToken, expiresAt }) => {
        this.result = { access_token: accessToken, expires_in: expiresAt, ref: encryptedRefreshToken };
      });
  }
}

To login with Spotify we need the Client ID of our Spotify App we created in the first step. Then we need the callback URL that we’ve added to that app and set inside our server as well.

The scopes variable is an array of elements you can pick from the scopes documentation, this basically defines what resources our app can access later and the user will also see a list of these permissions and hopefully accept what we request.

Finally, the URLs for the tokens are the URLs of your Heroku app so make sure to copy the right values and set them so the Cordova plugin automatically knows which routes to call for the tokens!

To make this app run, simply add a button to the view that calls the authWithSpotify function but I think if you’ve followed this tutorial you’ll know how to do it.

If you want to test the app, make sure to deploy it to your device as it’s not going to work from within your browser! Then you can log out the result after the login and hopefully you’ll app will have an access token at that point!

What’s next

We’ve only built the basics for our Ionic Spotify app but we’re on a pretty good way. Right now we got a full-blown OAuth setup with our own server and token exchange/refresh mechanism in place. In the next part we’ll then use our scopes and tokens to make requests against the Spotify API to build our own Ionic Spotify client!

You can also find a vide version of this article below.

https://youtu.be/_Cic2D3-gjM