raywo/MMM-NowPlayingOnSpotify

View on GitHub
authorization/app.js

Summary

Maintainability
A
0 mins
Test Coverage
'use strict';


/**
 * This is an example of a basic node.js script that performs
 * the Authorization Code oAuth2 flow to authenticate against
 * the Spotify Accounts.
 *
 * For more information, read
 * https://developer.spotify.com/web-api/authorization-guide/#authorization_code_flow
 */
const express = require('express'); // Express web server framework
const request = require('request'); // 'Request' library
const querystring = require('querystring');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');

const port = 8888;
let client_id = '';
let client_secret = '';
let redirect_uri = '';


/**
 * Generates a random string containing numbers and letters
 * @param  {number} length The length of the string
 * @return {string} The generated string
 */
function generateRandomString(length) {
  let text = '';
  const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';

  for (let i = 0; i < length; i++) {
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  }
  return text;
}


function getError() {
  let error = '';

  if (!client_id) {
    error = 'clientID';
  }

  if (!client_secret) {
    error += ', clientSecret';
  }
  return error;
}


function redirectToError(response, error) {
  let url = '/#';
  let urlParams = {
    error: error,
    clientID: client_id,
    clientSecret: client_secret
  };

  response.redirect(url + querystring.stringify(urlParams));
}


function redirectToAuthorization(response, state) {
  // your application requests authorization
  const scope = 'user-read-playback-state user-read-currently-playing';
  let url = 'https://accounts.spotify.com/authorize?';
  let urlParams = {
    response_type: 'code',
    client_id: client_id,
    scope: scope,
    redirect_uri: redirect_uri,
    state: state
  };

  response.redirect(url + querystring.stringify(urlParams));
}


function redirectToSuccess(response, body) {
  let access_token = body.access_token;
  let refresh_token = body.refresh_token;

  // we can also pass the token to the browser to make requests from there
  response.redirect('/#' +
    querystring.stringify({
      access_token: access_token,
      refresh_token: refresh_token,
      client_id: client_id,
      client_secret: client_secret
    }));
}


function getAuthOptions(code) {
  return {
    url: 'https://accounts.spotify.com/api/token',
    form: {
      code: code,
      redirect_uri: redirect_uri,
      grant_type: 'authorization_code'
    },
    headers: {
      'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
    },
    json: true
  };
}


let stateKey = 'spotify_auth_state';
let app = express();

app.use(express.static(__dirname + '/public'));
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));


app.post('/login', function (req, response) {
  let state = generateRandomString(16);
  response.cookie(stateKey, state);

  client_id = req.body.clientID;
  client_secret = req.body.clientSecret;
  redirect_uri = req.body.redirectURI;

  let error = getError();

  if (error) {
    redirectToError(response, error);
  } else {
    redirectToAuthorization(response, state);
  }
});


app.get('/callback', function (req, res) {
  // your application requests refresh and access tokens
  // after checking the state parameter
  let code = req.query.code || null;
  let state = req.query.state || null;
  let storedState = req.cookies ? req.cookies[stateKey] : null;

  if (state === null || state !== storedState) {
    redirectToError(res, 'state_mismatch');

  } else {
    res.clearCookie(stateKey);

    request.post(getAuthOptions(code), function (error, response, body) {
      if (!error && response.statusCode === 200) {
        redirectToSuccess(res, body);
      } else {
        redirectToError(res, 'invalid_token');
      }
    });
  }
});


console.log('Listening on %s', port);
console.log('You can now open your browser and visit localhost:%s', port);
app.listen(port);