songkick-oauth2-provider 0.10.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.
Files changed (47) hide show
  1. data/README.rdoc +394 -0
  2. data/example/README.rdoc +11 -0
  3. data/example/application.rb +159 -0
  4. data/example/config.ru +3 -0
  5. data/example/environment.rb +11 -0
  6. data/example/models/connection.rb +9 -0
  7. data/example/models/note.rb +4 -0
  8. data/example/models/user.rb +6 -0
  9. data/example/public/style.css +78 -0
  10. data/example/schema.rb +27 -0
  11. data/example/views/authorize.erb +28 -0
  12. data/example/views/create_user.erb +3 -0
  13. data/example/views/error.erb +6 -0
  14. data/example/views/home.erb +25 -0
  15. data/example/views/layout.erb +25 -0
  16. data/example/views/login.erb +20 -0
  17. data/example/views/new_client.erb +25 -0
  18. data/example/views/new_user.erb +22 -0
  19. data/example/views/show_client.erb +15 -0
  20. data/lib/songkick/oauth2/model.rb +20 -0
  21. data/lib/songkick/oauth2/model/authorization.rb +126 -0
  22. data/lib/songkick/oauth2/model/client.rb +61 -0
  23. data/lib/songkick/oauth2/model/client_owner.rb +15 -0
  24. data/lib/songkick/oauth2/model/hashing.rb +29 -0
  25. data/lib/songkick/oauth2/model/resource_owner.rb +54 -0
  26. data/lib/songkick/oauth2/provider.rb +122 -0
  27. data/lib/songkick/oauth2/provider/access_token.rb +68 -0
  28. data/lib/songkick/oauth2/provider/authorization.rb +190 -0
  29. data/lib/songkick/oauth2/provider/error.rb +22 -0
  30. data/lib/songkick/oauth2/provider/exchange.rb +227 -0
  31. data/lib/songkick/oauth2/router.rb +79 -0
  32. data/lib/songkick/oauth2/schema.rb +17 -0
  33. data/lib/songkick/oauth2/schema/20120828112156_songkick_oauth2_schema_original_schema.rb +36 -0
  34. data/spec/factories.rb +27 -0
  35. data/spec/request_helpers.rb +52 -0
  36. data/spec/songkick/oauth2/model/authorization_spec.rb +216 -0
  37. data/spec/songkick/oauth2/model/client_spec.rb +55 -0
  38. data/spec/songkick/oauth2/model/resource_owner_spec.rb +88 -0
  39. data/spec/songkick/oauth2/provider/access_token_spec.rb +125 -0
  40. data/spec/songkick/oauth2/provider/authorization_spec.rb +346 -0
  41. data/spec/songkick/oauth2/provider/exchange_spec.rb +353 -0
  42. data/spec/songkick/oauth2/provider_spec.rb +545 -0
  43. data/spec/spec_helper.rb +62 -0
  44. data/spec/test_app/helper.rb +33 -0
  45. data/spec/test_app/provider/application.rb +68 -0
  46. data/spec/test_app/provider/views/authorize.erb +19 -0
  47. metadata +273 -0
@@ -0,0 +1,346 @@
1
+ require 'spec_helper'
2
+
3
+ describe Songkick::OAuth2::Provider::Authorization do
4
+ let(:resource_owner) { TestApp::User['Bob'] }
5
+
6
+ let(:authorization) { Songkick::OAuth2::Provider::Authorization.new(resource_owner, params) }
7
+
8
+ let(:params) { { 'response_type' => 'code',
9
+ 'client_id' => @client.client_id,
10
+ 'redirect_uri' => @client.redirect_uri }
11
+ }
12
+
13
+ before do
14
+ @client = Factory(:client)
15
+ Songkick::OAuth2.stub(:random_string).and_return('s1', 's2', 's3')
16
+ end
17
+
18
+ describe "with valid parameters" do
19
+ it "is valid" do
20
+ authorization.error.should be_nil
21
+ end
22
+ end
23
+
24
+ describe "with the scope parameter" do
25
+ before { params['scope'] = 'foo bar qux' }
26
+
27
+ it "exposes the scope as a list of strings" do
28
+ authorization.scopes.should == Set.new(%w[foo bar qux])
29
+ end
30
+
31
+ it "exposes the scopes the client has not yet granted" do
32
+ authorization.unauthorized_scopes.should == Set.new(%w[foo bar qux])
33
+ end
34
+
35
+ describe "when the owner has already authorized the client" do
36
+ before do
37
+ create_authorization(:owner => resource_owner, :client => @client, :scope => 'foo bar')
38
+ end
39
+
40
+ it "exposes the scope as a list of strings" do
41
+ authorization.scopes.should == Set.new(%w[foo bar qux])
42
+ end
43
+
44
+ it "exposes the scopes the client has not yet granted" do
45
+ authorization.unauthorized_scopes.should == %w[qux]
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "missing response_type" do
51
+ before { params.delete('response_type') }
52
+
53
+ it "is invalid" do
54
+ authorization.error.should == "invalid_request"
55
+ authorization.error_description.should == "Missing required parameter response_type"
56
+ end
57
+ end
58
+
59
+ describe "with a bad response_type" do
60
+ before { params['response_type'] = "no_such_type" }
61
+
62
+ it "is invalid" do
63
+ authorization.error.should == "unsupported_response_type"
64
+ authorization.error_description.should == "Response type no_such_type is not supported"
65
+ end
66
+
67
+ it "causes a redirect" do
68
+ authorization.should be_redirect
69
+ authorization.redirect_uri.should == "https://client.example.com/cb?error=unsupported_response_type&error_description=Response+type+no_such_type+is+not+supported"
70
+ end
71
+ end
72
+
73
+ describe "missing client_id" do
74
+ before { params.delete('client_id') }
75
+
76
+ it "is invalid" do
77
+ authorization.error.should == "invalid_request"
78
+ authorization.error_description.should == "Missing required parameter client_id"
79
+ end
80
+
81
+ it "does not cause a redirect" do
82
+ authorization.should_not be_redirect
83
+ end
84
+ end
85
+
86
+ describe "with an unknown client_id" do
87
+ before { params['client_id'] = "unknown" }
88
+
89
+ it "is invalid" do
90
+ authorization.error.should == "invalid_client"
91
+ authorization.error_description.should == "Unknown client ID unknown"
92
+ end
93
+
94
+ it "does not cause a redirect" do
95
+ authorization.should_not be_redirect
96
+ end
97
+ end
98
+
99
+ describe "missing redirect_uri" do
100
+ before { params.delete('redirect_uri') }
101
+
102
+ it "is invalid" do
103
+ authorization.error.should == "invalid_request"
104
+ authorization.error_description.should == "Missing required parameter redirect_uri"
105
+ end
106
+
107
+ it "causes a redirect to the client's registered redirect_uri" do
108
+ authorization.should be_redirect
109
+ authorization.redirect_uri.should == "https://client.example.com/cb?error=invalid_request&error_description=Missing+required+parameter+redirect_uri"
110
+ end
111
+ end
112
+
113
+ describe "with a mismatched redirect_uri" do
114
+ before { params['redirect_uri'] = "http://songkick.com" }
115
+
116
+ it "is invalid" do
117
+ authorization.error.should == "redirect_uri_mismatch"
118
+ authorization.error_description.should == "Parameter redirect_uri does not match registered URI"
119
+ end
120
+
121
+ it "causes a redirect to the client's registered redirect_uri" do
122
+ authorization.should be_redirect
123
+ authorization.redirect_uri.should == "https://client.example.com/cb?error=redirect_uri_mismatch&error_description=Parameter+redirect_uri+does+not+match+registered+URI"
124
+ end
125
+
126
+ describe "when the client has not registered a redirect_uri" do
127
+ before { @client.update_attribute(:redirect_uri, nil) }
128
+
129
+ it "is valid" do
130
+ authorization.error.should be_nil
131
+ end
132
+ end
133
+ end
134
+
135
+ # http://en.wikipedia.org/wiki/HTTP_response_splitting
136
+ # scope and state values are passed back in the redirect
137
+
138
+ describe "with an illegal scope" do
139
+ before { params['scope'] = "http\r\nsplitter" }
140
+
141
+ it "is invalid" do
142
+ authorization.error.should == "invalid_request"
143
+ authorization.error_description.should == "Illegal value for scope parameter"
144
+ end
145
+ end
146
+
147
+ describe "with an illegal state" do
148
+ before { params['state'] = "http\r\nsplitter" }
149
+
150
+ it "is invalid" do
151
+ authorization.error.should == "invalid_request"
152
+ authorization.error_description.should == "Illegal value for state parameter"
153
+ end
154
+ end
155
+
156
+ describe "#grant_access!" do
157
+ describe "when there is an existing authorization with no code" do
158
+ before do
159
+ @model = Factory(:authorization,
160
+ :owner => resource_owner,
161
+ :client => @client,
162
+ :code => nil)
163
+ end
164
+
165
+ it "generates and returns a code to the client" do
166
+ authorization.grant_access!
167
+ @model.reload
168
+ @model.code.should == "s1"
169
+ authorization.code.should == "s1"
170
+ end
171
+ end
172
+
173
+ describe "when there is an existing authorization with scopes" do
174
+ before do
175
+ @model = Factory(:authorization,
176
+ :owner => resource_owner,
177
+ :client => @client,
178
+ :code => nil,
179
+ :scope => 'foo bar')
180
+
181
+ params['scope'] = 'qux'
182
+ end
183
+
184
+ it "merges the new scopes with the existing ones" do
185
+ authorization.grant_access!
186
+ @model.reload
187
+ @model.scopes.should == Set.new(%w[foo bar qux])
188
+ end
189
+ end
190
+
191
+ describe "when there is an existing expired authorization" do
192
+ before do
193
+ @model = Factory(:authorization,
194
+ :owner => resource_owner,
195
+ :client => @client,
196
+ :expires_at => 2.months.ago,
197
+ :code => 'existing_code',
198
+ :scope => 'foo bar')
199
+ end
200
+
201
+ it "renews the authorization" do
202
+ authorization.grant_access!
203
+ @model.reload
204
+ @model.expires_at.should be_nil
205
+ end
206
+
207
+ it "returns a code to the client" do
208
+ authorization.grant_access!
209
+ authorization.code.should == "existing_code"
210
+ authorization.access_token.should be_nil
211
+ end
212
+
213
+ it "sets the expiry time if a duration is given" do
214
+ authorization.grant_access!(:duration => 1.hour)
215
+ @model.reload
216
+ @model.expires_in.should == 3600
217
+ authorization.expires_in.should == 3600
218
+ end
219
+
220
+ it "augments the scope" do
221
+ params['scope'] = 'qux'
222
+ authorization.grant_access!
223
+ @model.reload
224
+ @model.scopes.should == Set.new(%w[foo bar qux])
225
+ end
226
+ end
227
+
228
+ describe "for code requests" do
229
+ before do
230
+ params['response_type'] = 'code'
231
+ params['scope'] = 'foo bar'
232
+ end
233
+
234
+ it "makes the authorization redirect" do
235
+ authorization.grant_access!
236
+ authorization.client.should_not be_nil
237
+ authorization.should be_redirect
238
+ end
239
+
240
+ it "creates a code for the authorization" do
241
+ authorization.grant_access!
242
+ authorization.code.should == "s1"
243
+ authorization.access_token.should be_nil
244
+ authorization.expires_in.should be_nil
245
+ end
246
+
247
+ it "creates an Authorization in the database" do
248
+ authorization.grant_access!
249
+
250
+ authorization = Songkick::OAuth2::Model::Authorization.first
251
+ authorization.owner.should == resource_owner
252
+ authorization.client.should == @client
253
+ authorization.code.should == "s1"
254
+ authorization.scopes.should == Set.new(%w[foo bar])
255
+ end
256
+ end
257
+
258
+ describe "for token requests" do
259
+ before { params['response_type'] = 'token' }
260
+
261
+ it "creates a token for the authorization" do
262
+ authorization.grant_access!
263
+ authorization.code.should be_nil
264
+ authorization.access_token.should == "s1"
265
+ authorization.refresh_token.should == "s2"
266
+ authorization.expires_in.should be_nil
267
+ end
268
+
269
+ it "creates an Authorization in the database" do
270
+ authorization.grant_access!
271
+
272
+ authorization = Songkick::OAuth2::Model::Authorization.first
273
+ authorization.owner.should == resource_owner
274
+ authorization.client.should == @client
275
+ authorization.code.should be_nil
276
+ authorization.access_token_hash.should == Songkick::OAuth2.hashify("s1")
277
+ authorization.refresh_token_hash.should == Songkick::OAuth2.hashify("s2")
278
+ end
279
+ end
280
+
281
+ describe "for code_and_token requests" do
282
+ before { params['response_type'] = 'code_and_token' }
283
+
284
+ it "creates a code and token for the authorization" do
285
+ authorization.grant_access!
286
+ authorization.code.should == "s1"
287
+ authorization.access_token.should == "s2"
288
+ authorization.refresh_token.should == "s3"
289
+ authorization.expires_in.should be_nil
290
+ end
291
+
292
+ it "creates an Authorization in the database" do
293
+ authorization.grant_access!
294
+
295
+ authorization = Songkick::OAuth2::Model::Authorization.first
296
+ authorization.owner.should == resource_owner
297
+ authorization.client.should == @client
298
+ authorization.code.should == "s1"
299
+ authorization.access_token_hash.should == Songkick::OAuth2.hashify("s2")
300
+ authorization.refresh_token_hash.should == Songkick::OAuth2.hashify("s3")
301
+ end
302
+ end
303
+ end
304
+
305
+ describe "#deny_access!" do
306
+ it "puts the authorization in an error state" do
307
+ authorization.deny_access!
308
+ authorization.error.should == "access_denied"
309
+ authorization.error_description.should == "The user denied you access"
310
+ end
311
+
312
+ it "does not create an Authorization" do
313
+ Songkick::OAuth2::Model::Authorization.should_not_receive(:create)
314
+ Songkick::OAuth2::Model::Authorization.should_not_receive(:new)
315
+ authorization.deny_access!
316
+ end
317
+ end
318
+
319
+ describe "#params" do
320
+ before do
321
+ params['scope'] = params['state'] = 'valid'
322
+ params['controller'] = 'invalid'
323
+ end
324
+
325
+ it "only exposes OAuth-related parameters" do
326
+ authorization.params.should == {
327
+ 'response_type' => 'code',
328
+ 'client_id' => @client.client_id,
329
+ 'redirect_uri' => @client.redirect_uri,
330
+ 'state' => 'valid',
331
+ 'scope' => 'valid'
332
+ }
333
+ end
334
+
335
+ it "does not expose parameters with no value" do
336
+ params.delete('scope')
337
+ authorization.params.should == {
338
+ 'response_type' => 'code',
339
+ 'client_id' => @client.client_id,
340
+ 'redirect_uri' => @client.redirect_uri,
341
+ 'state' => 'valid'
342
+ }
343
+ end
344
+ end
345
+ end
346
+
@@ -0,0 +1,353 @@
1
+ require 'spec_helper'
2
+
3
+ describe Songkick::OAuth2::Provider::Exchange do
4
+ before do
5
+ @client = Factory(:client)
6
+ @alice = TestApp::User['Alice']
7
+ @bob = TestApp::User['Bob']
8
+ @authorization = Factory(:authorization, :client => @client, :owner => @bob, :scope => 'foo bar')
9
+ Songkick::OAuth2.stub(:random_string).and_return('random_string')
10
+ end
11
+
12
+ let(:exchange) { Songkick::OAuth2::Provider::Exchange.new(@bob, params) }
13
+
14
+ shared_examples_for "validates required parameters" do
15
+ describe "missing grant_type" do
16
+ before { params.delete('client_id') }
17
+
18
+ it "is invalid" do
19
+ exchange.error.should == "invalid_request"
20
+ exchange.error_description.should == "Missing required parameter client_id"
21
+ end
22
+ end
23
+
24
+ describe "with an unknown grant type" do
25
+ before { params['grant_type'] = 'unknown' }
26
+
27
+ it "is invalid" do
28
+ exchange.error.should == "unsupported_grant_type"
29
+ exchange.error_description.should == "The grant type unknown is not recognized"
30
+ end
31
+ end
32
+
33
+ describe "missing client_id" do
34
+ before { params.delete('client_id') }
35
+
36
+ it "is invalid" do
37
+ exchange.error.should == "invalid_request"
38
+ exchange.error_description.should == "Missing required parameter client_id"
39
+ end
40
+ end
41
+
42
+ describe "with an unknown client_id" do
43
+ before { params['client_id'] = "unknown" }
44
+
45
+ it "is invalid" do
46
+ exchange.error.should == "invalid_client"
47
+ exchange.error_description.should == "Unknown client ID unknown"
48
+ end
49
+ end
50
+
51
+ describe "missing client_secret" do
52
+ before { params.delete('client_secret') }
53
+
54
+ it "is invalid" do
55
+ exchange.error.should == "invalid_request"
56
+ exchange.error_description.should == "Missing required parameter client_secret"
57
+ end
58
+ end
59
+
60
+ describe "with a mismatched client_secret" do
61
+ before { params['client_secret'] = "nosoupforyou" }
62
+
63
+ it "is invalid" do
64
+ exchange.error.should == "invalid_client"
65
+ exchange.error_description.should == "Parameter client_secret does not match"
66
+ end
67
+ end
68
+
69
+ describe "with lesser scope than the authorization code represents" do
70
+ before { params['scope'] = 'bar' }
71
+
72
+ it "is valid" do
73
+ exchange.error.should be_nil
74
+ end
75
+ end
76
+
77
+ describe "with scopes not covered by the authorization code" do
78
+ before { params['scope'] = 'qux' }
79
+
80
+ it "is invalid" do
81
+ exchange.error.should == 'invalid_scope'
82
+ exchange.error_description.should == 'The request scope was never granted by the user'
83
+ end
84
+ end
85
+ end
86
+
87
+ shared_examples_for "valid token request" do
88
+ before do
89
+ Songkick::OAuth2.stub(:random_string).and_return('random_access_token')
90
+ end
91
+
92
+ it "is valid" do
93
+ exchange.error.should be_nil
94
+ end
95
+
96
+ it "updates the Authorization with tokens" do
97
+ exchange.update_authorization
98
+ authorization.reload
99
+ authorization.code.should be_nil
100
+ authorization.access_token_hash.should == Songkick::OAuth2.hashify('random_access_token')
101
+ authorization.refresh_token.should be_nil
102
+ end
103
+ end
104
+
105
+ describe "using authorization_code grant type" do
106
+ let(:params) { { 'client_id' => @client.client_id,
107
+ 'client_secret' => @client.client_secret,
108
+ 'grant_type' => 'authorization_code',
109
+ 'code' => @authorization.code,
110
+ 'redirect_uri' => @client.redirect_uri }
111
+ }
112
+
113
+ let(:authorization) { @authorization }
114
+
115
+ it_should_behave_like "validates required parameters"
116
+ it_should_behave_like "valid token request"
117
+
118
+ describe "missing redirect_uri" do
119
+ before { params.delete('redirect_uri') }
120
+
121
+ it "is invalid" do
122
+ exchange.error.should == "invalid_request"
123
+ exchange.error_description.should == "Missing required parameter redirect_uri"
124
+ end
125
+ end
126
+
127
+ describe "with a mismatched redirect_uri" do
128
+ before { params['redirect_uri'] = "http://songkick.com" }
129
+
130
+ it "is invalid" do
131
+ exchange.error.should == "redirect_uri_mismatch"
132
+ exchange.error_description.should == "Parameter redirect_uri does not match registered URI"
133
+ end
134
+
135
+ describe "when the client has not registered a redirect_uri" do
136
+ before { @client.update_attribute(:redirect_uri, nil) }
137
+
138
+ it "is valid" do
139
+ exchange.error.should be_nil
140
+ end
141
+ end
142
+ end
143
+
144
+ describe "missing code" do
145
+ before { params.delete('code') }
146
+
147
+ it "is invalid" do
148
+ exchange.error.should == "invalid_request"
149
+ exchange.error_description.should == "Missing required parameter code"
150
+ end
151
+ end
152
+
153
+ describe "with an unknown code" do
154
+ before { params['code'] = "unknown" }
155
+
156
+ it "is invalid" do
157
+ exchange.error.should == "invalid_grant"
158
+ exchange.error_description.should == "The access grant you supplied is invalid"
159
+ end
160
+ end
161
+
162
+ describe "with an expired code" do
163
+ before { @authorization.update_attribute(:expires_at, 1.day.ago) }
164
+
165
+ it "is invalid" do
166
+ exchange.error.should == "invalid_grant"
167
+ exchange.error_description.should == "The access grant you supplied is invalid"
168
+ end
169
+ end
170
+ end
171
+
172
+ describe "using password grant type" do
173
+ let(:params) { { 'client_id' => @client.client_id,
174
+ 'client_secret' => @client.client_secret,
175
+ 'grant_type' => 'password',
176
+ 'password' => 'soldier' }
177
+ }
178
+
179
+ before do
180
+ Songkick::OAuth2::Provider.handle_passwords do |client, username, password, scopes|
181
+ user = TestApp::User[username]
182
+ if password == 'soldier'
183
+ user.grant_access!(client, :scopes => scopes.reject { |s| s == 'qux' })
184
+ else
185
+ nil
186
+ end
187
+ end
188
+ end
189
+
190
+ describe "for a user with existing authorization" do
191
+ let(:authorization) { @authorization }
192
+ before { params['username'] = 'Bob' }
193
+
194
+ it_should_behave_like "validates required parameters"
195
+ it_should_behave_like "valid token request"
196
+
197
+ describe "missing username" do
198
+ before { params.delete('username') }
199
+
200
+ it "is invalid" do
201
+ exchange.error.should == 'invalid_request'
202
+ exchange.error_description.should == 'Missing required parameter username'
203
+ end
204
+ end
205
+
206
+ describe "missing password" do
207
+ before { params.delete('password') }
208
+
209
+ it "is invalid" do
210
+ exchange.error.should == 'invalid_request'
211
+ exchange.error_description.should == 'Missing required parameter password'
212
+ end
213
+ end
214
+
215
+ describe "with a bad password" do
216
+ before { params['password'] = 'bad' }
217
+
218
+ it "is invalid" do
219
+ exchange.error.should == 'invalid_grant'
220
+ exchange.error_description.should == 'The access grant you supplied is invalid'
221
+ end
222
+ end
223
+ end
224
+
225
+ describe "for a user with no existing authorization" do
226
+ let(:authorization) { Songkick::OAuth2::Model::Authorization.find_by_oauth2_resource_owner_id(@alice.id) }
227
+ before { params['username'] = 'Alice' }
228
+
229
+ it_should_behave_like "validates required parameters"
230
+ it_should_behave_like "valid token request"
231
+
232
+ describe "with ungranted but permissible scopes" do
233
+ before { params['scope'] = 'lol' }
234
+ it_should_behave_like "validates required parameters"
235
+ it_should_behave_like "valid token request"
236
+
237
+ it "sets the scope from the request" do
238
+ exchange.update_authorization
239
+ authorization.reload
240
+ authorization.scopes.should == Set.new(['lol'])
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ describe "using assertion grant type" do
247
+ let(:params) { { 'client_id' => @client.client_id,
248
+ 'client_secret' => @client.client_secret,
249
+ 'grant_type' => 'assertion',
250
+ 'assertion_type' => 'https://graph.facebook.com/me',
251
+ 'assertion' => 'Bob' }
252
+ }
253
+
254
+ let(:authorization) { @authorization }
255
+
256
+ before do
257
+ Songkick::OAuth2::Provider.filter_assertions { |client| @client == client }
258
+
259
+ Songkick::OAuth2::Provider.handle_assertions('https://graph.facebook.com/me') do |client, assertion|
260
+ user = TestApp::User[assertion]
261
+ user.grant_access!(client, :scopes => ['foo', 'bar'])
262
+ end
263
+ end
264
+
265
+ after do
266
+ Songkick::OAuth2::Provider.clear_assertion_handlers!
267
+ end
268
+
269
+ it_should_behave_like "validates required parameters"
270
+ it_should_behave_like "valid token request"
271
+
272
+ describe "missing assertion_type" do
273
+ before { params.delete('assertion_type') }
274
+
275
+ it "is invalid" do
276
+ exchange.error.should == 'invalid_request'
277
+ exchange.error_description.should == 'Missing required parameter assertion_type'
278
+ end
279
+ end
280
+
281
+ describe "with a non-URI assertion_type" do
282
+ before { params['assertion_type'] = 'invalid' }
283
+
284
+ it "is invalid" do
285
+ exchange.error.should == 'invalid_request'
286
+ exchange.error_description.should == 'Parameter assertion_type must be an absolute URI'
287
+ end
288
+ end
289
+
290
+ describe "missing assertion" do
291
+ before { params.delete('assertion') }
292
+
293
+ it "is invalid" do
294
+ exchange.error.should == 'invalid_request'
295
+ exchange.error_description.should == 'Missing required parameter assertion'
296
+ end
297
+ end
298
+
299
+ describe "with an unrecognized assertion_type" do
300
+ before { params['assertion_type'] = 'https://oauth.what.com/ohai' }
301
+
302
+ it "is invalid" do
303
+ exchange.error.should == 'unauthorized_client'
304
+ exchange.error_description.should == 'Client cannot use the given assertion type'
305
+ end
306
+ end
307
+
308
+ describe "with a client unauthorized to use the assertion scheme" do
309
+ before do
310
+ client = Factory(:client)
311
+ params['client_id'] = client.client_id
312
+ params['client_secret'] = client.client_secret
313
+ end
314
+
315
+ it "is invalid" do
316
+ exchange.error.should == 'unauthorized_client'
317
+ exchange.error_description.should == 'Client cannot use the given assertion type'
318
+ end
319
+ end
320
+ end
321
+
322
+ describe "using refresh_token grant type" do
323
+ before do
324
+ @refresher = Factory(:authorization, :client => @client,
325
+ :owner => @bob,
326
+ :scope => 'foo bar',
327
+ :code => nil,
328
+ :refresh_token => 'roflscale')
329
+ end
330
+
331
+ let(:params) { { 'client_id' => @client.client_id,
332
+ 'client_secret' => @client.client_secret,
333
+ 'grant_type' => 'refresh_token',
334
+ 'refresh_token' => 'roflscale' }
335
+ }
336
+
337
+ let(:authorization) { @refresher }
338
+
339
+ it_should_behave_like "validates required parameters"
340
+ it_should_behave_like "valid token request"
341
+
342
+ describe "with unknown refresh_token" do
343
+ before { params['refresh_token'] = 'woops' }
344
+
345
+ it "is invalid" do
346
+ exchange.error.should == "invalid_grant"
347
+ exchange.error_description.should == "The access grant you supplied is invalid"
348
+ end
349
+ end
350
+
351
+ end
352
+ end
353
+