Skip to content

Instantly share code, notes, and snippets.

@ktcy
Last active July 10, 2019 23:52
Show Gist options
  • Save ktcy/7b131c1126c99dacc3596f54e08bebef to your computer and use it in GitHub Desktop.
Save ktcy/7b131c1126c99dacc3596f54e08bebef to your computer and use it in GitHub Desktop.
Spotify API Authorization
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Example of the Authorization Code flow with Spotify</title>
<style type="text/css">
#login, #loggedin {
display: none;
}
.text-overflow {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 500px;
}
</style>
</head>
<body>
<div class="container">
<div id="login">
<h1>This is an example of the Authorization Code flow</h1>
<a href="/login">Log in with Spotify</a>
</div>
<div id="loggedin">
<div id="user-profile">
</div>
<div id="oauth">
</div>
<button id="obtain-new-token">Obtain new token using the refresh token</button>
</div>
</div>
<script>
(function() {
function renderUserProfile({
display_name,
images,
id,
email,
external_urls,
href,
country
}) {
return `<h1>Logged in as ${display_name}</h1>
<div>
<div><img width="150" src="${images[0].url}"></div>
<div>
<dl>
<dt>Display name</dt><dd>${display_name}</dd>
<dt>Id</dt><dd>${id}</dd>
<dt>Email</dt><dd>${email}</dd>
<dt>Spotify URI</dt><dd><a href="${external_urls.spotify}">${external_urls.spotify}</a></dd>
<dt>Link</dt><dd><a href="${href}">${href}</a></dd>
<dt>Profile Image</dt><dd><a href="${images[0].url}">${images[0].url}</a></dd>
<dt>Country</dt><dd>${country}</dd>
</dl>
</div>
</div>`;
}
function renderOAuth({access_token, refresh_token}) {
return `<h2>OAuth Info</h2>
<dl>
<dt>Access token</dt><dd class="text-overflow">${access_token}</dd>
<dt>Refresh token</dt><dd class="text-overflow">${refresh_token}</dd>
</dl>`;
}
const loginBlock = document.getElementById('login');
const loggedInBlock = document.getElementById('loggedin')
const userProfilePlaceholder = document.getElementById('user-profile');
const oauthPlaceholder = document.getElementById('oauth');
const hashParams = new URLSearchParams(window.location.hash.substring(1));
let access_token = hashParams.get('access_token'),
refresh_token = hashParams.get('refresh_token'),
error = hashParams.get('error');
if (error) {
alert('There was an error during the authentication');
} else {
if (access_token) {
oauthPlaceholder.innerHTML = renderOAuth({access_token, refresh_token});
const headers = {
'Authorization': `Bearer ${access_token}`
};
fetch('https://api.spotify.com/v1/me', {headers})
.then(response => response.json())
.then(response => {
userProfilePlaceholder.innerHTML = renderUserProfile(response);
loginBlock.style.display = 'none';
loggedInBlock.style.display = 'unset';
});
} else {
loginBlock.style.display = 'unset';
loggedInBlock.style.display = 'none';
}
document.getElementById('obtain-new-token').addEventListener('click', () => {
const params = new URLSearchParams({refresh_token});
fetch(`/refresh_token?${params.toString()}`)
.then(response => response.json())
.then(response => {
access_token = response.access_token;
oauthPlaceholder.innerHTML = renderOAuth({access_token, refresh_token});
});
}, false);
}
})();
</script>
</body>
</html>
{
"private": true,
"dependencies": {
"cookie-parser": "^1.4.3",
"cors": "^2.8.4",
"express": "^4.16.3",
"request": "^2.87.0",
"request-promise-native": "^1.0.5"
}
}
const express = require('express');
const cors = require('cors');
const spotifyAuth = require('./spotify-auth');
const path = require('path');
const {URL} = require('url');
const client_id = '';
const client_secret = '';
const redirect_uri = 'http://localhost:8888/callback';
const port = new URL(redirect_uri).port;
const app = express();
app.use(express.static(path.join(__dirname, 'public')))
.use(cors())
.use(spotifyAuth({client_id, client_secret, redirect_uri}));
app.listen(port, () => console.log(`Listening on ${port}`));
const express = require('express');
const cookieParser = require('cookie-parser');
const request = require('request-promise-native');
const {URL, URLSearchParams} = require('url');
const SPOTIFY_AUTHORIZE_ENDPOINT = 'https://accounts.spotify.com/authorize';
const SPOTIFY_TOKEN_ENDPOINT = 'https://accounts.spotify.com/api/token';
const STATE_KEY = 'spotify_auth_state';
module.exports = function(options = {}) {
const {client_id, client_secret, redirect_uri} = options;
const credentials = new Buffer(`${client_id}:${client_secret}`).toString('base64');
const requestToken = function(form) {
return request.post({
url: SPOTIFY_TOKEN_ENDPOINT,
headers: {'Authorization': `Basic ${credentials}`},
json: true,
form
});
};
const router = express.Router();
router.get('/login', (req, res) => {
const url = new URL(SPOTIFY_AUTHORIZE_ENDPOINT);
const params = new URLSearchParams();
const state = generateRandomString(16);
params.append('client_id', client_id);
params.append('redirect_uri', redirect_uri);
params.append('response_type', 'code');
params.append('scope', 'user-read-private user-read-email');
params.append('state', state);
url.search = params.toString();
res.cookie(STATE_KEY, state);
res.redirect(url);
});
router.get('/callback', cookieParser(), async (req, res) => {
const params = new URLSearchParams();
const {code, state} = req.query;
const storedState = req.cookies[STATE_KEY];
if (!storedState || storedState !== state) {
params.append('error', 'state_mismatch');
} else {
res.clearCookie(STATE_KEY);
try {
const response = await requestToken({
grant_type: 'authorization_code', code, redirect_uri
});
const {access_token, refresh_token} = response;
params.append('access_token', access_token);
params.append('refresh_token', refresh_token);
} catch (error) {
params.append('error', 'invalid_token');
}
}
res.redirect(`/#${params}`);
});
router.get('/refresh_token', async (req, res) => {
const {refresh_token} = req.query;
let access_token;
try {
const response = await requestToken({grant_type: 'refresh_token', refresh_token});
access_token = response.access_token;
} catch (error) {
access_token = null;
}
res.send({access_token});
});
return router;
}
function generateRandomString(length) {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < length; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment