├── .gitignore ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── lib ├── omniauth-spotify.rb └── omniauth-spotify │ └── version.rb └── omniauth-spotify.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in omniauth-box2.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Claudio Poli 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spotify OmniAuth Strategy 2 | 3 | This gem provides a simple way to authenticate to the Spotify Web API using OmniAuth with OAuth2. 4 | 5 | ## Installation 6 | 7 | Add this line to your application's Gemfile: 8 | 9 | gem 'omniauth-spotify' 10 | 11 | And then execute: 12 | 13 | $ bundle 14 | 15 | Or install it yourself as: 16 | 17 | $ gem install omniauth-spotify 18 | 19 | ## Usage 20 | 21 | You'll need to register an app on Spotify, you can do this here - https://developer.spotify.com/my-applications/#!/applications 22 | 23 | Usage of the gem is very similar to other OmniAuth strategies. 24 | You'll need to add your app credentials to `config/initializers/omniauth.rb`: 25 | 26 | ```ruby 27 | Rails.application.config.middleware.use OmniAuth::Builder do 28 | provider :spotify, Rails.application.credentials.spotify[:client_id], Rails.application.credentials.spotify[:client_secret], scope: %w( 29 | playlist-read-private 30 | user-read-private 31 | user-read-email 32 | ).join(' ') 33 | end 34 | ``` 35 | 36 | Please replace the example `scope` provided with your own. 37 | Read more about scopes here: https://developer.spotify.com/web-api/using-scopes/ 38 | 39 | Or with Devise in `config/initializers/devise.rb`: 40 | 41 | ```ruby 42 | config.omniauth :spotify, Rails.application.credentials.spotify[:client_id], Rails.application.credentials.spotify[:client_secret], scope: %w( 43 | playlist-read-private 44 | user-read-private 45 | user-read-email 46 | ).join(' ') 47 | ``` 48 | 49 | ## Forcing a Permission-Request Dialog 50 | 51 | If a user has given permission for an app to access a scope, that permission won't be asked again unless the user revokes access. 52 | In these cases, authorization sequences proceed without user interation. 53 | 54 | To force a permission dialog being shown to the user, which also makes it possible for them to switch Spotify accounts, 55 | set either `request.env['rack.session'][:ommiauth_spotify_force_approval?]` or `flash[:ommiauth_spotify_force_approval?]` (Rails apps only) 56 | to a truthy value on the request that performs the Omniauth redirection. 57 | 58 | Alternately, you can pass `show_dialog=true` when you redirect to your spotify auth URL if you prefer not to use the session. 59 | ``` 60 | http://localhost:3000/auth/spotify?show_dialog=true 61 | ``` 62 | 63 | ## Auth Hash Schema 64 | 65 | * Authorization data is available in the `request.env['omniauth.auth'].credentials` -- a hash that also responds to 66 | the `token`, `refresh_token`, `expires_at`, and `expires` methods. 67 | 68 | ```ruby 69 | { 70 | "token" => "xxxx", 71 | "refresh_token" => "xxxx", 72 | "expires_at" => 1403021232, 73 | "expires" => true 74 | } 75 | ``` 76 | 77 | * Information about the authorized Spotify user is available in the `request.env['omniauth.auth'].info` hash. e.g. 78 | 79 | ```ruby 80 | { 81 | :name => "Claudio Poli", 82 | :nickname => "SomeName", 83 | :email => "claudio@icorete.ch", 84 | :urls => {"spotify" => "https://open.spotify.com/user/1111111111"}, 85 | :image => "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xfp1/t1.0-1/s320x320/301234_1962753760624_625151598_n.jpg", 86 | :birthdate => Mon, 01 Mar 1993, # Date class 87 | :country_code => "IT", 88 | :product => "open", 89 | :follower_count => 10 90 | } 91 | ``` 92 | 93 | The username/nickname is also available via a call to `request.env['omniauth.auth'].uid`. 94 | 95 | * Unless the `user-read-private` scope is included, the `birthdate`, `country`, `image`, and `product` fields may be `nil`, 96 | and the `name` field will be set to the username/nickname instead of the display name. 97 | * The email field will be nil if the 'user-read-email' scope isn't included. 98 | 99 | 100 | * The raw response to the `me` endpoint call is also available in `request.env['omniauth.auth'].extra['raw_info']`. e.g. 101 | 102 | ```ruby 103 | { 104 | "country" => "IT", 105 | "display_name" => "Claudio Poli", 106 | "birthdate" => "1993-03-01", 107 | "email" => "claudio@icorete.ch", 108 | "external_urls" => { 109 | "spotify" => "https://open.spotify.com/user/1111111111" 110 | }, 111 | "followers" => { 112 | "href" => nil, 113 | "total" => 10 114 | }, 115 | "href" => "https://api.spotify.com/v1/users/1111111111", 116 | "id" => "1111111111", 117 | "images" => [ 118 | { 119 | "height" => nil, 120 | "url" => "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-xfp1/t1.0-1/s320x320/301234_1962753760624_625151598_n.jpg", 121 | "width" => nil 122 | } 123 | ], 124 | "product" => "open", 125 | "type" => "user", 126 | "uri" => "spotify:user:1111111111" 127 | } 128 | 129 | ``` 130 | 131 | ## More 132 | 133 | This gem is brought to you by the [AudioBox](https://audiobox.fm) guys. 134 | Enjoy! 135 | 136 | ## Contributing 137 | 138 | 1. Fork it 139 | 2. Create your feature branch (`git checkout -b my-new-feature`) 140 | 3. Commit your changes (`git commit -am 'Add some feature'`) 141 | 4. Push to the branch (`git push origin my-new-feature`) 142 | 5. Create new Pull Request 143 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /lib/omniauth-spotify.rb: -------------------------------------------------------------------------------- 1 | require 'omniauth/strategies/oauth2' 2 | require 'date' 3 | 4 | module OmniAuth 5 | module Strategies 6 | class Spotify < OmniAuth::Strategies::OAuth2 7 | # Give your strategy a name. 8 | option :name, 'spotify' 9 | 10 | FORCE_APPROVAL_KEY = 'ommiauth_spotify_force_approval?'.freeze 11 | 12 | # This is where you pass the options you would pass when 13 | # initializing your consumer from the OAuth gem. 14 | option :client_options, { 15 | :site => 'https://api.spotify.com/v1', 16 | :authorize_url => 'https://accounts.spotify.com/authorize', 17 | :token_url => 'https://accounts.spotify.com/api/token', 18 | } 19 | 20 | # These are called after authentication has succeeded. If 21 | # possible, you should try to set the UID without making 22 | # additional calls (if the user id is returned with the token 23 | # or as a URI parameter). This may not be possible with all 24 | # providers. 25 | uid{ raw_info['id'] } 26 | 27 | info do 28 | { 29 | # Unless the 'user-read-private' scope is included, the birthdate, country, image, and product fields may be nil, 30 | # and the name field will be set to the username/nickname instead of the display name. 31 | # The email field will be nil if the 'user-read-email' scope isn't included. 32 | # 33 | :name => raw_info['display_name'] || raw_info['id'], 34 | :nickname => raw_info['id'], 35 | :email => raw_info['email'], 36 | :urls => raw_info['external_urls'], 37 | :image => image_url, 38 | :birthdate => raw_info['birthdate'] && Date.parse(raw_info['birthdate']), 39 | :country_code => raw_info['country'], 40 | :product => raw_info['product'], 41 | :follower_count => raw_info['followers']['total'] 42 | } 43 | end 44 | 45 | extra do 46 | { 47 | 'raw_info' => raw_info 48 | } 49 | end 50 | 51 | def image_url 52 | if images = raw_info['images'] 53 | if first = images.first 54 | first['url'] 55 | end 56 | end 57 | end 58 | 59 | def raw_info 60 | @raw_info ||= access_token.get('me').parsed 61 | end 62 | 63 | def authorize_params 64 | super.tap do |params| 65 | if session.delete(FORCE_APPROVAL_KEY) || 66 | (session[:flash] && session[:flash]['flashes'] && session[:flash]['flashes'][FORCE_APPROVAL_KEY]) 67 | params[:show_dialog] = true 68 | end 69 | end 70 | end 71 | 72 | def request_phase 73 | %w[show_dialog].each do |v| 74 | if request.params[v] 75 | options[:authorize_params][v.to_sym] = request.params[v] 76 | end 77 | end 78 | super 79 | end 80 | 81 | def callback_url 82 | if @authorization_code_from_signed_request_in_cookie 83 | '' 84 | else 85 | # Fixes regression in omniauth-oauth2 v1.4.0 by https://github.com/intridea/omniauth-oauth2/commit/85fdbe117c2a4400d001a6368cc359d88f40abc7 86 | options[:callback_url] || (full_host + script_name + callback_path) 87 | end 88 | end 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/omniauth-spotify/version.rb: -------------------------------------------------------------------------------- 1 | module Omniauth 2 | module Spotify 3 | VERSION = "0.0.13" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /omniauth-spotify.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'omniauth-spotify/version' 5 | 6 | Gem::Specification.new do |gem| 7 | gem.name = "omniauth-spotify" 8 | gem.version = Omniauth::Spotify::VERSION 9 | gem.authors = ["Claudio Poli"] 10 | gem.email = ["claudio@icorete.ch\n"] 11 | gem.description = %q{OmniAuth strategy for Spotify} 12 | gem.summary = %q{OmniAuth strategy for Spotify} 13 | gem.homepage = "https://github.com/icoretech/omniauth-spotify" 14 | 15 | gem.files = `git ls-files`.split($/) 16 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 17 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 18 | gem.require_paths = ["lib"] 19 | 20 | gem.add_dependency 'omniauth-oauth2', '~> 1.1' 21 | end 22 | --------------------------------------------------------------------------------