Skip to content

Commit

Permalink
Better refreshing of accessTokens
Browse files Browse the repository at this point in the history
When making a lot of consecutive search requests the accessToken might
expire. Now the application refreshes the accessToken when this happens
while searching.
  • Loading branch information
crohrer committed Jun 10, 2017
1 parent 86dce02 commit b1ec450
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 129 deletions.
117 changes: 67 additions & 50 deletions spotifyOAuth.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ var http = require('http');
var https = require('https');
var fs = require('fs');
var url = require('url') ;
var Promise = require('bluebird');
var logger = require('./logger');
var querystring = require('querystring');
var config = JSON.parse(fs.readFileSync('config.json', 'utf8'));
var server = http.createServer(handleRequest);
Expand All @@ -30,14 +32,16 @@ var spotifyOAuth = {
});
},
refresh: function(){
try {
console.log('refreshing token...');
var refreshToken = fs.readFileSync('refreshToken', 'utf8');
getToken(undefined, refreshToken);
} catch (e){
console.log('no refreshToken found');
spotifyOAuth.authenticate();
}
return new Promise(resolve => {
try {
console.log('refreshing token...');
let refreshToken = fs.readFileSync('refreshToken', 'utf8');
resolve(getToken(undefined, refreshToken));
} catch (e){
logger.log('no refreshToken found');
spotifyOAuth.authenticate();
}
});
},
getAccessToken: function(){
try {
Expand All @@ -47,7 +51,9 @@ var spotifyOAuth = {
return spotifyOAuth.accessToken;
} catch (e){
console.log('no accessToken found');
spotifyOAuth.refresh();
spotifyOAuth.refresh()
.then(require('./main').start)
.catch(error => logger.log(error, 'spotifyOAuth.getAccessToken'));
}
return false;
}
Expand All @@ -63,58 +69,64 @@ function handleRequest(request, response){
response.end('You can close this Window now.');

if(queryObject.code){ // this is the authorization code
getToken(queryObject.code);
server.close();
getToken(queryObject.code)
.then(server.close)
.catch(error => logger.log(error));
}
});
}

/**
* one of the params is required!
* @param code authCode or undefined
* @param refresh refreshToken or undefined
* @param {String|undefined} code authCode or undefined
* @param {String|undefined} refresh refreshToken or undefined
* @returns {Promise}
*/
function getToken(code, refresh){
var jsonData;
if(refresh){
jsonData = {
grant_type: "refresh_token",
refresh_token: refresh
};
} else {
jsonData = {
grant_type: "authorization_code",
code: code,
redirect_uri: REDIRECT_URI
};
}
var idAndSecret = config.clientId+':'+config.clientSecret;
var authString = 'Basic ' + new Buffer(idAndSecret).toString('base64');
var data = querystring.stringify(jsonData);
var tokenReq = https.request({
hostname: 'accounts.spotify.com',
path: '/api/token',
method: 'POST',
headers: {
'Authorization': authString,
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(data)
return new Promise((resolve, reject) => {
var jsonData;
if(refresh){
jsonData = {
grant_type: "refresh_token",
refresh_token: refresh
};
} else {
jsonData = {
grant_type: "authorization_code",
code: code,
redirect_uri: REDIRECT_URI
};
}
}, function(res){
var body = '';
res.on('data', function(chunk){
body += new Buffer(chunk).toString();
var idAndSecret = config.clientId+':'+config.clientSecret;
var authString = 'Basic ' + new Buffer(idAndSecret).toString('base64');
var data = querystring.stringify(jsonData);
var tokenReq = https.request({
hostname: 'accounts.spotify.com',
path: '/api/token',
method: 'POST',
headers: {
'Authorization': authString,
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(data)
}
}, function(res){
var body = '';
res.on('data', function(chunk){
body += new Buffer(chunk).toString();
});
res.on('end', function(){
var responseJson = JSON.parse(body);
writeAccessToken(responseJson.access_token);
writeRefreshToken(responseJson.refresh_token);
spotifyOAuth.accessToken = responseJson.access_token.replace(/\s/g,'');
resolve(spotifyOAuth.accessToken);
});
});
res.on('end', function(){
var responseJson = JSON.parse(body);
writeAccessToken(responseJson.access_token);
writeRefreshToken(responseJson.refresh_token);
spotifyOAuth.accessToken = responseJson.access_token.replace(/\s/g,'');
require('./main').start();
tokenReq.on('error', () => {
reject('Error Refreshing Auth Token');
});
tokenReq.end(data);
});

tokenReq.end(data);
}

function writeAccessToken(token){
Expand All @@ -125,10 +137,15 @@ function writeRefreshToken(token){
writeFile('refreshToken', token);
}

/**
* writes file in fs
* @param {String} name
* @param {String} content
*/
function writeFile(name, content){
if(content){
fs.writeFileSync(name, content);
console.log(name + ' saved!');
logger.log(name + ' saved!');
}
}

Expand Down
38 changes: 24 additions & 14 deletions spotifyPlaylist.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ function getTracks(playlistName, offset){
spotifyHelper.checkForRateLimit(res, 'requesting playlist tracks', () => spotifyPlaylist.getTracks(playlistName, offset))
.then(() => {
if(res.statusCode === 401){
spotifyOAuth.refresh();
spotifyOAuth.refresh()
.then(require('./main').start);
} else {
var error = "Error getting tracks from playlist. Status "+res.statusCode;
logger.log(error, playlistName);
Expand Down Expand Up @@ -117,35 +118,44 @@ function addTracks(playlistName, results){
return Promise.all(requests);

function makeAddRequest(items, timeout){
return new Promise(resolve => {
setTimeout(() => {
var addRequest = https.request({
return new Promise((resolve, reject) => {
let tryCounter = 0; // count how often this was tried to prevent endless loop
setTimeout(sendRequest, timeout);

function sendRequest(){
if(tryCounter > 5){
return reject('request to add to playlist failed 5 times');
}
tryCounter += 1;

let addRequest = https.request({
hostname: 'api.spotify.com',
path: '/v1/users/'+config.userId+'/playlists/'+playlistConfig.playlistId+'/tracks?position=0&uris='+items.join(), // join all 40 track ids for the query string
path: '/v1/users/' + config.userId + '/playlists/' + playlistConfig.playlistId + '/tracks?position=0&uris=' + items.join(), // join all 40 track ids for the query string
method: 'POST',
headers: {
'Authorization': 'Bearer '+ accessToken,
'Authorization': 'Bearer ' + accessToken,
'Accept': 'application/json'
}
}, function(res){
if(res.statusCode === 201){
logger.log('Success! Added '+ items.length + ' tracks.', playlistName);
if (res.statusCode === 201) {
logger.log('Success! Added ' + items.length + ' tracks.', playlistName);
resolve();
return;
} else {
spotifyHelper.checkForRateLimit(res, 'adding to playlist', () => resolve(spotifyPlaylist.addTracks(playlistName, results)))
.then(() => {
if(res.statusCode === 401){
spotifyOAuth.refresh();
.then(() =>{
if (res.statusCode === 401) {
spotifyOAuth.refresh()
.then(sendRequest) // try again
.catch(error => logger.log(error, playlistName));
} else {
logger.log("Error adding to playlist. Status "+res.statusCode, playlistName);
logger.log("Error adding to playlist. Status " + res.statusCode, playlistName);
process.exit(1);
}
});
}
});
addRequest.end();
}, timeout);
}
});
}
}
Expand Down
Loading

0 comments on commit b1ec450

Please sign in to comment.