Add state generation and check
This commit is contained in:
parent
90d6d902ce
commit
1c48876015
4 changed files with 179 additions and 110 deletions
|
@ -10,17 +10,39 @@
|
||||||
var express = require('express'); // Express web server framework
|
var express = require('express'); // Express web server framework
|
||||||
var request = require('request'); // "Request" library
|
var request = require('request'); // "Request" library
|
||||||
var querystring = require('querystring');
|
var querystring = require('querystring');
|
||||||
|
var cookieParser = require('cookie-parser');
|
||||||
|
|
||||||
var client_id = '03ffe0cac0a0401aa6673c3cf6d02ced'; // Your client id
|
var client_id = '03ffe0cac0a0401aa6673c3cf6d02ced'; // Your client id
|
||||||
var client_secret = 'a57c43efb9644574a96d6623fb8bfbc2'; // Your client secret
|
var client_secret = 'a57c43efb9644574a96d6623fb8bfbc2'; // Your client secret
|
||||||
var redirect_uri = 'http://localhost:8888/callback'; // Your redirect uri
|
var redirect_uri = 'http://localhost:8888/callback'; // Your redirect uri
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random string containing numbers and letters
|
||||||
|
* @param {number} length The length of the string
|
||||||
|
* @return {string} The generated string
|
||||||
|
*/
|
||||||
|
var generateRandomString = function(length) {
|
||||||
|
var text = '';
|
||||||
|
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
var stateKey = 'spotify_auth_state';
|
||||||
|
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
app.use(express.static(__dirname + '/public'));
|
app.use(express.static(__dirname + '/public'))
|
||||||
|
.use(cookieParser());
|
||||||
|
|
||||||
app.get('/login', function(req, res) {
|
app.get('/login', function(req, res) {
|
||||||
|
|
||||||
|
var state = generateRandomString(16);
|
||||||
|
res.cookie(stateKey, state);
|
||||||
|
|
||||||
// your application requests authorization
|
// your application requests authorization
|
||||||
var scope = 'user-read-private user-read-email';
|
var scope = 'user-read-private user-read-email';
|
||||||
res.redirect('https://accounts.spotify.com/authorize?' +
|
res.redirect('https://accounts.spotify.com/authorize?' +
|
||||||
|
@ -28,51 +50,70 @@ app.get('/login', function(req, res) {
|
||||||
response_type: 'code',
|
response_type: 'code',
|
||||||
client_id: client_id,
|
client_id: client_id,
|
||||||
scope: scope,
|
scope: scope,
|
||||||
redirect_uri: redirect_uri
|
redirect_uri: redirect_uri,
|
||||||
|
state: state
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/callback', function(req, res) {
|
app.get('/callback', function(req, res) {
|
||||||
|
|
||||||
// your application requests refresh and access tokens
|
// your application requests refresh and access tokens
|
||||||
var code = req.query.code;
|
// after checking the state parameter
|
||||||
var authOptions = {
|
|
||||||
url: 'https://accounts.spotify.com/api/token',
|
|
||||||
form: {
|
|
||||||
code: code,
|
|
||||||
redirect_uri: redirect_uri,
|
|
||||||
grant_type: 'authorization_code',
|
|
||||||
client_id: client_id,
|
|
||||||
client_secret: client_secret
|
|
||||||
},
|
|
||||||
json: true
|
|
||||||
};
|
|
||||||
|
|
||||||
request.post(authOptions, function(error, response, body) {
|
var code = req.query.code || null;
|
||||||
if (!error && response.statusCode === 200) {
|
var state = req.query.state || null;
|
||||||
|
var storedState = req.cookies ? req.cookies[stateKey] : null;
|
||||||
|
|
||||||
var access_token = body.access_token,
|
if (state === null || state !== storedState) {
|
||||||
refresh_token = body.refresh_token;
|
res.redirect('/#' +
|
||||||
|
querystring.stringify({
|
||||||
|
error: 'state_mismatch'
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
res.clearCookie(stateKey);
|
||||||
|
var authOptions = {
|
||||||
|
url: 'https://accounts.spotify.com/api/token',
|
||||||
|
form: {
|
||||||
|
code: code,
|
||||||
|
redirect_uri: redirect_uri,
|
||||||
|
grant_type: 'authorization_code',
|
||||||
|
client_id: client_id,
|
||||||
|
client_secret: client_secret
|
||||||
|
},
|
||||||
|
json: true
|
||||||
|
};
|
||||||
|
|
||||||
var options = {
|
request.post(authOptions, function(error, response, body) {
|
||||||
url: 'https://api.spotify.com/v1/me',
|
if (!error && response.statusCode === 200) {
|
||||||
headers: { 'Authorization': 'Bearer ' + access_token },
|
|
||||||
json: true
|
|
||||||
};
|
|
||||||
|
|
||||||
// use the access token to access the Spotify Web API
|
var access_token = body.access_token,
|
||||||
request.get(options, function(error, response, body) {
|
refresh_token = body.refresh_token;
|
||||||
console.log(body);
|
|
||||||
});
|
|
||||||
|
|
||||||
// we can also pass the token to the browser to make requests from there
|
var options = {
|
||||||
res.redirect('/#' +
|
url: 'https://api.spotify.com/v1/me',
|
||||||
querystring.stringify({
|
headers: { 'Authorization': 'Bearer ' + access_token },
|
||||||
access_token: access_token,
|
json: true
|
||||||
refresh_token: refresh_token
|
};
|
||||||
}));
|
|
||||||
}
|
// use the access token to access the Spotify Web API
|
||||||
});
|
request.get(options, function(error, response, body) {
|
||||||
|
console.log(body);
|
||||||
|
});
|
||||||
|
|
||||||
|
// we can also pass the token to the browser to make requests from there
|
||||||
|
res.redirect('/#' +
|
||||||
|
querystring.stringify({
|
||||||
|
access_token: access_token,
|
||||||
|
refresh_token: refresh_token
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
res.redirect('/#' +
|
||||||
|
querystring.stringify({
|
||||||
|
error: 'invalid_token'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/refresh_token', function(req, res) {
|
app.get('/refresh_token', function(req, res) {
|
||||||
|
|
|
@ -89,46 +89,50 @@
|
||||||
var params = getHashParams();
|
var params = getHashParams();
|
||||||
|
|
||||||
var access_token = params.access_token
|
var access_token = params.access_token
|
||||||
refresh_token = params.refresh_token;
|
refresh_token = params.refresh_token,
|
||||||
|
error = params.error;
|
||||||
|
|
||||||
oauthPlaceholder.innerHTML = oauthTemplate({
|
if (error) {
|
||||||
access_token: access_token,
|
alert('There was an error during the authentication');
|
||||||
refresh_token: refresh_token
|
|
||||||
});
|
|
||||||
|
|
||||||
if (access_token) {
|
|
||||||
$.ajax({
|
|
||||||
url: 'https://api.spotify.com/v1/me',
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + access_token
|
|
||||||
},
|
|
||||||
success: function(response) {
|
|
||||||
userProfilePlaceholder.innerHTML = userProfileTemplate(response);
|
|
||||||
|
|
||||||
$('#login').hide();
|
|
||||||
$('#loggedin').show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
$('#login').show();
|
oauthPlaceholder.innerHTML = oauthTemplate({
|
||||||
$('#loggedin').hide();
|
access_token: access_token,
|
||||||
}
|
refresh_token: refresh_token
|
||||||
|
|
||||||
document.getElementById('obtain-new-token').addEventListener('click', function() {
|
|
||||||
$.ajax({
|
|
||||||
url: '/refresh_token',
|
|
||||||
data: {
|
|
||||||
'refresh_token': refresh_token
|
|
||||||
}
|
|
||||||
}).done(function(data) {
|
|
||||||
access_token = data.access_token;
|
|
||||||
oauthPlaceholder.innerHTML = oauthTemplate({
|
|
||||||
access_token: access_token,
|
|
||||||
refresh_token: refresh_token
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}, false);
|
|
||||||
|
|
||||||
|
if (access_token) {
|
||||||
|
$.ajax({
|
||||||
|
url: 'https://api.spotify.com/v1/me',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + access_token
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
userProfilePlaceholder.innerHTML = userProfileTemplate(response);
|
||||||
|
|
||||||
|
$('#login').hide();
|
||||||
|
$('#loggedin').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('#login').show();
|
||||||
|
$('#loggedin').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('obtain-new-token').addEventListener('click', function() {
|
||||||
|
$.ajax({
|
||||||
|
url: '/refresh_token',
|
||||||
|
data: {
|
||||||
|
'refresh_token': refresh_token
|
||||||
|
}
|
||||||
|
}).done(function(data) {
|
||||||
|
access_token = data.access_token;
|
||||||
|
oauthPlaceholder.innerHTML = oauthTemplate({
|
||||||
|
access_token: access_token,
|
||||||
|
refresh_token: refresh_token
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -62,6 +62,8 @@
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
|
|
||||||
|
var stateKey = 'spotify_auth_state';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains parameters from the hash of the URL
|
* Obtains parameters from the hash of the URL
|
||||||
* @return Object
|
* @return Object
|
||||||
|
@ -76,6 +78,21 @@
|
||||||
return hashParams;
|
return hashParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random string containing numbers and letters
|
||||||
|
* @param {number} length The length of the string
|
||||||
|
* @return {string} The generated string
|
||||||
|
*/
|
||||||
|
function generateRandomString(length) {
|
||||||
|
var text = '';
|
||||||
|
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
var userProfileSource = document.getElementById('user-profile-template').innerHTML,
|
var userProfileSource = document.getElementById('user-profile-template').innerHTML,
|
||||||
userProfileTemplate = Handlebars.compile(userProfileSource),
|
userProfileTemplate = Handlebars.compile(userProfileSource),
|
||||||
userProfilePlaceholder = document.getElementById('user-profile');
|
userProfilePlaceholder = document.getElementById('user-profile');
|
||||||
|
@ -86,46 +103,52 @@
|
||||||
|
|
||||||
var params = getHashParams();
|
var params = getHashParams();
|
||||||
|
|
||||||
var access_token = params.access_token;
|
var access_token = params.access_token,
|
||||||
|
state = params.state,
|
||||||
|
storedState = localStorage.getItem(stateKey);
|
||||||
|
|
||||||
oauthPlaceholder.innerHTML = oauthTemplate({
|
if (access_token && (state == null || state !== storedState)) {
|
||||||
access_token: access_token
|
alert('There was an error during the authentication');
|
||||||
});
|
|
||||||
|
|
||||||
if (access_token) {
|
|
||||||
$.ajax({
|
|
||||||
url: 'https://api.spotify.com/v1/me',
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + access_token
|
|
||||||
},
|
|
||||||
success: function(response) {
|
|
||||||
userProfilePlaceholder.innerHTML = userProfileTemplate(response);
|
|
||||||
|
|
||||||
$('#login').hide();
|
|
||||||
$('#loggedin').show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
$('#login').show();
|
localStorage.removeItem(stateKey);
|
||||||
$('#loggedin').hide();
|
if (access_token) {
|
||||||
|
$.ajax({
|
||||||
|
url: 'https://api.spotify.com/v1/me',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + access_token
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
userProfilePlaceholder.innerHTML = userProfileTemplate(response);
|
||||||
|
|
||||||
|
$('#login').hide();
|
||||||
|
$('#loggedin').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$('#login').show();
|
||||||
|
$('#loggedin').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('login-button').addEventListener('click', function() {
|
||||||
|
|
||||||
|
var client_id = '03ffe0cac0a0401aa6673c3cf6d02ced'; // Your client id
|
||||||
|
var redirect_uri = 'http://localhost:8888/'; // Your redirect uri
|
||||||
|
|
||||||
|
var state = generateRandomString(16);
|
||||||
|
|
||||||
|
localStorage.setItem(stateKey, state);
|
||||||
|
var scope = 'user-read-private user-read-email';
|
||||||
|
|
||||||
|
var url = 'https://accounts.spotify.com/authorize';
|
||||||
|
url += '?response_type=token';
|
||||||
|
url += '&client_id=' + encodeURIComponent(client_id);
|
||||||
|
url += '&scope=' + encodeURIComponent(scope);
|
||||||
|
url += '&redirect_uri=' + encodeURIComponent(redirect_uri);
|
||||||
|
url += '&state=' + encodeURIComponent(state);
|
||||||
|
|
||||||
|
window.location = url;
|
||||||
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('login-button').addEventListener('click', function() {
|
|
||||||
|
|
||||||
var client_id = '03ffe0cac0a0401aa6673c3cf6d02ced'; // Your client id
|
|
||||||
var redirect_uri = 'http://localhost:8888/'; // Your redirect uri
|
|
||||||
|
|
||||||
var scope = 'user-read-private user-read-email';
|
|
||||||
|
|
||||||
var url = 'https://accounts.spotify.com/authorize';
|
|
||||||
url += '?response_type=token';
|
|
||||||
url += '&client_id=' + encodeURIComponent(client_id);
|
|
||||||
url += '&scope=' + encodeURIComponent(scope);
|
|
||||||
url += '&redirect_uri=' + encodeURIComponent(redirect_uri);
|
|
||||||
|
|
||||||
window.location = url;
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"cookie-parser": "1.3.2",
|
||||||
"express": "~4.0.0",
|
"express": "~4.0.0",
|
||||||
"request": "~2.34.0",
|
"querystring": "~0.2.0",
|
||||||
"querystring": "~0.2.0"
|
"request": "~2.34.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue