Add state generation and check

This commit is contained in:
jperez 2014-06-30 10:11:04 +02:00
parent 90d6d902ce
commit 1c48876015
4 changed files with 179 additions and 110 deletions

View file

@ -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,14 +50,27 @@ 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 code = req.query.code || null;
var state = req.query.state || null;
var storedState = req.cookies ? req.cookies[stateKey] : null;
if (state === null || state !== storedState) {
res.redirect('/#' +
querystring.stringify({
error: 'state_mismatch'
}));
} else {
res.clearCookie(stateKey);
var authOptions = { var authOptions = {
url: 'https://accounts.spotify.com/api/token', url: 'https://accounts.spotify.com/api/token',
form: { form: {
@ -71,8 +106,14 @@ app.get('/callback', function(req, res) {
access_token: access_token, access_token: access_token,
refresh_token: refresh_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) {

View file

@ -89,8 +89,12 @@
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;
if (error) {
alert('There was an error during the authentication');
} else {
oauthPlaceholder.innerHTML = oauthTemplate({ oauthPlaceholder.innerHTML = oauthTemplate({
access_token: access_token, access_token: access_token,
refresh_token: refresh_token refresh_token: refresh_token
@ -128,7 +132,7 @@
}); });
}); });
}, false); }, false);
}
})(); })();
</script> </script>
</html> </html>

View file

@ -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,12 +103,14 @@
var params = getHashParams(); var params = getHashParams();
var access_token = params.access_token; var access_token = params.access_token,
state = params.state,
oauthPlaceholder.innerHTML = oauthTemplate({ storedState = localStorage.getItem(stateKey);
access_token: access_token
});
if (access_token && (state == null || state !== storedState)) {
alert('There was an error during the authentication');
} else {
localStorage.removeItem(stateKey);
if (access_token) { if (access_token) {
$.ajax({ $.ajax({
url: 'https://api.spotify.com/v1/me', url: 'https://api.spotify.com/v1/me',
@ -115,6 +134,9 @@
var client_id = '03ffe0cac0a0401aa6673c3cf6d02ced'; // Your client id var client_id = '03ffe0cac0a0401aa6673c3cf6d02ced'; // Your client id
var redirect_uri = 'http://localhost:8888/'; // Your redirect uri 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 scope = 'user-read-private user-read-email';
var url = 'https://accounts.spotify.com/authorize'; var url = 'https://accounts.spotify.com/authorize';
@ -122,10 +144,11 @@
url += '&client_id=' + encodeURIComponent(client_id); url += '&client_id=' + encodeURIComponent(client_id);
url += '&scope=' + encodeURIComponent(scope); url += '&scope=' + encodeURIComponent(scope);
url += '&redirect_uri=' + encodeURIComponent(redirect_uri); url += '&redirect_uri=' + encodeURIComponent(redirect_uri);
url += '&state=' + encodeURIComponent(state);
window.location = url; window.location = url;
}, false); }, false);
}
})(); })();
</script> </script>
</html> </html>

View file

@ -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"
} }
} }