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.
- data/README.rdoc +394 -0
- data/example/README.rdoc +11 -0
- data/example/application.rb +159 -0
- data/example/config.ru +3 -0
- data/example/environment.rb +11 -0
- data/example/models/connection.rb +9 -0
- data/example/models/note.rb +4 -0
- data/example/models/user.rb +6 -0
- data/example/public/style.css +78 -0
- data/example/schema.rb +27 -0
- data/example/views/authorize.erb +28 -0
- data/example/views/create_user.erb +3 -0
- data/example/views/error.erb +6 -0
- data/example/views/home.erb +25 -0
- data/example/views/layout.erb +25 -0
- data/example/views/login.erb +20 -0
- data/example/views/new_client.erb +25 -0
- data/example/views/new_user.erb +22 -0
- data/example/views/show_client.erb +15 -0
- data/lib/songkick/oauth2/model.rb +20 -0
- data/lib/songkick/oauth2/model/authorization.rb +126 -0
- data/lib/songkick/oauth2/model/client.rb +61 -0
- data/lib/songkick/oauth2/model/client_owner.rb +15 -0
- data/lib/songkick/oauth2/model/hashing.rb +29 -0
- data/lib/songkick/oauth2/model/resource_owner.rb +54 -0
- data/lib/songkick/oauth2/provider.rb +122 -0
- data/lib/songkick/oauth2/provider/access_token.rb +68 -0
- data/lib/songkick/oauth2/provider/authorization.rb +190 -0
- data/lib/songkick/oauth2/provider/error.rb +22 -0
- data/lib/songkick/oauth2/provider/exchange.rb +227 -0
- data/lib/songkick/oauth2/router.rb +79 -0
- data/lib/songkick/oauth2/schema.rb +17 -0
- data/lib/songkick/oauth2/schema/20120828112156_songkick_oauth2_schema_original_schema.rb +36 -0
- data/spec/factories.rb +27 -0
- data/spec/request_helpers.rb +52 -0
- data/spec/songkick/oauth2/model/authorization_spec.rb +216 -0
- data/spec/songkick/oauth2/model/client_spec.rb +55 -0
- data/spec/songkick/oauth2/model/resource_owner_spec.rb +88 -0
- data/spec/songkick/oauth2/provider/access_token_spec.rb +125 -0
- data/spec/songkick/oauth2/provider/authorization_spec.rb +346 -0
- data/spec/songkick/oauth2/provider/exchange_spec.rb +353 -0
- data/spec/songkick/oauth2/provider_spec.rb +545 -0
- data/spec/spec_helper.rb +62 -0
- data/spec/test_app/helper.rb +33 -0
- data/spec/test_app/provider/application.rb +68 -0
- data/spec/test_app/provider/views/authorize.erb +19 -0
- 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
|
+
|