omniauth-granicus 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ .rspec
4
+ /Gemfile.lock
5
+ pkg/*
6
+ .powenv
7
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'jruby-openssl', :platform => :jruby
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # OmniAuth Granicus
2
+
3
+ Granicus OAuth2 Strategies for OmniAuth 1.0.
4
+
5
+ Supports the OAuth 2.0 server-side flow.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/example/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source :rubygems
2
+
3
+ # https://github.com/mkdynamic/omniauth-facebook/issues/20
4
+ gem 'rack', '~> 1.3.6'
5
+
6
+ gem 'sinatra'
7
+ gem 'omniauth-facebook', :path => '../'
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: ../
3
+ specs:
4
+ omniauth-facebook (1.2.0)
5
+ omniauth-oauth2 (~> 1.0.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ addressable (2.2.7)
11
+ faraday (0.7.6)
12
+ addressable (~> 2.2)
13
+ multipart-post (~> 1.1)
14
+ rack (~> 1.1)
15
+ hashie (1.2.0)
16
+ multi_json (1.1.0)
17
+ multipart-post (1.1.5)
18
+ oauth2 (0.5.2)
19
+ faraday (~> 0.7)
20
+ multi_json (~> 1.0)
21
+ omniauth (1.0.2)
22
+ hashie (~> 1.2)
23
+ rack
24
+ omniauth-oauth2 (1.0.0)
25
+ oauth2 (~> 0.5.0)
26
+ omniauth (~> 1.0)
27
+ rack (1.3.6)
28
+ rack-protection (1.2.0)
29
+ rack
30
+ sinatra (1.3.2)
31
+ rack (~> 1.3, >= 1.3.6)
32
+ rack-protection (~> 1.2)
33
+ tilt (~> 1.3, >= 1.3.3)
34
+ tilt (1.3.3)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ omniauth-facebook!
41
+ rack (~> 1.3.6)
42
+ sinatra
data/example/config.ru ADDED
@@ -0,0 +1,118 @@
1
+ require 'bundler/setup'
2
+ require 'sinatra/base'
3
+ require 'omniauth-facebook'
4
+
5
+ # https://github.com/intridea/omniauth-oauth2/pull/9
6
+ require 'timeout'
7
+
8
+ SCOPE = 'email,read_stream'
9
+
10
+ class App < Sinatra::Base
11
+ # turn off sinatra default X-Frame-Options for FB canvas
12
+ set :protection, :except => :frame_options
13
+
14
+ # server-side flow
15
+ get '/' do
16
+ # NOTE: you would just hit this endpoint directly from the browser
17
+ # in a real app. the redirect is just here to setup the root
18
+ # path in this example sinatra app.
19
+ redirect '/auth/facebook'
20
+ end
21
+
22
+ # client-side flow
23
+ get '/client-side' do
24
+ content_type 'text/html'
25
+ # NOTE: when you enable cookie below in the FB.init call
26
+ # the GET request in the FB.login callback will send
27
+ # a signed request in a cookie back the OmniAuth callback
28
+ # which will parse out the authorization code and obtain
29
+ # the access_token. This will be the exact same access_token
30
+ # returned to the client in response.authResponse.accessToken.
31
+ <<-END
32
+ <html>
33
+ <head>
34
+ <title>Client-side Flow Example</title>
35
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js" type="text/javascript"></script>
36
+ </head>
37
+ <body>
38
+ <div id="fb-root"></div>
39
+
40
+ <script type="text/javascript">
41
+ window.fbAsyncInit = function() {
42
+ FB.init({
43
+ appId : '#{ENV['APP_ID']}',
44
+ status : true, // check login status
45
+ cookie : true, // enable cookies to allow the server to access the session
46
+ xfbml : true // parse XFBML
47
+ });
48
+ };
49
+
50
+ (function(d) {
51
+ var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
52
+ js = d.createElement('script'); js.id = id; js.async = true;
53
+ js.src = "//connect.facebook.net/en_US/all.js";
54
+ d.getElementsByTagName('head')[0].appendChild(js);
55
+ }(document));
56
+
57
+ $(function() {
58
+ $('a').click(function(e) {
59
+ e.preventDefault();
60
+
61
+ FB.login(function(response) {
62
+ if (response.authResponse) {
63
+ $('#connect').html('Connected! Hitting OmniAuth callback (GET /auth/facebook/callback)...');
64
+
65
+ // since we have cookies enabled, this request will allow omniauth to parse
66
+ // out the auth code from the signed request in the fbsr_XXX cookie
67
+ $.getJSON('/auth/facebook/callback', function(json) {
68
+ $('#connect').html('Connected! Callback complete.');
69
+ $('#results').html(JSON.stringify(json));
70
+ });
71
+ }
72
+ }, { scope: '#{SCOPE}' });
73
+ });
74
+ });
75
+ </script>
76
+
77
+ <p id="connect">
78
+ <a href="#">Connect to FB</a>
79
+ </p>
80
+
81
+ <p id="results" />
82
+ </body>
83
+ </html>
84
+ END
85
+ end
86
+
87
+ # auth via FB canvas and signed request param
88
+ post '/canvas/' do
89
+ # we just redirect to /auth/facebook here which will parse the
90
+ # signed_request FB sends us, asking for auth if the user has
91
+ # not already granted access, or simply moving straight to the
92
+ # callback where they have already granted access.
93
+ #
94
+ # we pass the state parameter which we detect in our callback
95
+ # to do custom rendering/redirection for the canvas app page
96
+ redirect "/auth/facebook?signed_request=#{request.params['signed_request']}&state=canvas"
97
+ end
98
+
99
+ get '/auth/:provider/callback' do
100
+ # we can do something special here is +state+ param is canvas
101
+ # (see notes abovein /canvas/ method for more details)
102
+ content_type 'application/json'
103
+ MultiJson.encode(request.env)
104
+ end
105
+
106
+ get '/auth/failure' do
107
+ content_type 'application/json'
108
+ MultiJson.encode(request.env)
109
+ end
110
+ end
111
+
112
+ use Rack::Session::Cookie
113
+
114
+ use OmniAuth::Builder do
115
+ provider :facebook, ENV['APP_ID'], ENV['APP_SECRET'], :scope => SCOPE
116
+ end
117
+
118
+ run App.new
@@ -0,0 +1,5 @@
1
+ module OmniAuth
2
+ module GranicusAdmin
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require 'omniauth/granicus/version'
2
+ require 'omniauth/strategies/granicus-admin'
@@ -0,0 +1,136 @@
1
+ require 'omniauth/strategies/oauth2'
2
+ require 'base64'
3
+ require 'openssl'
4
+ require 'rack/utils'
5
+
6
+ module OmniAuth
7
+ module Strategies
8
+ class GranicusAdmin < OmniAuth::Strategies::OAuth2
9
+ class NoAuthorizationCodeError < StandardError; end
10
+
11
+ DEFAULT_SCOPE = ''
12
+
13
+ option :name, 'granicus_admin'
14
+
15
+ option :client_options, {
16
+ :site => 'https://citizen.dev.granicus.com',
17
+ :token_url => '/auth/oauth/token',
18
+ :authorize_url => '/auth/oauth/authorize'
19
+ }
20
+
21
+ option :token_params, {
22
+ :parse => :query
23
+ }
24
+
25
+ option :access_token_options, {
26
+ :header_format => 'OAuth %s',
27
+ :param_name => 'access_token'
28
+ }
29
+
30
+ option :authorize_options, [:scope, :display]
31
+
32
+ uid { raw_info['id'] }
33
+
34
+ info do
35
+ prune!({
36
+ 'nickname' => raw_info['username'],
37
+ 'email' => raw_info['email'],
38
+ 'name' => raw_info['name'],
39
+ 'first_name' => raw_info['first_name'],
40
+ 'last_name' => raw_info['last_name'],
41
+ 'image' => "#{options[:secure_image_url] ? 'https' : 'http'}://graph.facebook.com/#{uid}/picture?type=square",
42
+ 'description' => raw_info['bio'],
43
+ 'urls' => {
44
+ 'Facebook' => raw_info['link'],
45
+ 'Website' => raw_info['website']
46
+ },
47
+ 'location' => (raw_info['location'] || {})['name'],
48
+ 'verified' => raw_info['verified']
49
+ })
50
+ end
51
+
52
+ credentials do
53
+ prune!({
54
+ 'expires' => access_token.expires?,
55
+ 'expires_at' => access_token.expires_at
56
+ })
57
+ end
58
+
59
+ extra do
60
+ prune!({
61
+ 'raw_info' => raw_info
62
+ })
63
+ end
64
+
65
+ def raw_info
66
+ @raw_info ||= access_token.get('/me').parsed
67
+ end
68
+
69
+ def build_access_token
70
+ with_authorization_code! { super }.tap do |token|
71
+ token.options.merge!(access_token_options)
72
+ end
73
+ end
74
+
75
+ # NOTE if we're using code from the signed request
76
+ # then FB sets the redirect_uri to '' during the authorize
77
+ # phase + it must match during the access_token phase:
78
+ # https://github.com/facebook/php-sdk/blob/master/src/base_facebook.php#L348
79
+ def callback_url
80
+ options[:callback_url] || super
81
+ end
82
+
83
+ def access_token_options
84
+ options.access_token_options.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
85
+ end
86
+
87
+ ##
88
+ # You can pass +display+, +state+ or +scope+ params to the auth request, if
89
+ # you need to set them dynamically. You can also set these options
90
+ # in the OmniAuth config :authorize_params option.
91
+ #
92
+ # /auth/granicus_admin?host=sacramento.granicus.com
93
+ #
94
+ def authorize_params
95
+ super.tap do |params|
96
+ %w[host scope].each { |v| params[v.to_sym] = request.params[v] if request.params[v] }
97
+ params[:scope] ||= DEFAULT_SCOPE
98
+ if !params[:host].nil?
99
+ options.client_options[:site] = "https://#{params[:host]}"
100
+ end
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ ##
107
+ # Picks the authorization code in order, from:
108
+ #
109
+ # the request 'code' param (manual callback from standard server-side flow)
110
+ #
111
+ def with_authorization_code!
112
+ if request.params.key?('code')
113
+ yield
114
+ else
115
+ raise NoAuthorizationCodeError, 'must pass a `code` parameter'
116
+ end
117
+ end
118
+
119
+ def prune!(hash)
120
+ hash.delete_if do |_, value|
121
+ prune!(value) if value.is_a?(Hash)
122
+ value.nil? || (value.respond_to?(:empty?) && value.empty?)
123
+ end
124
+ end
125
+
126
+ # def valid_signature?(secret, signature, payload, algorithm = OpenSSL::Digest::SHA256.new)
127
+ # OpenSSL::HMAC.digest(algorithm, secret, payload) == signature
128
+ # end
129
+
130
+ def base64_decode_url(value)
131
+ value += '=' * (4 - value.size.modulo(4))
132
+ Base64.decode64(value.tr('-_', '+/'))
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1 @@
1
+ require 'omniauth/granicus-admin'
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'omniauth/granicus/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'omniauth-granicus'
7
+ s.version = OmniAuth::GranicusAdmin::VERSION
8
+ s.authors = ['Javier Muniz']
9
+ s.email = ['javier@granicus.com']
10
+ s.summary = 'Granicus Admin strategy for OmniAuth'
11
+ s.homepage = 'https://github.com/granicus/omniauth-granicus-admin'
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
16
+ s.require_paths = ['lib']
17
+
18
+ s.add_runtime_dependency 'omniauth-oauth2', '~> 1.0.0'
19
+
20
+ s.add_development_dependency 'rspec', '~> 2.7.0'
21
+ s.add_development_dependency 'rake'
22
+ end
@@ -0,0 +1,339 @@
1
+ require 'spec_helper'
2
+ require 'omniauth-granicus-admin'
3
+ require 'openssl'
4
+ require 'base64'
5
+
6
+ describe OmniAuth::Strategies::GranicusAdmin do
7
+ before :each do
8
+ @request = double('Request')
9
+ @request.stub(:params) { {} }
10
+ @request.stub(:cookies) { {} }
11
+ @request.stub(:env) { {} }
12
+
13
+ @client_id = '123'
14
+ @client_secret = '53cr3tz'
15
+ end
16
+
17
+ subject do
18
+ args = [@client_id, @client_secret, @options].compact
19
+ OmniAuth::Strategies::GranicusAdmin.new(nil, *args).tap do |strategy|
20
+ strategy.stub(:request) { @request }
21
+ end
22
+ end
23
+
24
+ it_should_behave_like 'an oauth2 strategy'
25
+
26
+ describe '#client' do
27
+ it 'has correct Granicus site' do
28
+ subject.client.site.should eq('https://citizen.dev.granicus.com')
29
+ end
30
+
31
+ it 'has correct authorize url' do
32
+ subject.client.options[:authorize_url].should eq('/auth/oauth/authorize')
33
+ end
34
+
35
+ it 'has correct token url' do
36
+ subject.client.options[:token_url].should eq('/auth/oauth/token')
37
+ end
38
+ end
39
+
40
+ describe '#callback_url' do
41
+ it "returns the default callback url" do
42
+ url_base = 'http://auth.request.com'
43
+ @request.stub(:url) { "#{url_base}/some/page" }
44
+ subject.stub(:script_name) { '' } # as not to depend on Rack env
45
+ subject.callback_url.should eq("#{url_base}/auth/granicus_admin/callback")
46
+ end
47
+
48
+ it "returns path from callback_path option" do
49
+ @options = { :callback_path => "/auth/FB/done"}
50
+ url_base = 'http://auth.request.com'
51
+ @request.stub(:url) { "#{url_base}/page/path" }
52
+ subject.stub(:script_name) { '' } # as not to depend on Rack env
53
+ subject.callback_url.should eq("#{url_base}/auth/FB/done")
54
+ end
55
+
56
+ it "returns url from callback_url option" do
57
+ url = 'https://auth.myapp.com/auth/fb/callback'
58
+ @options = { :callback_url => url }
59
+ subject.callback_url.should eq(url)
60
+ end
61
+ end
62
+
63
+ describe '#authorize_params' do
64
+ it 'includes default scope for session access' do
65
+ subject.authorize_params.should be_a(Hash)
66
+ subject.authorize_params[:scope].should eq('')
67
+ end
68
+
69
+ it 'changes site to site defined by host param in request when present' do
70
+ @request.stub(:params) { { 'host' => 'dev.dev.granicus.com' } }
71
+ subject.authorize_params.should be_a(Hash)
72
+ subject.authorize_params[:host].should eq('dev.dev.granicus.com')
73
+ subject.client.site.should eq('https://dev.dev.granicus.com')
74
+ end
75
+
76
+ it 'overrides default scope with parameter passed from request' do
77
+ @request.stub(:params) { { 'scope' => 'email' } }
78
+ subject.authorize_params.should be_a(Hash)
79
+ subject.authorize_params[:scope].should eq('email')
80
+ end
81
+ end
82
+
83
+ describe '#token_params' do
84
+ it 'has correct parse strategy' do
85
+ subject.token_params[:parse].should eq(:query)
86
+ end
87
+ end
88
+
89
+ describe '#access_token_options' do
90
+ it 'has correct param name by default' do
91
+ subject.access_token_options[:param_name].should eq('access_token')
92
+ end
93
+
94
+ it 'has correct header format by default' do
95
+ subject.access_token_options[:header_format].should eq('OAuth %s')
96
+ end
97
+ end
98
+
99
+ describe '#uid' do
100
+ before :each do
101
+ subject.stub(:raw_info) { { 'id' => '123' } }
102
+ end
103
+
104
+ it 'returns the id from raw_info' do
105
+ subject.uid.should eq('123')
106
+ end
107
+ end
108
+
109
+ describe '#info' do
110
+ context 'when optional data is not present in raw info' do
111
+ before :each do
112
+ @raw_info ||= { 'name' => 'Fred Smith' }
113
+ subject.stub(:raw_info) { @raw_info }
114
+ end
115
+
116
+ it 'has no email key' do
117
+ subject.info.should_not have_key('email')
118
+ end
119
+
120
+ it 'has no nickname key' do
121
+ subject.info.should_not have_key('nickname')
122
+ end
123
+
124
+ it 'has no first name key' do
125
+ subject.info.should_not have_key('first_name')
126
+ end
127
+
128
+ it 'has no last name key' do
129
+ subject.info.should_not have_key('last_name')
130
+ end
131
+
132
+ it 'has no location key' do
133
+ subject.info.should_not have_key('location')
134
+ end
135
+
136
+ it 'has no description key' do
137
+ subject.info.should_not have_key('description')
138
+ end
139
+
140
+ it 'has no urls' do
141
+ subject.info.should_not have_key('urls')
142
+ end
143
+
144
+ it 'has no verified key' do
145
+ subject.info.should_not have_key('verified')
146
+ end
147
+ end
148
+
149
+ context 'when optional data is present in raw info' do
150
+ before :each do
151
+ @raw_info ||= { 'name' => 'Fred Smith' }
152
+ subject.stub(:raw_info) { @raw_info }
153
+ end
154
+
155
+ it 'returns the name' do
156
+ subject.info['name'].should eq('Fred Smith')
157
+ end
158
+
159
+ it 'returns the email' do
160
+ @raw_info['email'] = 'fred@smith.com'
161
+ subject.info['email'].should eq('fred@smith.com')
162
+ end
163
+
164
+ it 'returns the username as nickname' do
165
+ @raw_info['username'] = 'fredsmith'
166
+ subject.info['nickname'].should eq('fredsmith')
167
+ end
168
+
169
+ it 'returns the first name' do
170
+ @raw_info['first_name'] = 'Fred'
171
+ subject.info['first_name'].should eq('Fred')
172
+ end
173
+
174
+ it 'returns the last name' do
175
+ @raw_info['last_name'] = 'Smith'
176
+ subject.info['last_name'].should eq('Smith')
177
+ end
178
+
179
+ it 'returns the location name as location' do
180
+ @raw_info['location'] = { 'id' => '104022926303756', 'name' => 'Palo Alto, California' }
181
+ subject.info['location'].should eq('Palo Alto, California')
182
+ end
183
+
184
+ it 'returns bio as description' do
185
+ @raw_info['bio'] = 'I am great'
186
+ subject.info['description'].should eq('I am great')
187
+ end
188
+
189
+ it 'returns the square format granicus avatar url' do
190
+ @raw_info['id'] = '321'
191
+ subject.info['image'].should eq('http://graph.facebook.com/321/picture?type=square')
192
+ end
193
+
194
+ it 'returns the Facebook link as the Facebook url' do
195
+ @raw_info['link'] = 'http://www.facebook.com/fredsmith'
196
+ subject.info['urls'].should be_a(Hash)
197
+ subject.info['urls']['Facebook'].should eq('http://www.facebook.com/fredsmith')
198
+ end
199
+
200
+ it 'returns website url' do
201
+ @raw_info['website'] = 'https://my-wonderful-site.com'
202
+ subject.info['urls'].should be_a(Hash)
203
+ subject.info['urls']['Website'].should eq('https://my-wonderful-site.com')
204
+ end
205
+
206
+ it 'return both Facebook link and website urls' do
207
+ @raw_info['link'] = 'http://www.facebook.com/fredsmith'
208
+ @raw_info['website'] = 'https://my-wonderful-site.com'
209
+ subject.info['urls'].should be_a(Hash)
210
+ subject.info['urls']['Facebook'].should eq('http://www.facebook.com/fredsmith')
211
+ subject.info['urls']['Website'].should eq('https://my-wonderful-site.com')
212
+ end
213
+
214
+ it 'returns the positive verified status' do
215
+ @raw_info['verified'] = true
216
+ subject.info['verified'].should be_true
217
+ end
218
+
219
+ it 'returns the negative verified status' do
220
+ @raw_info['verified'] = false
221
+ subject.info['verified'].should be_false
222
+ end
223
+ end
224
+
225
+ it 'returns the secure facebook avatar url when `secure_image_url` option is specified' do
226
+ @options = { :secure_image_url => true }
227
+ raw_info = { 'name' => 'Fred Smith', 'id' => '321' }
228
+ subject.stub(:raw_info) { raw_info }
229
+ subject.info['image'].should eq('https://graph.facebook.com/321/picture?type=square')
230
+ end
231
+ end
232
+
233
+ describe '#raw_info' do
234
+ before :each do
235
+ @access_token = double('OAuth2::AccessToken')
236
+ subject.stub(:access_token) { @access_token }
237
+ end
238
+
239
+ it 'performs a GET to https://graph.facebook.com/me' do
240
+ @access_token.stub(:get) { double('OAuth2::Response').as_null_object }
241
+ @access_token.should_receive(:get).with('/me')
242
+ subject.raw_info
243
+ end
244
+
245
+ it 'returns a Hash' do
246
+ @access_token.stub(:get).with('/me') do
247
+ raw_response = double('Faraday::Response')
248
+ raw_response.stub(:body) { '{ "ohai": "thar" }' }
249
+ raw_response.stub(:status) { 200 }
250
+ raw_response.stub(:headers) { { 'Content-Type' => 'application/json' } }
251
+ OAuth2::Response.new(raw_response)
252
+ end
253
+ subject.raw_info.should be_a(Hash)
254
+ subject.raw_info['ohai'].should eq('thar')
255
+ end
256
+ end
257
+
258
+ describe '#credentials' do
259
+ before :each do
260
+ @access_token = double('OAuth2::AccessToken')
261
+ @access_token.stub(:token)
262
+ @access_token.stub(:expires?)
263
+ @access_token.stub(:expires_at)
264
+ @access_token.stub(:refresh_token)
265
+ subject.stub(:access_token) { @access_token }
266
+ end
267
+
268
+ it 'returns a Hash' do
269
+ subject.credentials.should be_a(Hash)
270
+ end
271
+
272
+ it 'returns the token' do
273
+ @access_token.stub(:token) { '123' }
274
+ subject.credentials['token'].should eq('123')
275
+ end
276
+
277
+ it 'returns the expiry status' do
278
+ @access_token.stub(:expires?) { true }
279
+ subject.credentials['expires'].should eq(true)
280
+
281
+ @access_token.stub(:expires?) { false }
282
+ subject.credentials['expires'].should eq(false)
283
+ end
284
+
285
+ it 'returns the refresh token and expiry time when expiring' do
286
+ ten_mins_from_now = (Time.now + 600).to_i
287
+ @access_token.stub(:expires?) { true }
288
+ @access_token.stub(:refresh_token) { '321' }
289
+ @access_token.stub(:expires_at) { ten_mins_from_now }
290
+ subject.credentials['refresh_token'].should eq('321')
291
+ subject.credentials['expires_at'].should eq(ten_mins_from_now)
292
+ end
293
+
294
+ it 'does not return the refresh token when it is nil and expiring' do
295
+ @access_token.stub(:expires?) { true }
296
+ @access_token.stub(:refresh_token) { nil }
297
+ subject.credentials['refresh_token'].should be_nil
298
+ subject.credentials.should_not have_key('refresh_token')
299
+ end
300
+
301
+ it 'does not return the refresh token when not expiring' do
302
+ @access_token.stub(:expires?) { false }
303
+ @access_token.stub(:refresh_token) { 'XXX' }
304
+ subject.credentials['refresh_token'].should be_nil
305
+ subject.credentials.should_not have_key('refresh_token')
306
+ end
307
+ end
308
+
309
+ describe '#extra' do
310
+ before :each do
311
+ @raw_info = { 'name' => 'Fred Smith' }
312
+ subject.stub(:raw_info) { @raw_info }
313
+ end
314
+
315
+ it 'returns a Hash' do
316
+ subject.extra.should be_a(Hash)
317
+ end
318
+
319
+ it 'contains raw info' do
320
+ subject.extra.should eq({ 'raw_info' => @raw_info })
321
+ end
322
+ end
323
+
324
+ private
325
+
326
+ def signed_request(payload, secret)
327
+ encoded_payload = base64_encode_url(MultiJson.dump(payload))
328
+ encoded_signature = base64_encode_url(signature(encoded_payload, secret))
329
+ [encoded_signature, encoded_payload].join('.')
330
+ end
331
+
332
+ def base64_encode_url(value)
333
+ Base64.encode64(value).tr('+/', '-_').gsub(/\n/, '')
334
+ end
335
+
336
+ def signature(payload, secret, algorithm = OpenSSL::Digest::SHA256.new)
337
+ OpenSSL::HMAC.digest(algorithm, secret, payload)
338
+ end
339
+ end
@@ -0,0 +1,6 @@
1
+ require 'bundler/setup'
2
+ require 'rspec'
3
+ Dir[File.expand_path('../support/**/*', __FILE__)].each { |f| require f }
4
+
5
+ RSpec.configure do |config|
6
+ end
@@ -0,0 +1,37 @@
1
+ # NOTE it would be useful if this lived in omniauth-oauth2 eventually
2
+ shared_examples 'an oauth2 strategy' do
3
+ describe '#client' do
4
+ it 'should be initialized with symbolized client_options' do
5
+ @options = { :client_options => { 'authorize_url' => 'https://example.com' } }
6
+ subject.client.options[:authorize_url].should == 'https://example.com'
7
+ end
8
+ end
9
+
10
+ describe '#authorize_params' do
11
+ it 'should include any authorize params passed in the :authorize_params option' do
12
+ @options = { :authorize_params => { :foo => 'bar', :baz => 'zip' } }
13
+ subject.authorize_params['foo'].should eq('bar')
14
+ subject.authorize_params['baz'].should eq('zip')
15
+ end
16
+
17
+ it 'should include top-level options that are marked as :authorize_options' do
18
+ @options = { :authorize_options => [:scope, :foo], :scope => 'bar', :foo => 'baz' }
19
+ subject.authorize_params['scope'].should eq('bar')
20
+ subject.authorize_params['foo'].should eq('baz')
21
+ end
22
+ end
23
+
24
+ describe '#token_params' do
25
+ it 'should include any authorize params passed in the :authorize_params option' do
26
+ @options = { :token_params => { :foo => 'bar', :baz => 'zip' } }
27
+ subject.token_params['foo'].should eq('bar')
28
+ subject.token_params['baz'].should eq('zip')
29
+ end
30
+
31
+ it 'should include top-level options that are marked as :authorize_options' do
32
+ @options = { :token_options => [:scope, :foo], :scope => 'bar', :foo => 'baz' }
33
+ subject.token_params['scope'].should eq('bar')
34
+ subject.token_params['foo'].should eq('baz')
35
+ end
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-granicus
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Javier Muniz
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-24 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: omniauth-oauth2
16
+ requirement: &2152710860 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2152710860
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &2152703180 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 2.7.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *2152703180
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &2152702700 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2152702700
47
+ description:
48
+ email:
49
+ - javier@granicus.com
50
+ executables: []
51
+ extensions: []
52
+ extra_rdoc_files: []
53
+ files:
54
+ - .gitignore
55
+ - Gemfile
56
+ - README.md
57
+ - Rakefile
58
+ - example/Gemfile
59
+ - example/Gemfile.lock
60
+ - example/config.ru
61
+ - lib/omniauth-granicus-admin.rb
62
+ - lib/omniauth/granicus-admin.rb
63
+ - lib/omniauth/granicus/version.rb
64
+ - lib/omniauth/strategies/granicus-admin.rb
65
+ - omniauth-granicus.gemspec
66
+ - spec/omniauth/strategies/granicus_spec.rb
67
+ - spec/spec_helper.rb
68
+ - spec/support/shared_examples.rb
69
+ homepage: https://github.com/granicus/omniauth-granicus-admin
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ! '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.5
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Granicus Admin strategy for OmniAuth
93
+ test_files:
94
+ - spec/omniauth/strategies/granicus_spec.rb
95
+ - spec/spec_helper.rb
96
+ - spec/support/shared_examples.rb