From 1c48876015e07a124fd177d756f9644234e65bda Mon Sep 17 00:00:00 2001 From: jperez Date: Mon, 30 Jun 2014 10:11:04 +0200 Subject: [PATCH 1/2] Add state generation and check --- authorization_code/app.js | 111 ++++++++++++++++++--------- authorization_code/public/index.html | 76 +++++++++--------- implicit_grant/public/index.html | 97 ++++++++++++++--------- package.json | 5 +- 4 files changed, 179 insertions(+), 110 deletions(-) diff --git a/authorization_code/app.js b/authorization_code/app.js index 86ca512..4f72903 100644 --- a/authorization_code/app.js +++ b/authorization_code/app.js @@ -10,17 +10,39 @@ var express = require('express'); // Express web server framework var request = require('request'); // "Request" library var querystring = require('querystring'); +var cookieParser = require('cookie-parser'); var client_id = '03ffe0cac0a0401aa6673c3cf6d02ced'; // Your client id var client_secret = 'a57c43efb9644574a96d6623fb8bfbc2'; // Your client secret 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(); -app.use(express.static(__dirname + '/public')); +app.use(express.static(__dirname + '/public')) + .use(cookieParser()); app.get('/login', function(req, res) { + var state = generateRandomString(16); + res.cookie(stateKey, state); + // your application requests authorization var scope = 'user-read-private user-read-email'; res.redirect('https://accounts.spotify.com/authorize?' + @@ -28,51 +50,70 @@ app.get('/login', function(req, res) { response_type: 'code', client_id: client_id, scope: scope, - redirect_uri: redirect_uri + redirect_uri: redirect_uri, + state: state })); }); app.get('/callback', function(req, res) { // your application requests refresh and access tokens - var code = req.query.code; - 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 - }; + // after checking the state parameter - request.post(authOptions, function(error, response, body) { - if (!error && response.statusCode === 200) { + var code = req.query.code || null; + var state = req.query.state || null; + var storedState = req.cookies ? req.cookies[stateKey] : null; - var access_token = body.access_token, - refresh_token = body.refresh_token; + if (state === null || state !== storedState) { + 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 = { - url: 'https://api.spotify.com/v1/me', - headers: { 'Authorization': 'Bearer ' + access_token }, - json: true - }; + request.post(authOptions, function(error, response, body) { + if (!error && response.statusCode === 200) { - // use the access token to access the Spotify Web API - request.get(options, function(error, response, body) { - console.log(body); - }); + var access_token = body.access_token, + refresh_token = body.refresh_token; - // 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 - })); - } - }); + var options = { + url: 'https://api.spotify.com/v1/me', + headers: { 'Authorization': 'Bearer ' + access_token }, + json: true + }; + + // 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) { diff --git a/authorization_code/public/index.html b/authorization_code/public/index.html index 7523ca2..608e6c7 100644 --- a/authorization_code/public/index.html +++ b/authorization_code/public/index.html @@ -89,46 +89,50 @@ var params = getHashParams(); var access_token = params.access_token - refresh_token = params.refresh_token; + refresh_token = params.refresh_token, + error = params.error; - oauthPlaceholder.innerHTML = oauthTemplate({ - access_token: access_token, - 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(); - } - }); + if (error) { + alert('There was an error during the authentication'); } 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 - }); + 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); + } })(); diff --git a/implicit_grant/public/index.html b/implicit_grant/public/index.html index 718bafc..b275305 100644 --- a/implicit_grant/public/index.html +++ b/implicit_grant/public/index.html @@ -62,6 +62,8 @@ diff --git a/package.json b/package.json index 7067828..056a120 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,9 @@ "version": "0.0.1", "main": "app.js", "dependencies": { + "cookie-parser": "1.3.2", "express": "~4.0.0", - "request": "~2.34.0", - "querystring": "~0.2.0" + "querystring": "~0.2.0", + "request": "~2.34.0" } } From 2d9c7ac1f76a6c34322734f15b890cfbec451a63 Mon Sep 17 00:00:00 2001 From: jperez Date: Tue, 1 Jul 2014 09:48:10 +0200 Subject: [PATCH 2/2] Moved rendering of OAuth info inside the condition for access token --- authorization_code/public/index.html | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/authorization_code/public/index.html b/authorization_code/public/index.html index 608e6c7..7b65c5a 100644 --- a/authorization_code/public/index.html +++ b/authorization_code/public/index.html @@ -95,12 +95,13 @@ if (error) { alert('There was an error during the authentication'); } else { - oauthPlaceholder.innerHTML = oauthTemplate({ - access_token: access_token, - refresh_token: refresh_token - }); - if (access_token) { + // render oauth info + oauthPlaceholder.innerHTML = oauthTemplate({ + access_token: access_token, + refresh_token: refresh_token + }); + $.ajax({ url: 'https://api.spotify.com/v1/me', headers: { @@ -114,6 +115,7 @@ } }); } else { + // render initial screen $('#login').show(); $('#loggedin').hide(); }