Spotify Token Swap Service

by yuhyunch

GitHub Readme.md

Spotify Logo

Token Swap Service for Spotify 🔑 ⛓

Built by Spotify Build Status VersionEye Minimum Ruby Version - 2.3.6

This is a tiny Ruby service for supporting Authorization Code Flow on Spotify integrations with:

  • iOS Apps
  • Android Apps
  • Static Web Apps

Contents 📖

Intro

When should I use Authorization Code Flow instead of Implicit Grant Flow?

  • You don't want users to have to re-authenticate every 60 minutes.
  • You don't want to insecurely expose your client secret to third parties.

Read more about token swapping on Spotify for Developers.

Install

One-click with Heroku

Just click that button below. Fill in the form and it'll work like magic. ✨

Deploy

Manual Install

Install the project locally:

$ git clone https://github.com/bih/spotify-token-swap-service.git
$ cd spotify-token-swap-service/
$ bundle install

Then to run the server:

$ cp .sample.env .env
$ vim .env
$ rackup

How It Works

When authenticating users with your Spotify application, you can authenticate them through two ways: Implicit Grant Flow and Authorization Code Flow.

Implicit Grant Flow

You don't need to setup this service, and you can close your window.

The Implicit Grant Flow returns an access_token directly back to your application once the user has authorized your application. It expires in 60 minutes, after which the user has to re-authorize your application.

Authorization Code Flow

Recommended

The Authorization Code Flow returns a code directly back to your application once the user has authorized your application. This code can be exchanged for an access_token through the Spotify Accounts API.

This could be performed directly inside of your iOS, Android, or static web apps and will work as intended - but it is extremely insecure as it exposes your client secret to the world. This should never be done for production apps, ever.

The right way is to handle the "exchange" on a server and have your application call that server. This would securely store your client secret away from developers who might reverse engineer your iOS, Android, or static web apps. This repository contains said simple exchange server.

Configuration

There are several environment variables you'll need to set:

Environment Variable Description Required SPOTIFY_CLIENT_ID A valid client ID from Spotify for Developers. Required ✅ SPOTIFY_CLIENT_SECRET A valid client secret from Spotify for Developers. Required ✅ SPOTIFY_CLIENT_CALLBACK_URL A registered callback from Spotify for Developers. Required ✅ ENCRYPTION_SECRET A random "salt" for securing your refresh token. Grab one here. Optional ╳

As mentioned in Manual Install, these are all outlined in .sample.env which you can move over to .env and modify with your respective credentials.

API

POST /api/token

Request (cURL)

$ curl -X POST -d "code=[code]" https://yourapp.herokuapp.com/api/token

Request (CLI)

$ bin/token "[code]"

JSON Response

{
  "access_token":
    "BQDjrNCJ66N1utnFnpgcPZy8yD8KSsGN_zC1qP6jg1xeWfCl_slv8LGig_ia8bHynxFuSs-PvmHp-_6U13cBPR8469s66KmWxxdOsHCN00Gg5AgX3wyZYJLX0V-HqiXqCNdzDVShlzFaPEHJbKbm73TWJDHTG4c",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token":
    "p7jJ+3agZ8m9aBMZdiTq85wqNIl16ctbMgCPFOlRBanVgB+kht2hDmrCDL5V\nvRFQS9s1vBsWkpBCC0kbA6srol8NrKaHzY1tNrvDRFoN7xumQId8agd6Tqs6\nM8ypEhvTDElFbt1cMxd+N3z0JG3gSmOPk2h8/idwVBub0cqyCSacf4GPpnwW\nCg==\n",
  "scope": "user-read-private"
}

POST /api/refresh_token

Request (cURL)

$ curl -X POST -d "refresh_token=[refresh token]" https://yourapp.herokuapp.com/api/refresh_token

Request (CLI)

$ bin/refresh_token "[refresh token]"

JSON Response

{
  "access_token":
    "BQCjHuWkG2pSAFaa7-zQJQWjylilINTpUbfRbRgJtAMJrBF9h3vg-N6bnaG9XCKYE8ceAsGgTGwbeO8MfbZKlYbyHG4B7EOeIUlTo0wn08PgkQZGjBzMYQwzNwr_pmel4pCgKOiEyH9Zc8L6iss3anLSSg6IWag",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "user-read-private"
}

CLI

We have two binaries included, which allows us to test our credentials easily. Before running these commands, make sure you have run the following:

$ git clone https://github.com/bih/spotify-token-swap-service.git
$ cd spotify-token-swap-service/
$ bundle install
$ cp .sample.env .env
$ vim .env

bin/token

$ bin/token "[code]"

bin/refresh_token

$ bin/refresh_token "[refresh token]"

Code Samples

Objective-C with Spotify iOS SDK

// Swapping code for access_token
NSURL *swapServiceURL = [NSURL urlWithString:@"http://yourapp.herokuapp.com/api/token"];

[SPAuth handleAuthCallbackWithTriggeredAuthURL:url
        tokenSwapServiceEndpointAtURL:swapServiceURL
        callback:callback];

Swift

This is using the Alamofire Swift Framework.

import Alamofire

// Swapping code for access_token
Alamofire.request(.POST, "https://yourapp.herokuapp.com/api/token", ["code": "[code]"])

// Swapping refresh_token for access_token
Alamofire.request(.POST, "https://yourapp.herokuapp.com/api/refresh_token", ["refresh_token": "[refresh token]"])

Ruby

This is using the HTTParty gem.

require "httparty"

# Swapping code for access_token
HTTParty.post("https://yourapp.herokuapp.com/api/token", body: {
  code: "[code]"
}).parsed_response

# Swapping refresh_token for access_token
HTTParty.post("https://yourapp.herokuapp.com/api/refresh_token", body: {
  refresh_token: "[refresh token]"
}).parsed_response

JavaScript

// Swapping code for access_token
fetch("https://yourapp.herokuapp.com/api/token", {
  method: "POST",
  body: JSON.stringify({
    code: "[code]"
  })
}).then(res => res.json());

// Swapping refresh_token for access_token
fetch("https://yourapp.herokuapp.com/api/refresh_token", {
  method: "POST",
  body: JSON.stringify({
    refresh_token: "[refresh token]"
  })
}).then(res => res.json());

Error Handling

The Token Swap Service will either return an error from our server, or a forwarded error from the Spotify Accounts API.

Token Swap Service

It returns a JSON response with an error key, like as follows:

{ "error": "invalid refresh_token" }

See spotify_token_swap_service.rb for more information.

Spotify Accounts API

It will look something like this:

{
  "error": "invalid_grant",
  "error_description": "Invalid authorization code"
}

Read the Authorization Guide for more information.

Contributing

This project adheres to the Open Code of Conduct. By participating, you are expected to honor this code.

Clone the repository and make a new branch:

$ git clone https://github.com/bih/spotify-token-swap-service.git
$ cd spotify-token-swap-service/
$ git checkout -b new-feature-branch

Access the console:

$ bin/console

Run tests:

$ bundle exec rake spec

All of the main code exists inside of spotify_token_swap_service.rb.

Credits

This project was built from SpotifyTokenSwap by @simontaen in 2014, and the encryption of refresh tokens was taken from their work.