#RailsとJsでSpotifyAPIをたたいてみる
###環境・前提条件
ruby 2.6.2
rails 5.2.4
###この記事に書いてあること
- ユーザーを認証し、ユーザーデータにアクセスするための承認を取得する
- Web APIエンドポイントからデータを取得する。
下記のURLはSpotifyAPIの公式ドキュメントです。公式ドキュメントとやっていることは同じですが、この記事ではわかりやすく進行していきます。
###参考URL
公式ドキュメント
https://developer.spotify.com/documentation/web-api/quick-start/
公式github
https://github.com/spotify/web-api-auth-examples
###この記事でできること
アプリのログイン→Spotifyログイン
###対象者
- Spotify APIをたたきたいけど、どうやってたたけばいいか分からないAPI初心者
- ドキュメント読むの難しいと感じている方
- Spotify会員
- deviseを使ってログイン機能が作成できる方
###今回取得するトークンについて❶
例えばWEB PLAYBACK SDKを使いたいと思ったとき、
下記のソースコードをコピペすればプレイヤーを使用できます。音も鳴るよ♬
ただ参考URLで取得したトークンだと1時間で切れてしまうので参考URLで取得したAccessToken
のところを1時間に一回手動で書き換えなくてはいけません。そんなの無理ー(⌒-⌒; )って感じですよね!
そこで登場するのが今回取得するトークンなんです。
<!DOCTYPE html>
<html>
<head>
<title>Spotify Web Playback SDK Quick Start Tutorial</title>
</head>
<body>
<h1>Spotify Web Playback SDK Quick Start Tutorial</h1>
<h2>Open your console log: <code>View > Developer > JavaScript Console</code></h2>
<script src="https://sdk.scdn.co/spotify-player.js"></script>
<script>
window.onSpotifyWebPlaybackSDKReady = () => {
const token = '参考URLで取得したAccessToken';
const player = new Spotify.Player({
name: 'Web Playback SDK Quick Start Player',
getOAuthToken: cb => { cb(token); }
});
// Error handling
player.addListener('initialization_error', ({ message }) => { console.error(message); });
player.addListener('authentication_error', ({ message }) => { console.error(message); });
player.addListener('account_error', ({ message }) => { console.error(message); });
player.addListener('playback_error', ({ message }) => { console.error(message); });
// Playback status updates
player.addListener('player_state_changed', state => { console.log(state); });
// Ready
player.addListener('ready', ({ device_id }) => {
console.log('Ready with Device ID', device_id);
});
// Not Ready
player.addListener('not_ready', ({ device_id }) => {
console.log('Device ID has gone offline', device_id);
});
// Connect to the player!
player.connect();
};
</script>
</body>
</html>
###今回取得するトークンについて❷
Spotifyが用意しているトークンの種類は3種類あります。
下記公式ドキュメントですので、目を通してみて下さい。
https://developer.spotify.com/documentation/general/guides/authorization-guide/
今回取得するものは上記のURLの一番下のものです。
Client Credentials Flowは時間が経つと切れてしましますが、手動でコードを直さなくてもいいような仕様となります。3種類の中でも一番簡単でお手軽ですね♬
###手順❶
Spotify API公式ドキュメントのDASHBOADからアプリを作成し、
ClientID,SecretIDを取得しましょう。
アプリを作成したらClientID,SecretIDを確認しましょう。
次に、ページ右にあるEDIT SETTINGからRedirect URIsを登録しよう。
###手順❷
deviseを使ってログイン画面を作る。
おなじみdevise先生です♬
gem 'devise'
$ bundle install
$ rails g devise:install
$ rails g devise User
$ rails db:migrate
<!DOCTYPE html>
<html>
<head>
<title>Authorization</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= include_gon %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<% if user_signed_in? %>
<ul class="navbar">
<li class="nav-item item">
<%= link_to "Log out", destroy_user_session_path, method: :delete, class:"nav-link" %>
</li>
</ul>
<% else %>
<ul class="navbar">
<li class="nav-item item">
<%= link_to "Sign up", new_user_registration_path, class:"nav-link" %>
</li>
<li class="nav-item item">
<%= link_to "Log in", new_user_session_path, class:"nav-link" %>
</li>
</ul>
<% end %>
<%= yield %>
</body>
</html>
class ApplicationController < ActionController::Base
def after_sign_in_path_for(resource)
if user_signed_in?
spotify_login_path
end
end
def after_sign_out_path_for(resource)
new_user_session_path
end
end
###手順❸
APIキーを管理しましょう。
gem 'gon'
gem 'dotenv-rails'
*補足:gonはrailsのコントローラーで指定した変数をJsに持っていける便利gemです♬
dotenv-railsは.envファイルを管理するgemです♬
$ bundle install
$ rails g controller users
class UsersController < ApplicationController
def spotify_login
gon.client_id = ENV['SPOTIFY_CLIENT_ID'];
gon.redirect_uri = ENV["SPOTIFY_REDIRECT_URI"];
end
end
APIキーは環境変数にして、
.envファイルを作成し、その中に入れましょう。
SPOTIFY_CLIENT_ID=your client ID
SPOTIFY_SECRET_ID=your secret ID
SPOTIFY_REDIRECT_URI=http://localhost:3000/tests/
<!doctype html>
<html>
<head>
<title>Example of the Implicit Grant 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="gradation">
<div class="container">
<div id="login">
<br>
<br>
<h1>This is Spotify Authentication</h1>
<div class="text-center"><button id="login-button" class="btn btn-primary">Log in with Spotify</button>
</div>
</div>
<div id="loggedin">
<div id="user-profile">
</div>
<div id="oauth">
</div>
</div>
</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>
</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 () {
var stateKey = 'spotify_auth_state';
/**
* 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;
}
/**
* Generates a random string containing numbers and letters
* @param {number} length The length of the string
* @return {string} The generated string
*/
function generateRandomString(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 userProfileSource = document.getElementById('user-profile-template').innerHTML,
userProfileTemplate = Handlebars.compile(userProfileSource),
userProfilePlaceholder = document.getElementById('user-profile');
oauthSource = document.getElementById('oauth-template').innerHTML,
oauthTemplate = Handlebars.compile(oauthSource),
oauthPlaceholder = document.getElementById('oauth');
var params = getHashParams();
var access_token = params.access_token,
state = params.state,
storedState = localStorage.getItem(stateKey);
console.log("-------------------------")
console.log(access_token);
if (access_token && (state == null || state !== storedState)) {
alert('There was an error during the authentication');
} else {
localStorage.removeItem(stateKey);
if (access_token) {
$.ajax({
url: 'https://api.spotify.com/v1/me',
headers: {
'Authorization': 'Bearer ' + access_token
},
success: function (response) {
userProfilePlaceholder.innerHTML = userProfileTemplate(response);
console.log(response);
$('#login').hide();
$('#loggedin').show();
}
});
} else {
$('#login').show();
$('#loggedin').hide();
}
document.getElementById('login-button').addEventListener('click', function () {
var client_id = gon.client_id; // Your client id
var redirect_uri = gon.redirect_uri; // Your redirect uri
var state = generateRandomString(16);
localStorage.setItem(stateKey, state);
var scope = 'streaming user-read-private user-read-email playlist-modify-private';
var url = 'https://accounts.spotify.com/authorize';
url += '?response_type=token';
url += '&client_id=' + encodeURIComponent(client_id);
url += '&scope=' + encodeURIComponent(scope);
url += '&redirect_uri=' + encodeURIComponent(redirect_uri);
url += '&state=' + encodeURIComponent(state);
window.location = url;
}, false);
}
})();
</script>
</html>
###手順❹
ログイン後のviewを作成しましょう。
$ rails g conrtoller tests
class TestsController < ApplicationController
def index
end
end
ログインできました。
お疲れ様でした!
Rails.application.routes.draw do
devise_for :users
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
get 'users/spotify_login', to: 'users#spotify_login', as: 'spotify_login'
resources :tests, only: [:index]
end
###手順❺
####完成!
ログインしてみましょう。
access_tokenは遷移先(今回でいうとtests_path)のURLに表示されています。
URLのxxxのところがaccess_tokenです。
expires_in=3600は、トークンの期限を表示しています。3600秒=1時間です。1時間でaccess_tokenが切れるのでユーザーは1時間ごとにログインをし直しましょう♬
http://localhost:3000/tests/#access_token=xxx&token_type=Bearer&expires_in=3600&state=caGLGQ8xzz1yYbH7
###Github
全コードは下記のgithubを参照ください♬
https://github.com/natan777natan/authorization