omniauth-tent 0.1.8 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 801dbe53cc70dbbc354b3f415ffa2c832320a897
4
+ data.tar.gz: 8126aac753fc7eebb17ea5c3193aceac2fa9a28d
5
+ SHA512:
6
+ metadata.gz: fd0b21a9ddb59b362baafeb5ceb940f8e6ec98111be28a2f7622e31317d0c6e3c04ed0366e12a538057cba0318c5b1a4fbc6e350e0cdb7c9bf95147e52c56ba5
7
+ data.tar.gz: 63a4cdf0dac45fb62124862faef74e2212ffcbc05c10415c266ce2d3576e8c0915c9a8fe321335107b197922e99e4e9686ce04001e7be731e832d22d3d8cd002
@@ -1,9 +1,6 @@
1
- before_script: mkdir ./log && touch ./log/test.log
2
1
  language: ruby
3
2
  rvm:
3
+ - 2.0.0
4
4
  - 1.9.3
5
5
  - 1.9.2
6
- - rbx-18mode
7
6
  - rbx-19mode
8
- - 1.8.7
9
- - ree
data/Gemfile CHANGED
@@ -4,4 +4,5 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'tent-client', :git => 'git://github.com/tent/tent-client-ruby.git', :branch => 'master'
7
+ gem 'hawk-auth', :git => 'git://github.com/tent/hawk-ruby.git', :branch => 'master'
7
8
  gem 'faraday_middleware-multi_json', :git => 'git://github.com/jvatic/faraday_middleware-multi_json.git', :branch => :master
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://secure.travis-ci.org/tent/omniauth-tent.png)](http://travis-ci.org/tent/omniauth-tent)
1
+ [![Build Status](https://travis-ci.org/tent/omniauth-tent.png?branch=master)](https://travis-ci.org/tent/omniauth-tent)
2
2
 
3
3
  # Omniauth::Tent
4
4
 
@@ -19,23 +19,16 @@ use OmniAuth::Builder do
19
19
  end
20
20
  ```
21
21
 
22
- Available options (see [the Tent.io docs for details](http://tent.io/docs/app-auth))
22
+ Available options (see the Tent docs for details)
23
23
 
24
24
  | Option | Required | Description |
25
25
  | ------ | -------- | ----------- |
26
26
  | get_app | Yes | Should be a lambda (or anything which responds to `call`) returning either an existing app attributes hash or `nil`. The entity URI will be passed in as a single argument. |
27
27
  | on_app_created | No | Should respond to `call`. Gets called with Hashie::Mash representation of app when created |
28
- | app | Yes | `name`, `icon`, `url`, `description`, `scopes`, and `redirect_uris` |
29
- | profile_info_types | Yes | Array of profile info type URIs your app wants access to |
30
- | post_types | Yes | Array of post type URIs your app wants access to |
31
- | notification_url | Yes | URL for receiving notifications |
28
+ | app | Yes | `name`, `description`, `url`, `redirect_uri`, `read_types`, `write_types`, `notification_types`, `notification_url`, `scopes` |
32
29
 
33
30
  ## Testing
34
31
 
35
- bundle exec kicker
36
-
37
- OR
38
-
39
32
  bundle exec rspec
40
33
 
41
34
  ## Contributing
@@ -1,5 +1,5 @@
1
1
  module Omniauth
2
2
  module Tent
3
- VERSION = "0.1.8"
3
+ VERSION = "0.2.1"
4
4
  end
5
5
  end
@@ -10,23 +10,38 @@ module OmniAuth
10
10
 
11
11
  Error = Class.new(StandardError)
12
12
  AppCreateFailure = Class.new(Error)
13
+ AppUpdateFailure = Class.new(Error)
13
14
  AppLookupFailure = Class.new(Error)
14
15
  AppAuthorizationCreateFailure = Class.new(Error)
16
+ DiscoveryFailure = Class.new(Error)
15
17
  StateMissmatchError = Class.new(Error)
18
+ InvalidAppError = Class.new(Error)
19
+ OAuthError = Class.new(Error)
16
20
 
17
21
  option :get_app, lambda { |entity| }
18
22
  option :on_app_created, lambda { |app, entity| }
19
- option :app, { :name => nil, :icon => nil, :description => nil, :scopes => {}, :redirect_uris => nil }
20
- option :profile_info_types, []
21
- option :post_types, []
22
- option :notification_url, ""
23
+
24
+ option :app, {
25
+ :name => nil,
26
+ :description => nil,
27
+ :url => nil,
28
+ :redirect_uri => nil,
29
+ :read_types => [],
30
+ :write_types => [],
31
+ :notification_types => [],
32
+ :notification_url => nil,
33
+ :scopes => [],
34
+ :icon => nil
35
+ }
23
36
 
24
37
  def request_params
25
38
  Hashie::Mash.new(request.params)
26
39
  end
27
40
 
28
41
  def request_phase
29
- if request.post? && request_params.entity
42
+ if request.post?
43
+ raise DiscoveryFailure.new("No entity given!") unless request_params.entity
44
+
30
45
  delete_state!
31
46
  set_state(:entity, ensure_entity_has_scheme(request_params.entity))
32
47
  perform_discovery!
@@ -40,24 +55,37 @@ module OmniAuth
40
55
  end.to_response
41
56
  end
42
57
  rescue AppCreateFailure => e
58
+ delete_state!
43
59
  fail!(:app_create_failure, e)
44
60
  rescue AppLookupFailure => e
61
+ delete_state!
45
62
  fail!(:app_lookup_failure, e)
63
+ rescue InvalidAppError => e
64
+ delete_state!
65
+ fail!(:invalid_app, e)
66
+ rescue DiscoveryFailure => e
67
+ delete_state!
68
+ fail!(:discovery_failure, e)
46
69
  rescue => e
70
+ delete_state!
47
71
  fail!(:unknown_error, e)
48
72
  end
49
73
 
50
74
  def callback_phase
75
+ check_error!
51
76
  verify_state!
52
- create_app_authorization!
77
+ token_exchange!
53
78
  build_auth_hash!
54
79
  delete_state!
55
80
  call_app!
56
81
  rescue AppAuthorizationCreateFailure => e
82
+ delete_state!
57
83
  fail!(:app_auth_create_failure, e)
58
84
  rescue StateMissmatchError => e
85
+ delete_state!
59
86
  fail!(:state_missmatch, e)
60
87
  rescue => e
88
+ delete_state!
61
89
  fail!(:unknown_error, e)
62
90
  end
63
91
 
@@ -72,6 +100,7 @@ module OmniAuth
72
100
  end
73
101
 
74
102
  def set_state(key, val)
103
+ (session["omniauth.tent-keys"] ||= []) << key unless session["omniauth.tent-keys"].to_a.include?(key)
75
104
  session["omniauth.#{key}"] = val
76
105
  val
77
106
  end
@@ -81,141 +110,248 @@ module OmniAuth
81
110
  end
82
111
 
83
112
  def perform_discovery!
84
- client = ::TentClient.new
85
- @profile, @server_url = client.discover(get_state(:entity)).get_profile
86
- set_state(:server_url, @server_url)
87
- set_state(:profile, @profile)
113
+ client = ::TentClient.new(get_state(:entity))
114
+ unless @server_meta = client.server_meta_post
115
+ raise DiscoveryFailure.new("Failed to perform discovery on #{get_state(:entity).inspect}")
116
+ end
117
+ end
118
+
119
+ def validate_app!(app)
120
+ unless Hash === app
121
+ raise InvalidAppError.new("Expected app to be a hash, got instance of #{app.class.name}")
122
+ end
123
+
124
+ expected_attributes = [:id, :credentials]
125
+ if expected_attributes.any? { |key| !app.has_key?(key) }
126
+ raise InvalidAppError.new("Expected app to have #{expected_attributes.map(&:inspect).join(', ')}, got #{app.keys.map(&:inspect).join(', ')}")
127
+ end
88
128
  end
89
129
 
90
130
  def find_or_create_app!
91
- app = Hashie::Mash.new(options[:get_app].call(get_state(:entity)) || {})
92
- client = ::TentClient.new(get_state(:server_url), :mac_key_id => app[:mac_key_id],
93
- :mac_key => app[:mac_key],
94
- :mac_algorithm => app[:mac_algorithm])
95
- if app[:id]
96
- res = client.app.get(app[:id])
97
- if !res.success?
131
+ app = options[:get_app].call(get_state(:entity))
132
+ app = Hashie::Mash.new(app) if Hash === app
133
+
134
+ if app
135
+ validate_app!(app)
136
+
137
+ ##
138
+ # Check if app exists, if not create a new one
139
+
140
+ app_credentials = {
141
+ :id => app[:credentials][:hawk_id],
142
+ :hawk_key => app[:credentials][:hawk_key],
143
+ :hawk_algorithm => app[:credentials][:hawk_algorithm]
144
+ }
145
+ client = ::TentClient.new(get_state(:entity), :credentials => app_credentials, :server_meta => @server_meta)
146
+
147
+ res = client.post.get(get_state(:entity), app[:id])
148
+
149
+ if res.success?
150
+ if res.body['post']['content']['scopes'].to_a.sort == options[:app][:scopes].to_a.sort &&
151
+ res.body['post']['content']['types']['read'].to_a.sort == options[:app][:read_types].to_a.sort &&
152
+ res.body['post']['content']['types']['write'].to_a.sort == options[:app][:write_types].to_a.sort
153
+
154
+ set_app(app)
155
+ set_server(res.env[:tent_server])
156
+ else
157
+ update_app(res.body['post'], app_credentials)
158
+ end
159
+ else
98
160
  if (400...500).include?(res.status)
99
161
  create_app and return
100
162
  else
101
163
  raise AppLookupFailure.new(res.inspect)
102
164
  end
103
- else
104
- set_app(app)
105
165
  end
106
166
  else
107
167
  create_app
108
168
  end
109
169
  end
110
170
 
171
+ def set_server(server)
172
+ server['urls'].delete_if { |k,v| k !~ /\Aoauth/ }
173
+ set_state(:server, server)
174
+ @server = server
175
+ end
176
+
177
+ def get_server
178
+ @server ||= get_state(:server)
179
+ end
180
+
111
181
  def set_app(app)
112
- set_state(:app, app)
182
+ @tent_app = Hashie::Mash.new(app)
113
183
  end
114
184
 
115
185
  def get_app
116
- @tent_app ||= Hashie::Mash.new(get_state(:app) || {})
186
+ @tent_app || set_app(options[:get_app].call(get_state(:entity)))
117
187
  end
118
188
 
119
- def create_app
120
- client = ::TentClient.new(@server_url)
189
+ def build_app
121
190
  app_attrs = {
122
- :name => options.app.name,
123
- :description => options.app.description,
124
- :scopes => options.app.scopes,
125
- :icon => options.app.icon,
126
- :url => options.app.url,
127
- :redirect_uris => options.app.redirect_uris || [callback_url]
191
+ :type => "https://tent.io/types/app/v0#",
192
+ :content => {
193
+ :name => options[:app][:name],
194
+ :description => options[:app][:description],
195
+ :url => options[:app][:url],
196
+ :redirect_uri => options[:app][:redirect_uri],
197
+ :types => {
198
+ :read => options[:app][:read_types],
199
+ :write => options[:app][:write_types]
200
+ },
201
+ :notification_url => options[:app][:notification_url],
202
+ :notification_types => options[:app][:notification_types],
203
+ :scopes => options[:app][:scopes]
204
+ },
205
+ :permissions => {
206
+ :public => false
207
+ }
128
208
  }
129
209
 
130
- res = client.app.create(app_attrs)
210
+ app_icon_attrs = if options[:app][:icon]
211
+ if Hash === options[:app][:icon]
212
+ options[:app][:icon]
213
+ elsif options[:app][:icon].respond_to?(:path)
214
+ # Assume IO
215
+ filename = options[:app][:icon].path.to_s.split('/').last || 'appicon.png'
216
+ {
217
+ :content_type => "image/#{filename.split('.').last || 'png'}",
218
+ :category => 'icon',
219
+ :name => filename,
220
+ :data => options[:app][:icon]
221
+ }
222
+ elsif String === options[:app][:icon]
223
+ {
224
+ :content_type => "image/png",
225
+ :category => 'icon',
226
+ :name => "appicon.png",
227
+ :data => options[:app][:icon]
228
+ }
229
+ end
230
+ end
231
+ attachments = [app_icon_attrs].compact
232
+
233
+ [app_attrs, attachments]
234
+ end
235
+
236
+ def update_app(app, app_credentials)
237
+ client = ::TentClient.new(get_state(:entity), :credentials => app_credentials, :server_meta => @server_meta)
238
+
239
+ app_attrs, attachments = build_app
240
+ app_attrs[:version] = { :parents => [{ :version => app['version']['id'] }] }
241
+
242
+ res = client.post.update(app['entity'], app['id'], app_attrs)
131
243
 
132
- if (app = res.body) && !app.kind_of?(::String)
133
- set_app(app)
244
+ if res.success? && (Hash === res.body) && (Hash === res.body['post'])
245
+ set_app(res.body['post'].merge(:credentials => app_credentials.merge(:hawk_id => app_credentials.delete(:id))))
134
246
  options[:on_app_created].call(get_app, get_state(:entity))
247
+ set_server(res.env[:tent_server])
248
+ else
249
+ raise AppUpdateFailure.new(res.inspect)
250
+ end
251
+ end
252
+
253
+ def create_app
254
+ client = ::TentClient.new(get_state(:entity), :server_meta => @server_meta)
255
+
256
+ app_attrs, attachments = build_app
257
+ res = client.post.create(app_attrs, {}, :attachments => attachments)
258
+
259
+ if res.success? && (Hash === res.body) && (Hash === res.body['post'])
260
+ app = res.body['post']
261
+ credentials_post_link = ::TentClient::LinkHeader.parse(res.env[:response_headers]['Link'].to_s).links.find { |l| l[:rel] == 'https://tent.io/rels/credentials' }
262
+
263
+ if credentials_post_link && (credentials_post_res = client.http.get(credentials_post_link.uri.to_s)) && credentials_post_res.success?
264
+ credentials_post = Hashie::Mash.new(credentials_post_res.body).post
265
+ set_app(app.merge(:credentials => {
266
+ :hawk_id => credentials_post.id,
267
+ :hawk_key => credentials_post.content.hawk_key,
268
+ :hawk_algorithm => credentials_post.content.hawk_algorithm
269
+ }))
270
+ options[:on_app_created].call(get_app, get_state(:entity))
271
+ else
272
+ raise AppLookupFailure.new("Failed to fetch app credentials!")
273
+ end
274
+
275
+ set_server(res.env[:tent_server])
135
276
  else
136
277
  raise AppCreateFailure.new(res.inspect)
137
278
  end
138
279
  end
139
280
 
281
+ def generate_state
282
+ SecureRandom.hex(32)
283
+ end
284
+
140
285
  def build_uri_and_redirect!
141
- auth_uri = URI(@server_url + '/oauth/authorize')
286
+ auth_uri = URI(get_server['urls']['oauth_auth'])
287
+
142
288
  params = {
143
289
  :client_id => get_app[:id],
144
- :tent_profile_info_types => options[:profile_info_types].join(','),
145
- :tent_post_types => options[:post_types].join(','),
146
- :tent_notification_url => options[:notification_url],
147
- :scope => options[:app][:scopes].keys.join(','),
148
- :redirect_uri => Array(options.app.redirect_uris).first || callback_url,
149
290
  }
150
- params[:state] = set_state(:state, SecureRandom.hex(32))
151
- build_uri_params!(auth_uri, params)
291
+ params[:state] = set_state(:state, generate_state)
292
+
293
+ auth_uri.query = build_query_string(params)
152
294
 
153
295
  redirect auth_uri.to_s
154
296
  end
155
297
 
156
- def build_uri_params!(uri, params)
157
- uri.query = params.inject([]) do |memo, (key,val)|
298
+ def build_query_string(params)
299
+ params.inject([]) do |memo, (key,val)|
158
300
  memo << "#{key}=#{URI.encode_www_form_component(val)}"
159
301
  memo
160
302
  end.join('&')
161
303
  end
162
304
 
305
+ def check_error!
306
+ if request_params['error']
307
+ raise OAuthError.new(request_params['error'])
308
+ end
309
+ end
310
+
163
311
  def verify_state!
164
- raise StateMissmatchError unless get_state(:state) == request.params['state']
312
+ raise StateMissmatchError.new("Expected #{get_state(:state).inspect}, got #{request.params['state'].inspect}") unless get_state(:state) == request.params['state']
165
313
  end
166
314
 
167
- def create_app_authorization!
168
- client = ::TentClient.new(get_state(:server_url), :mac_key_id => get_app[:mac_key_id],
169
- :mac_key => get_app[:mac_key],
170
- :mac_algorithm => get_app[:mac_algorithm])
171
- res = client.app.authorization.create(get_app[:id], :code => request.params['code'])
172
- raise AppAuthorizationCreateFailure.new(res.body) if res.body.kind_of?(String)
173
- @app_authorization = Hashie::Mash.new(res.body)
315
+ def token_exchange!
316
+ app_credentials = get_app[:credentials].dup
317
+ app_credentials.merge!(:id => app_credentials.delete(:hawk_id))
318
+ client = ::TentClient.new(get_state(:entity), :credentials => app_credentials)
319
+ client.server_meta['servers'] = [get_server]
320
+
321
+ res = client.oauth_token_exchange(:code => request.params['code'], :token_type => 'https://tent.io/oauth/hawk-token')
322
+
323
+ raise AppAuthorizationCreateFailure.new(res.body) unless res.success?
324
+ @auth_credentials = Hashie::Mash.new(res.body)
174
325
  end
175
326
 
176
327
  def build_auth_hash!
177
328
  env['omniauth.auth'] = Hashie::Mash.new(
178
329
  :provider => 'tent',
179
330
  :uid => get_state(:entity),
180
- :info => extract_basic_info(get_state(:profile)),
181
331
  :credentials => {
182
- :token => @app_authorization.access_token,
183
- :secret => @app_authorization.mac_key
332
+ :token => @auth_credentials.access_token,
333
+ :secret => @auth_credentials.hawk_key
184
334
  },
185
335
  :extra => {
186
336
  :raw_info => {
187
- :profile => get_state(:profile),
188
- :app_authorization => @app_authorization,
337
+ :auth_credentials => @auth_credentials,
189
338
  :app => get_app
190
339
  },
191
340
  :credentials => {
192
- :mac_key_id => @app_authorization.access_token,
193
- :mac_key => @app_authorization.mac_key,
194
- :mac_algorithm => @app_authorization.mac_algorithm,
195
- :token_type => @app_authorization.token_type
341
+ :id => @auth_credentials.access_token,
342
+ :hawk_key => @auth_credentials.hawk_key,
343
+ :hawk_algorithm => @auth_credentials.hawk_algorithm,
344
+ :token_type => @auth_credentials.token_type
196
345
  }
197
346
  }
198
347
  )
199
348
  end
200
349
 
201
350
  def delete_state!
202
- %w( entity app server_url profile state ).each do |key|
351
+ session.delete('omniauth.tent-keys').to_a.each do |key|
203
352
  session.delete("omniauth.#{key}")
204
353
  end
205
354
  end
206
-
207
- def extract_basic_info(profile)
208
- basic_info = Hashie::Mash.new(profile.inject({}) { |memo, (k,v)|
209
- memo = v if k =~ %r{^https://tent.io/types/info/basic}
210
- memo
211
- })
212
-
213
- {
214
- :name => basic_info.name,
215
- :nickname => get_state(:entity),
216
- :image => basic_info.avatar
217
- }
218
- end
219
355
  end
220
356
  end
221
357
  end
@@ -25,7 +25,6 @@ Gem::Specification.new do |gem|
25
25
  gem.add_development_dependency 'webmock'
26
26
  gem.add_development_dependency 'bundler'
27
27
  gem.add_development_dependency 'rake'
28
- gem.add_development_dependency 'kicker'
29
28
  gem.add_development_dependency 'mocha'
30
29
  gem.add_development_dependency 'yajl-ruby'
31
30
  end
@@ -1,177 +1,385 @@
1
1
  require 'spec_helper'
2
2
  require 'yajl'
3
+ require 'uri'
3
4
 
4
5
  describe OmniAuth::Strategies::Tent do
5
- attr_accessor :app
6
-
7
- # customize rack app for testing, if block is given, reverts to default
8
- # rack app after testing is done
9
- def set_app!(tent_options = {})
10
- old_app = self.app
11
- self.app = Rack::Builder.app do
12
- use OmniAuth::Strategies::Tent, tent_options
6
+
7
+ def app
8
+ @app || set_app
9
+ end
10
+
11
+ def set_app(strategy_options = {})
12
+ @app = Rack::Builder.app do
13
+ use OmniAuth::Strategies::Tent, strategy_options
13
14
  run lambda{|env| [404, {'env' => env}, ["HELLO!"]]}
14
15
  end
15
- if block_given?
16
- yield
17
- self.app = old_app
18
- end
19
- self.app
20
16
  end
21
17
 
22
- before(:all) do
23
- set_app!
24
- end
18
+ let(:entity_uri) { "http://entity.example.org/xfapc" }
19
+ let(:server_url) { "http://tent.example.org/xfapc" }
20
+ let(:server_meta_post_url) { "#{server_url}/posts/#{URI.encode_www_form_component(entity_uri)}/meta-post-id" }
21
+ let(:link_header) {
22
+ %(<#{server_meta_post_url}>; rel="https://tent.io/rels/meta-post")
23
+ }
24
+ let(:meta_post) {
25
+ {
26
+ "id" => "DBhFT_BvKe4zKAPvZJaHMg",
27
+ "type" => "https://tent.io/types/meta/v0#",
28
+ "entity" => entity_uri,
29
+ "published_at" => 1371489957115,
30
+ "content" => {
31
+ "entity" => entity_uri,
32
+ "servers" => [{
33
+ "version" => "0.3",
34
+ "urls" => {
35
+ "oauth_auth" => "#{server_url}/oauth/authorize",
36
+ "oauth_token" => "#{server_url}/oauth/token",
37
+ "posts_feed" => "#{server_url}/posts",
38
+ "new_post" => "#{server_url}/posts",
39
+ "post" => "#{server_url}/posts/{entity}/{post}",
40
+ "post_attachment" => "#{server_url}/posts/{entity}/{post}/attachments/{name}",
41
+ "attachment" => "#{server_url}/attachments/{entity}/{digest}",
42
+ "batch" => "#{server_url}/batch",
43
+ "server_info" => "#{server_url}/server"
44
+ },
45
+ "preference" => 0
46
+ }]
47
+ },
48
+ "version" => {
49
+ "id" => "4fda2190756c2805ebd1094cc4a089982bac615279d8288cd4a4a9fdc4337761",
50
+ "published_at" => 1371489957115
51
+ }
52
+ }
53
+ }
25
54
 
26
- let(:env) { {'rack.session' => {}} }
55
+ let(:app_post_id) { 'app-post-id' }
56
+ let(:app_post) {
57
+ {
58
+ :id => app_post_id,
59
+ :published_at => (Time.now.to_f * 1000).to_i,
60
+ :type => "https://tent.io/types/app/v0#",
61
+ :content => {
62
+ :name => "Example App Name",
63
+ :description => "Example App Description",
64
+ :url => "http://someapp.example.com",
65
+ :redirect_uri => "http://someapp.example.com/oauth/callback",
66
+ :types => {
67
+ :read => %w( all ),
68
+ :write => %w( https://tent.io/types/status/v0# )
69
+ },
70
+ :notification_types => %w( all ),
71
+ :scopes => %w( import_posts )
72
+ },
73
+ :permissions => {
74
+ :public => false
75
+ }
76
+ }
77
+ }
27
78
 
28
- let(:fresh_strategy){ Class.new(OmniAuth::Strategies::Tent) }
79
+ let(:app_credentials_post_id) { 'app-credentials-post-id' }
80
+ let(:server_app_credentials_post_url) { "#{server_url}/posts/#{app_credentials_post_id}" }
81
+ let(:app_credentials_post) {
82
+ {
83
+ :id => app_credentials_post_id,
84
+ :published_at => (Time.now.to_f * 1000).to_i,
85
+ :type => "https://tent.io/types/credentials/v0#",
86
+ :content => {
87
+ :hawk_key => 'hawk-mac-key',
88
+ :hawk_algorithm => 'sha256'
89
+ },
90
+ :permissions => {
91
+ :public => false
92
+ }
93
+ }
94
+ }
29
95
 
30
- let(:tent_entity) { 'https://example.com' }
31
- let(:tent_server) { "#{tent_entity}/tent" }
32
- let(:app_id) { 'app-id-123' }
33
- let(:link_header) { %(<#{tent_server}/profile>; rel="%s") % TentClient::PROFILE_REL }
34
- let(:tent_profile) { %({"https://tent.io/types/info/core/v0.1.0":{"licenses":["http://creativecommons.org/licenses/by/3.0/"],"entity":"#{tent_entity}","servers":["#{tent_server}"]}}) }
35
- let(:app_attrs) do
96
+ let(:app_credentials) {
97
+ app_credentials_post[:content].merge(:hawk_id => app_credentials_post_id)
98
+ }
99
+
100
+ let(:access_token_hash) {
36
101
  {
37
- :name => "Example App",
38
- :description => "An example app",
39
- :scopes => { "read_posts" => "Display your posts feed" },
40
- :icon => "https://example.com/icon.png",
41
- :url => "https://example.com"
102
+ :access_token => 'app-auth-id',
103
+ :hawk_key => 'hawk-mac-key',
104
+ :hawk_algorithm => 'sha256',
105
+ :token_type => 'hawk'
42
106
  }
107
+ }
108
+
109
+ def server_named_url(name, params = {})
110
+ uri_template = meta_post['content']['servers'].first['urls'][name.to_s]
111
+ uri_template.gsub(/\{([^\}]+)\}/) { URI.encode_www_form_component(params[$1.to_sym]) || "#{$1}" }
43
112
  end
44
- let(:app_json) { %({"name":"Example App","id":"#{app_id}"}) }
45
- let(:app_hash) { Yajl::Parser.parse(app_json) }
46
113
 
47
- let(:token_code) { 'token-code-123abc' }
114
+ def stub_head_discovery!
115
+ stub_request(:head, entity_uri).to_return(:headers => { 'Link' => link_header })
116
+ end
48
117
 
49
- let(:access_token) { 'access-token-abc' }
50
- let(:mac_key) { 'mac-key-312' }
51
- let(:mac_algorithm) { 'hmac-sha-256' }
52
- let(:token_type) { 'mac' }
53
- let(:app_auth_json) { %({"access_token":"#{access_token}","mac_key":"#{mac_key}","mac_algorithm":"#{mac_algorithm}","token_type":"#{token_type}") }
118
+ def stub_meta_discovery!
119
+ stub_request(:any, server_meta_post_url).to_return(
120
+ :status => 200,
121
+ :headers => {
122
+ 'Content-Type' => 'application/json'
123
+ },
124
+ :body => Yajl::Encoder.encode(:post => meta_post)
125
+ )
126
+ end
54
127
 
55
- let(:stub_head_discovery!) do
56
- stub_request(:head, tent_entity).to_return(:headers => {'Link' => link_header})
128
+ def stub_app_create!
129
+ stub_request(:post, server_named_url(:new_post)).to_return(
130
+ :status => 200,
131
+ :headers => {
132
+ 'Content-Type' => 'application/json',
133
+ 'Link' => %(<#{server_app_credentials_post_url}>; rel="https://tent.io/rels/credentials")
134
+ },
135
+ :body => Yajl::Encoder.encode(:post => app_post)
136
+ )
57
137
  end
58
138
 
59
- let(:stub_profile_discovery!) do
60
- stub_request(:get, "#{tent_server}/profile").to_return(:body => tent_profile, :headers => {'Content-Type' => TentClient::MEDIA_TYPE})
139
+ def stub_fetch_app!
140
+ stub_request(:get, server_named_url(:post, :entity => entity_uri, :post => app_post_id)).to_return(
141
+ :status => 200,
142
+ :headers => {
143
+ 'Content-Type' => 'application/json',
144
+ },
145
+ :body => Yajl::Encoder.encode(:post => app_post)
146
+ )
61
147
  end
62
148
 
63
- let(:stub_app_lookup_success!) do
64
- stub_request(:get, "#{tent_server}/apps/#{app_id}").to_return(:body => app_json, :headers => { 'Content-Type' => TentClient::MEDIA_TYPE })
149
+ def stub_fetch_app_failure!
150
+ stub_request(:get, server_named_url(:post, :entity => entity_uri, :post => app_post_id)).to_return(
151
+ :status => 404,
152
+ :headers => {
153
+ 'Content-Type' => 'application/json',
154
+ },
155
+ :body => Yajl::Encoder.encode(:error => 'Not Found')
156
+ )
65
157
  end
66
158
 
67
- let(:stub_app_lookup_failure!) do
68
- stub_request(:get, "#{tent_server}/apps/#{app_id}").to_return(:status => 404)
159
+ def stub_fetch_app_credentials!
160
+ stub_request(:get, server_app_credentials_post_url).to_return(
161
+ :status => 200,
162
+ :headers => {
163
+ 'Content-Type' => 'application/json',
164
+ },
165
+ :body => Yajl::Encoder.encode(:post => app_credentials_post)
166
+ )
69
167
  end
70
168
 
71
- let(:stub_app_create_success!) do
72
- stub_request(:post, "#{tent_server}/apps").to_return(:body => app_json, :headers => { 'Content-Type' => TentClient::MEDIA_TYPE })
169
+ def stub_app_auth_create!
170
+ stub_request(:post, server_named_url(:oauth_token)).to_return(
171
+ :status => 200,
172
+ :headers => {
173
+ 'Content-Type' => 'application/json'
174
+ },
175
+ :body => Yajl::Encoder.encode(access_token_hash)
176
+ )
73
177
  end
74
178
 
75
- let(:stub_app_auth_create_success!) do
76
- stub_request(:post, "#{tent_server}/apps/#{app_id}/authorizations").with(:body => Yajl::Encoder.encode({ :code => token_code })).to_return(:body => app_auth_json, :headers => { 'Content-Type' => TentClient::MEDIA_TYPE })
179
+ let(:env) { {'rack.session' => {}} }
180
+
181
+ let(:app_attrs) do
182
+ {
183
+ :name => app_post[:content][:name],
184
+ :description => app_post[:content][:description],
185
+ :url => app_post[:content][:url],
186
+ :redirect_uri => app_post[:content][:redirect_uri],
187
+ :read_types => app_post[:content][:types][:read],
188
+ :write_types => app_post[:content][:types][:write],
189
+ :notification_types => app_post[:content][:notification_types],
190
+ :notification_url => app_post[:content][:notification_url],
191
+ :scopes => app_post[:content][:scopes],
192
+ }
77
193
  end
78
194
 
79
195
  describe '#request_phase' do
80
- it 'should display a form' do
196
+ it 'displays a form' do
81
197
  get '/auth/tent', {}, env
82
198
  expect(last_response.body).to be_include("<form")
83
199
  end
84
200
 
85
- it 'should perform disvocery' do
201
+ it 'performs disvocery' do
86
202
  head_stub = stub_head_discovery!
87
- profile_stub = stub_profile_discovery!
203
+ meta_stub = stub_meta_discovery!
88
204
 
89
205
  described_class.any_instance.stubs(:find_or_create_app!)
90
206
  described_class.any_instance.stubs(:build_uri_and_redirect!).returns([200, {}, []])
91
207
 
92
- post '/auth/tent', { :entity => tent_entity }, env
208
+ post '/auth/tent', { :entity => entity_uri }, env
93
209
 
94
210
  expect(head_stub).to have_been_requested
95
- expect(profile_stub).to have_been_requested
211
+ expect(meta_stub).to have_been_requested
96
212
  end
97
213
 
98
- it 'should create app if app_id callback returns nil' do
99
- set_app!(:app => app_attrs)
100
- stub_head_discovery!
101
- stub_profile_discovery!
102
- app_create_stub = stub_app_create_success!
103
- described_class.any_instance.stubs(:build_uri_and_redirect!).returns([200, {}, []])
214
+ creates_app = proc do
215
+ it 'creates app' do
216
+ stub_head_discovery!
217
+ stub_meta_discovery!
218
+ app_create_stub = stub_app_create!
219
+ fetch_credentials_stub = stub_fetch_app_credentials!
220
+
221
+ described_class.any_instance.stubs(:build_uri_and_redirect!).returns([200, {}, []])
104
222
 
105
- post '/auth/tent', { :entity => tent_entity }, env
223
+ post '/auth/tent', { :entity => entity_uri }, env
106
224
 
107
- expect(app_create_stub).to have_been_requested
225
+ expect(app_create_stub).to have_been_requested
226
+ expect(fetch_credentials_stub).to have_been_requested
227
+ end
108
228
  end
109
229
 
110
- it 'should create app if not found' do
111
- set_app!(:app => app_attrs, :on_app_created => mock(:call))
112
- stub_head_discovery!
113
- stub_profile_discovery!
114
- stub_app_lookup_failure!
115
- app_create_stub = stub_app_create_success!
116
- described_class.any_instance.stubs(:build_uri_and_redirect!).returns([200, {}, []])
230
+ builds_uri_and_redirects = proc do
231
+ it 'builds uri and redirects' do
232
+ stub_head_discovery!
233
+ stub_meta_discovery!
234
+ stub_fetch_app!
117
235
 
118
- post '/auth/tent', { :entity => tent_entity }, env
236
+ post '/auth/tent', { :entity => entity_uri }, env
119
237
 
120
- expect(app_create_stub).to have_been_requested
238
+ expect(last_response.status).to eq(302)
239
+ expect(last_response.headers["Location"]).to match(%r{\A#{Regexp.escape(server_named_url(:oauth_auth))}})
240
+ expect(last_response.headers["Location"]).to match(%r{client_id=#{app_post_id}})
241
+ end
121
242
  end
122
243
 
123
- it 'should build uri and redirect' do
124
- set_app!(:get_app => lambda { |entity| app_hash })
125
- stub_head_discovery!
126
- stub_profile_discovery!
127
- stub_app_lookup_success!
244
+ context 'when app_id proc returns nil' do
245
+ before do
246
+ set_app(:app => app_attrs)
247
+ end
248
+
249
+ context &creates_app
250
+ end
128
251
 
129
- post '/auth/tent', { :entity => tent_entity }, env
252
+ context 'when app not found' do
253
+ context 'when lookup fails' do
254
+ before do
255
+ set_app(:app => app_attrs, :get_app => lambda { |entity| })
256
+ end
130
257
 
131
- expect(last_response.status).to eq(302)
132
- expect(last_response.headers["Location"]).to match(%r{^#{tent_server}/oauth/authorize})
133
- expect(last_response.headers["Location"]).to match(%r{client_id=#{app_id}})
258
+ context &creates_app
259
+ end
260
+
261
+ context 'when fetch fails' do
262
+ before do
263
+ set_app(:app => app_attrs, :get_app => lambda { |entity| app_post.merge(:credentials => app_credentials) })
264
+ stub_fetch_app_failure!
265
+ end
266
+
267
+ context &creates_app
268
+ end
269
+ end
270
+
271
+ context 'when app found' do
272
+ before do
273
+ set_app(:app => app_attrs, :get_app => lambda { |entity| app_post.merge(:credentials => app_credentials) })
274
+ end
275
+
276
+ context &builds_uri_and_redirects
134
277
  end
135
278
  end
136
279
 
137
- describe '#callback_phase' do
138
- it 'should create app authorization' do
139
- state = 'abcdef'
140
- session = {}
280
+ describe "#callback_phase" do
281
+ let(:state) { 'request-state' }
282
+ let(:session) { Hash.new }
283
+
284
+ let(:token_code) { 'token-code' }
285
+
286
+ before do
141
287
  session['omniauth.state'] = state
142
- session['omniauth.entity'] = tent_entity
143
- session['omniauth.server_url'] = tent_server
144
- session['omniauth.app'] = { :id => app_id }
145
- session['omniauth.profile'] = Yajl::Parser.parse(tent_profile)
288
+ session['omniauth.entity'] = entity_uri
289
+ session['omniauth.server'] = meta_post['content']['servers'].first
290
+ end
146
291
 
147
- stub_app_auth_create_success!
148
- stub_app_lookup_success!
292
+ it 'creates app authorization' do
293
+ app = app_post.merge(:credentials => app_credentials)
294
+ set_app(:app => app_attrs, :get_app => proc { |e| app })
295
+
296
+ stub_head_discovery!
297
+ stub_meta_discovery!
298
+ create_auth_stub = stub_app_auth_create!
149
299
 
150
300
  get '/auth/tent/callback', { :code => token_code, :state => state }, 'rack.session' => session
151
301
 
302
+ expect(create_auth_stub).to have_been_requested
303
+
152
304
  auth_hash = last_response['env']['omniauth.auth']
153
- expect(auth_hash).to_not be_nil
154
- expect(auth_hash.provider).to eq('tent')
155
- expect(auth_hash.uid).to eq(tent_entity)
156
- expect(auth_hash.info).to eq(Hashie::Mash.new(
157
- :name => nil,
158
- :nickname => tent_entity,
159
- :image => nil
305
+ expect(auth_hash).to be_kind_of(Hashie::Mash)
306
+
307
+ expect(auth_hash.provider).to eql('tent')
308
+ expect(auth_hash.uid).to eql(entity_uri)
309
+ expect(auth_hash.credentials).to eql(Hashie::Mash.new(
310
+ :token => access_token_hash[:access_token],
311
+ :secret => access_token_hash[:hawk_key]
160
312
  ))
161
- expect(auth_hash.credentials).to eq(Hashie::Mash.new(
162
- :token => access_token,
163
- :secret => mac_key
313
+
314
+ expect(auth_hash.extra).to be_kind_of(Hashie::Mash)
315
+ expect(auth_hash.extra.credentials).to eql(Hashie::Mash.new(
316
+ :id => access_token_hash[:access_token],
317
+ :hawk_key => access_token_hash[:hawk_key],
318
+ :hawk_algorithm => access_token_hash[:hawk_algorithm],
319
+ :token_type => access_token_hash[:token_type]
164
320
  ))
165
- expect(auth_hash.extra.raw_info.profile).to eq(Hashie::Mash.new(Yajl::Parser.parse(tent_profile)))
166
- expect(auth_hash.extra.credentials).to eq(Hashie::Mash.new(
167
- :mac_key_id => access_token,
168
- :mac_key => mac_key,
169
- :mac_algorithm => mac_algorithm,
170
- :token_type => token_type
321
+
322
+ expect(auth_hash.extra.raw_info).to be_kind_of(Hashie::Mash)
323
+ expect(auth_hash.extra.raw_info.auth_credentials).to eql(Hashie::Mash.new(access_token_hash))
324
+ expect(auth_hash.extra.raw_info.app).to eql(Hashie::Mash.new(app_post.merge(:credentials => app_credentials)))
325
+ end
326
+ end
327
+
328
+ describe "full flow" do
329
+ let(:state) { 'request-state' }
330
+ let(:token_code) { 'token-code' }
331
+
332
+ before do
333
+ described_class.any_instance.stubs(:generate_state).returns(state)
334
+ end
335
+
336
+ it "maintains state through full oauth flow" do
337
+ app = nil
338
+ set_app(:app => app_attrs, :on_app_created => proc { |a, e| app = a }, :get_app => proc { |e| app })
339
+
340
+ ##
341
+ # Request Phase
342
+
343
+ stub_head_discovery!
344
+ stub_meta_discovery!
345
+ app_create_stub = stub_app_create!
346
+ fetch_credentials_stub = stub_fetch_app_credentials!
347
+
348
+ post '/auth/tent', { :entity => entity_uri }, env
349
+
350
+ expect(last_response.status).to eq(302)
351
+ expect(last_response.headers["Location"]).to match(%r{\A#{Regexp.escape(server_named_url(:oauth_auth))}})
352
+ expect(last_response.headers["Location"]).to match(%r{client_id=#{app_post_id}})
353
+
354
+ ##
355
+ # Callback Phase
356
+
357
+ create_auth_stub = stub_app_auth_create!
358
+ get '/auth/tent/callback', { :code => token_code, :state => state }, env
359
+
360
+ expect(create_auth_stub).to have_been_requested
361
+
362
+ auth_hash = last_response['env']['omniauth.auth']
363
+ expect(auth_hash).to be_kind_of(Hashie::Mash)
364
+
365
+ expect(auth_hash.provider).to eql('tent')
366
+ expect(auth_hash.uid).to eql(entity_uri)
367
+ expect(auth_hash.credentials).to eql(Hashie::Mash.new(
368
+ :token => access_token_hash[:access_token],
369
+ :secret => access_token_hash[:hawk_key]
171
370
  ))
172
- expect(auth_hash.extra.raw_info.app_authorization).to eq(Hashie::Mash.new(
173
- Yajl::Parser.parse(app_auth_json)
371
+
372
+ expect(auth_hash.extra).to be_kind_of(Hashie::Mash)
373
+ expect(auth_hash.extra.credentials).to eql(Hashie::Mash.new(
374
+ :id => access_token_hash[:access_token],
375
+ :hawk_key => access_token_hash[:hawk_key],
376
+ :hawk_algorithm => access_token_hash[:hawk_algorithm],
377
+ :token_type => access_token_hash[:token_type]
174
378
  ))
379
+
380
+ expect(auth_hash.extra.raw_info).to be_kind_of(Hashie::Mash)
381
+ expect(auth_hash.extra.raw_info.auth_credentials).to eql(Hashie::Mash.new(access_token_hash))
382
+ expect(auth_hash.extra.raw_info.app).to eql(Hashie::Mash.new(app_post.merge(:credentials => app_credentials)))
175
383
  end
176
384
  end
177
385
  end
@@ -2,7 +2,7 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
3
 
4
4
  require 'bundler/setup'
5
- require 'mocha_standalone'
5
+ require 'mocha/api'
6
6
  require 'rack/test'
7
7
  require 'webmock/rspec'
8
8
  require 'omniauth-tent'
@@ -10,10 +10,14 @@ require 'omniauth-tent'
10
10
  Dir["#{File.dirname(__FILE__)}/support/*.rb"].each { |f| require f }
11
11
 
12
12
  RSpec.configure do |config|
13
+ config.color = true
13
14
  config.include WebMock::API
14
15
  config.include Rack::Test::Methods
15
16
  config.extend OmniAuth::Test::StrategyMacros, :type => :strategy
16
17
  config.mock_with :mocha
18
+ config.expect_with :rspec do |c|
19
+ c.syntax = :expect
20
+ end
17
21
  end
18
22
 
19
- OmniAuth.config.logger = Logger.new(File.join(File.dirname(__FILE__), '..', 'log', 'test.log'))
23
+ OmniAuth.config.logger = Logger.new("/dev/null")
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omniauth-tent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
5
- prerelease:
4
+ version: 0.2.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Jesse Stuart
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-04-05 00:00:00.000000000 Z
11
+ date: 2013-09-06 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: omniauth
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,23 +27,20 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: tent-client
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rspec
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ~>
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ~>
60
53
  - !ruby/object:Gem::Version
@@ -62,113 +55,85 @@ dependencies:
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: rack-test
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: webmock
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - '>='
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - '>='
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: bundler
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ! '>='
87
+ - - '>='
100
88
  - !ruby/object:Gem::Version
101
89
  version: '0'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ! '>='
94
+ - - '>='
108
95
  - !ruby/object:Gem::Version
109
96
  version: '0'
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: rake
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ! '>='
101
+ - - '>='
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ! '>='
124
- - !ruby/object:Gem::Version
125
- version: '0'
126
- - !ruby/object:Gem::Dependency
127
- name: kicker
128
- requirement: !ruby/object:Gem::Requirement
129
- none: false
130
- requirements:
131
- - - ! '>='
132
- - !ruby/object:Gem::Version
133
- version: '0'
134
- type: :development
135
- prerelease: false
136
- version_requirements: !ruby/object:Gem::Requirement
137
- none: false
138
- requirements:
139
- - - ! '>='
108
+ - - '>='
140
109
  - !ruby/object:Gem::Version
141
110
  version: '0'
142
111
  - !ruby/object:Gem::Dependency
143
112
  name: mocha
144
113
  requirement: !ruby/object:Gem::Requirement
145
- none: false
146
114
  requirements:
147
- - - ! '>='
115
+ - - '>='
148
116
  - !ruby/object:Gem::Version
149
117
  version: '0'
150
118
  type: :development
151
119
  prerelease: false
152
120
  version_requirements: !ruby/object:Gem::Requirement
153
- none: false
154
121
  requirements:
155
- - - ! '>='
122
+ - - '>='
156
123
  - !ruby/object:Gem::Version
157
124
  version: '0'
158
125
  - !ruby/object:Gem::Dependency
159
126
  name: yajl-ruby
160
127
  requirement: !ruby/object:Gem::Requirement
161
- none: false
162
128
  requirements:
163
- - - ! '>='
129
+ - - '>='
164
130
  - !ruby/object:Gem::Version
165
131
  version: '0'
166
132
  type: :development
167
133
  prerelease: false
168
134
  version_requirements: !ruby/object:Gem::Requirement
169
- none: false
170
135
  requirements:
171
- - - ! '>='
136
+ - - '>='
172
137
  - !ruby/object:Gem::Version
173
138
  version: '0'
174
139
  description: Omniauth Strategy for Tent
@@ -193,27 +158,26 @@ files:
193
158
  - spec/spec_helper.rb
194
159
  homepage: https://github.com/tent/omniauth-tent
195
160
  licenses: []
161
+ metadata: {}
196
162
  post_install_message:
197
163
  rdoc_options: []
198
164
  require_paths:
199
165
  - lib
200
166
  required_ruby_version: !ruby/object:Gem::Requirement
201
- none: false
202
167
  requirements:
203
- - - ! '>='
168
+ - - '>='
204
169
  - !ruby/object:Gem::Version
205
170
  version: '0'
206
171
  required_rubygems_version: !ruby/object:Gem::Requirement
207
- none: false
208
172
  requirements:
209
- - - ! '>='
173
+ - - '>='
210
174
  - !ruby/object:Gem::Version
211
175
  version: '0'
212
176
  requirements: []
213
177
  rubyforge_project:
214
- rubygems_version: 1.8.23
178
+ rubygems_version: 2.0.7
215
179
  signing_key:
216
- specification_version: 3
180
+ specification_version: 4
217
181
  summary: Omniauth Strategy for Tent
218
182
  test_files:
219
183
  - spec/omniauth/strategies/tent_spec.rb