Spotify backend server
This commit is contained in:
parent
e8d0a25fc9
commit
3802bce17d
6 changed files with 220 additions and 14 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -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
25
package-lock.json
generated
|
@ -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",
|
||||||
|
|
|
@ -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
149
server.js
Normal 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);
|
31
src/App.js
31
src/App.js
|
@ -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
|
||||||
|
|
21
src/views/ArtistSearch.jsx
Normal file
21
src/views/ArtistSearch.jsx
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue