Implement User Authorization
Completed step 1 of the Authorization Code Flow documented by the Spotify API.
This commit is contained in:
@@ -37,6 +37,7 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'spotifyvis.apps.SpotifyvisConfig',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
astroid==1.6.3
|
astroid==1.6.3
|
||||||
|
certifi==2018.4.16
|
||||||
|
chardet==3.0.4
|
||||||
Django==2.0.5
|
Django==2.0.5
|
||||||
djangorestframework==3.8.2
|
djangorestframework==3.8.2
|
||||||
|
idna==2.6
|
||||||
isort==4.3.4
|
isort==4.3.4
|
||||||
lazy-object-proxy==1.3.1
|
lazy-object-proxy==1.3.1
|
||||||
mccabe==0.6.1
|
mccabe==0.6.1
|
||||||
pylint==1.8.4
|
pylint==1.8.4
|
||||||
pytz==2018.4
|
pytz==2018.4
|
||||||
|
requests==2.18.4
|
||||||
six==1.11.0
|
six==1.11.0
|
||||||
|
urllib3==1.22
|
||||||
wrapt==1.10.11
|
wrapt==1.10.11
|
||||||
|
|||||||
132
spotifyvis/templates/spotifyvis/index.html
Normal file
132
spotifyvis/templates/spotifyvis/index.html
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Example of the Authorization Code flow with Spotify</title>
|
||||||
|
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
|
||||||
|
<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" class="btn btn-primary">Log in with Spotify</a>
|
||||||
|
</div>
|
||||||
|
<div id="loggedin">
|
||||||
|
<div id="user-profile">
|
||||||
|
</div>
|
||||||
|
<div id="oauth">
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-default" id="obtain-new-token">Obtain new token using the refresh token</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script id="user-profile-template" type="text/x-handlebars-template">
|
||||||
|
<h1>Logged in as {{display_name}}</h1>
|
||||||
|
<div class="media">
|
||||||
|
<div class="pull-left">
|
||||||
|
<img class="media-object" width="150" src="{{images.0.url}}" />
|
||||||
|
</div>
|
||||||
|
<div class="media-body">
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>Display name</dt><dd class="clearfix">{{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 class="clearfix"><a href="{{images.0.url}}">{{images.0.url}}</a></dd>
|
||||||
|
<dt>Country</dt><dd>{{country}}</dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="oauth-template" type="text/x-handlebars-template">
|
||||||
|
<h2>oAuth info</h2>
|
||||||
|
<dl class="dl-horizontal">
|
||||||
|
<dt>Access token</dt><dd class="text-overflow">{{access_token}}</dd>
|
||||||
|
<dt>Refresh token</dt><dd class="text-overflow">{{refresh_token}}</dd>
|
||||||
|
</dl>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0-alpha.1/handlebars.min.js"></script>
|
||||||
|
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
/**
|
||||||
|
* Obtains parameters from the hash of the URL
|
||||||
|
* @return Object
|
||||||
|
*/
|
||||||
|
function getHashParams() {
|
||||||
|
var hashParams = {};
|
||||||
|
var e, r = /([^&;=]+)=?([^&;]*)/g,
|
||||||
|
q = window.location.hash.substring(1);
|
||||||
|
while ( e = r.exec(q)) {
|
||||||
|
hashParams[e[1]] = decodeURIComponent(e[2]);
|
||||||
|
}
|
||||||
|
return hashParams;
|
||||||
|
}
|
||||||
|
var userProfileSource = document.getElementById('user-profile-template').innerHTML,
|
||||||
|
userProfileTemplate = Handlebars.compile(userProfileSource),
|
||||||
|
userProfilePlaceholder = document.getElementById('user-profile');
|
||||||
|
var oauthSource = document.getElementById('oauth-template').innerHTML,
|
||||||
|
oauthTemplate = Handlebars.compile(oauthSource),
|
||||||
|
oauthPlaceholder = document.getElementById('oauth');
|
||||||
|
var params = getHashParams();
|
||||||
|
var access_token = params.access_token,
|
||||||
|
refresh_token = params.refresh_token,
|
||||||
|
error = params.error;
|
||||||
|
if (error) {
|
||||||
|
alert('There was an error during the authentication');
|
||||||
|
} else {
|
||||||
|
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: {
|
||||||
|
'Authorization': 'Bearer ' + access_token
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
userProfilePlaceholder.innerHTML = userProfileTemplate(response);
|
||||||
|
$('#login').hide();
|
||||||
|
$('#loggedin').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// render initial screen
|
||||||
|
$('#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);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -3,4 +3,7 @@ from . import views
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.index, name='index'),
|
path('', views.index, name='index'),
|
||||||
|
path('login', views.login, name='login'),
|
||||||
|
path('callback', views.callback, name='callback'),
|
||||||
|
|
||||||
]
|
]
|
||||||
@@ -1,6 +1,52 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render, redirect
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import requests
|
||||||
|
import os
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
def generate_random_string(length):
|
||||||
|
"""Generates a random string of a certain length
|
||||||
|
|
||||||
|
Args:
|
||||||
|
length: the desired length of the randomized string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A random string
|
||||||
|
"""
|
||||||
|
rand_str = ""
|
||||||
|
possible_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
|
||||||
|
for _ in range(length):
|
||||||
|
rand_str += possible_chars[random.randint(0, len(possible_chars) - 1)]
|
||||||
|
|
||||||
|
return rand_str
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
def index(request):
|
def index(request):
|
||||||
return HttpResponse("You're at the index")
|
return render(request, 'spotifyvis/index.html')
|
||||||
|
|
||||||
|
|
||||||
|
def login(request):
|
||||||
|
|
||||||
|
state_str = generate_random_string(16)
|
||||||
|
# use a randomly generated state string to prevent cross-site request forgery attacks
|
||||||
|
request.session['state_string'] = state_str
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
'client_id': os.environ['SPOTIFY_CLIENT_ID'],
|
||||||
|
'response_type': 'code',
|
||||||
|
'redirect_uri': 'http://localhost:8000/callback',
|
||||||
|
'state': state_str,
|
||||||
|
'scope': 'user-library-read',
|
||||||
|
'show_dialog': False
|
||||||
|
}
|
||||||
|
|
||||||
|
params = urllib.parse.urlencode(payload) # turn the payload dict into a query string
|
||||||
|
authorize_url = "https://accounts.spotify.com/authorize/?{}".format(params)
|
||||||
|
return redirect(authorize_url)
|
||||||
|
|
||||||
|
def callback(request):
|
||||||
|
return HttpResponse("At callback")
|
||||||
Reference in New Issue
Block a user