Spotify backend server

This commit is contained in:
Rodrigo Pinto 2019-12-13 15:53:32 -05:00
commit 3802bce17d
6 changed files with 220 additions and 14 deletions

1
.gitignore vendored
View file

@ -22,6 +22,7 @@
.env.development.local .env.development.local
.env.test.local .env.test.local
.env.production.local .env.production.local
/config
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*

25
package-lock.json generated
View file

@ -3801,6 +3801,22 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
}, },
"cookie-parser": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.4.tgz",
"integrity": "sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw==",
"requires": {
"cookie": "0.3.1",
"cookie-signature": "1.0.6"
},
"dependencies": {
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
}
}
},
"cookie-signature": { "cookie-signature": {
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@ -3848,6 +3864,15 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
}, },
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"cosmiconfig": { "cosmiconfig": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",

View file

@ -6,9 +6,14 @@
"@testing-library/jest-dom": "^4.2.4", "@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.3", "@testing-library/react": "^9.3.3",
"@testing-library/user-event": "^7.1.2", "@testing-library/user-event": "^7.1.2",
"cookie-parser": "^1.4.4",
"cors": "^2.8.5",
"express": "^4.17.1",
"querystring": "^0.2.0",
"react": "^16.12.0", "react": "^16.12.0",
"react-dom": "^16.12.0", "react-dom": "^16.12.0",
"react-scripts": "3.3.0" "react-scripts": "3.3.0",
"request": "^2.88.0"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",

149
server.js Normal file
View file

@ -0,0 +1,149 @@
/**
* 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 cors = require('cors');
const querystring = require('querystring');
const cookieParser = require('cookie-parser');
const env = require('./config/config.json')
const client_id = env.client_id
const client_secret = env.client_secret
const redirect_uri = env.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'))
.use(cors())
.use(cookieParser());
app.get('/login', function(req, res) {
console.log('Login')
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?' +
querystring.stringify({
response_type: 'code',
client_id: client_id,
scope: scope,
redirect_uri: redirect_uri,
state: state
}));
});
app.get('/callback', function(req, res) {
// your application requests refresh and access tokens
// 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('http://localhost:3000/#' +
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'
},
headers: {
'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64'))
},
json: true
};
request.post(authOptions, function(error, response, body) {
if (!error && response.statusCode === 200) {
var access_token = body.access_token,
refresh_token = body.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('http://localhost:3000?' +
querystring.stringify({
access_token: access_token,
refresh_token: refresh_token
}));
} else {
res.redirect('http://localhost:3000/#' +
querystring.stringify({
error: 'invalid_token'
}));
}
});
}
});
app.get('/refresh_token', function(req, res) {
// requesting access token from refresh token
var refresh_token = req.query.refresh_token;
var authOptions = {
url: 'https://accounts.spotify.com/api/token',
headers: { 'Authorization': 'Basic ' + (new Buffer(client_id + ':' + client_secret).toString('base64')) },
form: {
grant_type: 'refresh_token',
refresh_token: refresh_token
},
json: true
};
request.post(authOptions, function(error, response, body) {
if (!error && response.statusCode === 200) {
var access_token = body.access_token;
res.send({
'access_token': access_token
});
}
});
});
console.log('Listening on 8888');
app.listen(8888);

View file

@ -1,26 +1,31 @@
import React from 'react'; import React, { useState, useEffect } from 'react'
import logo from './logo.svg'; import logo from './logo.svg'
import './App.css'; import './App.css'
function App() { function App() {
let [token, setToken] = useState(null)
useEffect(() => {
const search = window.location.search
const params = new URLSearchParams(search)
setToken(params.get('access_token'))
}, [])
return ( return (
<div className="App"> <div className="App">
<header className="App-header"> <header className="App-header">
<img src={logo} className="App-logo" alt="logo" /> <img src={logo} className="App-logo" alt="logo" />
<p> <p>
Edit <code>src/App.js</code> and save to reload. Edit <code>src/App.js</code> and save to reload.
</p> </p>
<a
className="App-link" <a href='http://localhost:8888/login' className='App-link'> Login to Spotify </a>
href="https://reactjs.org"
target="_blank" <p>{token}</p>
rel="noopener noreferrer"
>
Learn React
</a>
</header> </header>
</div> </div>
); )
} }
export default App; export default App

View file

@ -0,0 +1,21 @@
import React, { useState, useEffect } from 'react'
function ArtistSearch() {
let [token, setToken] = useState(null)
useEffect(() => {
const search = window.location.search
const params = new URLSearchParams(search)
setToken(params.get('access_token'))
}, [])
return (
<div className="artist-search">
<h1>Artist search</h1>
<p>{token}</p>
</div>
)
}
export default ArtistSearch