rockoauth 0.1.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.
- checksums.yaml +7 -0
- data/History.txt +5 -0
- data/README.rdoc +422 -0
- data/example/README.rdoc +11 -0
- data/example/application.rb +158 -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 +5 -0
- data/example/public/style.css +78 -0
- data/example/schema.rb +22 -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 +24 -0
- data/example/views/layout.erb +24 -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/rockoauth/model/authorization.rb +132 -0
- data/lib/rockoauth/model/client.rb +54 -0
- data/lib/rockoauth/model/client_owner.rb +13 -0
- data/lib/rockoauth/model/hashing.rb +26 -0
- data/lib/rockoauth/model/helpers.rb +14 -0
- data/lib/rockoauth/model/resource_owner.rb +22 -0
- data/lib/rockoauth/model.rb +38 -0
- data/lib/rockoauth/provider/access_token.rb +70 -0
- data/lib/rockoauth/provider/authorization.rb +185 -0
- data/lib/rockoauth/provider/error.rb +19 -0
- data/lib/rockoauth/provider/exchange.rb +225 -0
- data/lib/rockoauth/provider.rb +133 -0
- data/lib/rockoauth/router.rb +75 -0
- data/lib/rockoauth/schema/20120828112156_rockoauth_schema_original_schema.rb +35 -0
- data/lib/rockoauth/schema/20121024180930_rockoauth_schema_add_authorization_index.rb +13 -0
- data/lib/rockoauth/schema/20121025180447_rockoauth_schema_add_unique_indexes.rb +31 -0
- data/lib/rockoauth/schema.rb +25 -0
- data/lib/rockoauth.rb +1 -0
- data/spec/factories.rb +20 -0
- data/spec/request_helpers.rb +62 -0
- data/spec/rockoauth/model/authorization_spec.rb +237 -0
- data/spec/rockoauth/model/client_spec.rb +44 -0
- data/spec/rockoauth/model/helpers_spec.rb +25 -0
- data/spec/rockoauth/model/resource_owner_spec.rb +87 -0
- data/spec/rockoauth/provider/access_token_spec.rb +138 -0
- data/spec/rockoauth/provider/authorization_spec.rb +356 -0
- data/spec/rockoauth/provider/exchange_spec.rb +361 -0
- data/spec/rockoauth/provider_spec.rb +560 -0
- data/spec/spec_helper.rb +80 -0
- data/spec/test_app/helper.rb +36 -0
- data/spec/test_app/provider/application.rb +67 -0
- data/spec/test_app/provider/views/authorize.erb +19 -0
- metadata +238 -0
@@ -0,0 +1,356 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe RockOAuth::Provider::Authorization do
|
4
|
+
let(:resource_owner) { TestApp::User['Bob'] }
|
5
|
+
|
6
|
+
let(:authorization) { RockOAuth::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
|
+
allow(RockOAuth).to receive(:random_string).and_return('s1', 's2', 's3')
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "with valid parameters" do
|
19
|
+
it "is valid" do
|
20
|
+
expect(authorization.error).to 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
|
+
expect(authorization.scopes).to eq(Set.new(%w[foo bar qux]))
|
29
|
+
end
|
30
|
+
|
31
|
+
it "exposes the scopes the client has not yet granted" do
|
32
|
+
expect(authorization.unauthorized_scopes).to eq(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
|
+
expect(authorization.scopes).to eq(Set.new(%w[foo bar qux]))
|
42
|
+
end
|
43
|
+
|
44
|
+
it "exposes the scopes the client has not yet granted" do
|
45
|
+
expect(authorization.unauthorized_scopes).to eq(%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
|
+
expect(authorization.error).to eq("invalid_request")
|
55
|
+
expect(authorization.error_description).to eq("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
|
+
expect(authorization.error).to eq("unsupported_response_type")
|
64
|
+
expect(authorization.error_description).to eq("Response type no_such_type is not supported")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "causes a redirect" do
|
68
|
+
expect(authorization).to be_redirect
|
69
|
+
expect(authorization.redirect_uri).to eq("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
|
+
expect(authorization.error).to eq("invalid_request")
|
78
|
+
expect(authorization.error_description).to eq("Missing required parameter client_id")
|
79
|
+
end
|
80
|
+
|
81
|
+
it "does not cause a redirect" do
|
82
|
+
expect(authorization).not_to 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
|
+
expect(authorization.error).to eq("invalid_client")
|
91
|
+
expect(authorization.error_description).to eq("Unknown client ID unknown")
|
92
|
+
end
|
93
|
+
|
94
|
+
it "does not cause a redirect" do
|
95
|
+
expect(authorization).not_to 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
|
+
expect(authorization.error).to eq("invalid_request")
|
104
|
+
expect(authorization.error_description).to eq("Missing required parameter redirect_uri")
|
105
|
+
end
|
106
|
+
|
107
|
+
it "causes a redirect to the client's registered redirect_uri" do
|
108
|
+
expect(authorization).to be_redirect
|
109
|
+
expect(authorization.redirect_uri).to eq("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
|
+
expect(authorization.error).to eq("redirect_uri_mismatch")
|
118
|
+
expect(authorization.error_description).to eq("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
|
+
expect(authorization).to be_redirect
|
123
|
+
expect(authorization.redirect_uri).to eq("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
|
+
expect(authorization.error).to be_nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "with a redirect_uri with parameters" do
|
136
|
+
before do
|
137
|
+
authorization.client.redirect_uri = "http://songkick.com?some_parameter"
|
138
|
+
params['redirect_uri'] = "http://songkick.com?some_parameter"
|
139
|
+
end
|
140
|
+
|
141
|
+
it "adds the extra parameters with & instead of ?" do
|
142
|
+
expect(authorization.redirect_uri).to eq("http://songkick.com?some_parameter&")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# http://en.wikipedia.org/wiki/HTTP_response_splitting
|
147
|
+
# scope and state values are passed back in the redirect
|
148
|
+
|
149
|
+
describe "with an illegal scope" do
|
150
|
+
before { params['scope'] = "http\r\nsplitter" }
|
151
|
+
|
152
|
+
it "is invalid" do
|
153
|
+
expect(authorization.error).to eq("invalid_request")
|
154
|
+
expect(authorization.error_description).to eq("Illegal value for scope parameter")
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "with an illegal state" do
|
159
|
+
before { params['state'] = "http\r\nsplitter" }
|
160
|
+
|
161
|
+
it "is invalid" do
|
162
|
+
expect(authorization.error).to eq("invalid_request")
|
163
|
+
expect(authorization.error_description).to eq("Illegal value for state parameter")
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe "#grant_access!" do
|
168
|
+
describe "when there is an existing authorization with no code" do
|
169
|
+
before do
|
170
|
+
@model = create_authorization(
|
171
|
+
:owner => resource_owner,
|
172
|
+
:client => @client,
|
173
|
+
:code => nil)
|
174
|
+
end
|
175
|
+
|
176
|
+
it "generates and returns a code to the client" do
|
177
|
+
authorization.grant_access!
|
178
|
+
@model.reload
|
179
|
+
expect(@model.code).to eq("s1")
|
180
|
+
expect(authorization.code).to eq("s1")
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "when there is an existing authorization with scopes" do
|
185
|
+
before do
|
186
|
+
@model = create_authorization(
|
187
|
+
:owner => resource_owner,
|
188
|
+
:client => @client,
|
189
|
+
:code => nil,
|
190
|
+
:scope => 'foo bar')
|
191
|
+
|
192
|
+
params['scope'] = 'qux'
|
193
|
+
end
|
194
|
+
|
195
|
+
it "merges the new scopes with the existing ones" do
|
196
|
+
authorization.grant_access!
|
197
|
+
@model.reload
|
198
|
+
expect(@model.scopes).to eq(Set.new(%w[foo bar qux]))
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "when there is an existing expired authorization" do
|
203
|
+
before do
|
204
|
+
@model = create_authorization(
|
205
|
+
:owner => resource_owner,
|
206
|
+
:client => @client,
|
207
|
+
:expires_at => 2.months.ago,
|
208
|
+
:code => 'existing_code',
|
209
|
+
:scope => 'foo bar')
|
210
|
+
end
|
211
|
+
|
212
|
+
it "renews the authorization" do
|
213
|
+
authorization.grant_access!
|
214
|
+
@model.reload
|
215
|
+
expect(@model.expires_at).to be_nil
|
216
|
+
end
|
217
|
+
|
218
|
+
it "returns a code to the client" do
|
219
|
+
authorization.grant_access!
|
220
|
+
expect(authorization.code).to eq("existing_code")
|
221
|
+
expect(authorization.access_token).to be_nil
|
222
|
+
end
|
223
|
+
|
224
|
+
it "sets the expiry time if a duration is given" do
|
225
|
+
authorization.grant_access!(:duration => 1.hour)
|
226
|
+
@model.reload
|
227
|
+
expect(@model.expires_in).to eq(3600)
|
228
|
+
expect(authorization.expires_in).to eq(3600)
|
229
|
+
end
|
230
|
+
|
231
|
+
it "augments the scope" do
|
232
|
+
params['scope'] = 'qux'
|
233
|
+
authorization.grant_access!
|
234
|
+
@model.reload
|
235
|
+
expect(@model.scopes).to eq(Set.new(%w[foo bar qux]))
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
describe "for code requests" do
|
240
|
+
before do
|
241
|
+
params['response_type'] = 'code'
|
242
|
+
params['scope'] = 'foo bar'
|
243
|
+
end
|
244
|
+
|
245
|
+
it "makes the authorization redirect" do
|
246
|
+
authorization.grant_access!
|
247
|
+
expect(authorization.client).not_to be_nil
|
248
|
+
expect(authorization).to be_redirect
|
249
|
+
end
|
250
|
+
|
251
|
+
it "creates a code for the authorization" do
|
252
|
+
authorization.grant_access!
|
253
|
+
expect(authorization.code).to eq("s1")
|
254
|
+
expect(authorization.access_token).to be_nil
|
255
|
+
expect(authorization.expires_in).to be_nil
|
256
|
+
end
|
257
|
+
|
258
|
+
it "creates an Authorization in the database" do
|
259
|
+
authorization.grant_access!
|
260
|
+
|
261
|
+
authorization = RockOAuth::Model::Authorization.first
|
262
|
+
expect(authorization.owner).to eq(resource_owner)
|
263
|
+
expect(authorization.client).to eq(@client)
|
264
|
+
expect(authorization.code).to eq("s1")
|
265
|
+
expect(authorization.scopes).to eq(Set.new(%w[foo bar]))
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
describe "for token requests" do
|
270
|
+
before { params['response_type'] = 'token' }
|
271
|
+
|
272
|
+
it "creates a token for the authorization" do
|
273
|
+
authorization.grant_access!
|
274
|
+
expect(authorization.code).to be_nil
|
275
|
+
expect(authorization.access_token).to eq("s1")
|
276
|
+
expect(authorization.refresh_token).to eq("s2")
|
277
|
+
expect(authorization.expires_in).to be_nil
|
278
|
+
end
|
279
|
+
|
280
|
+
it "creates an Authorization in the database" do
|
281
|
+
authorization.grant_access!
|
282
|
+
|
283
|
+
authorization = RockOAuth::Model::Authorization.first
|
284
|
+
expect(authorization.owner).to eq(resource_owner)
|
285
|
+
expect(authorization.client).to eq(@client)
|
286
|
+
expect(authorization.code).to be_nil
|
287
|
+
expect(authorization.access_token_hash).to eq(RockOAuth.hashify("s1"))
|
288
|
+
expect(authorization.refresh_token_hash).to eq(RockOAuth.hashify("s2"))
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
describe "for code_and_token requests" do
|
293
|
+
before { params['response_type'] = 'code_and_token' }
|
294
|
+
|
295
|
+
it "creates a code and token for the authorization" do
|
296
|
+
authorization.grant_access!
|
297
|
+
expect(authorization.code).to eq("s1")
|
298
|
+
expect(authorization.access_token).to eq("s2")
|
299
|
+
expect(authorization.refresh_token).to eq("s3")
|
300
|
+
expect(authorization.expires_in).to be_nil
|
301
|
+
end
|
302
|
+
|
303
|
+
it "creates an Authorization in the database" do
|
304
|
+
authorization.grant_access!
|
305
|
+
|
306
|
+
authorization = RockOAuth::Model::Authorization.first
|
307
|
+
expect(authorization.owner).to eq(resource_owner)
|
308
|
+
expect(authorization.client).to eq(@client)
|
309
|
+
expect(authorization.code).to eq("s1")
|
310
|
+
expect(authorization.access_token_hash).to eq(RockOAuth.hashify("s2"))
|
311
|
+
expect(authorization.refresh_token_hash).to eq(RockOAuth.hashify("s3"))
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
describe "#deny_access!" do
|
317
|
+
it "puts the authorization in an error state" do
|
318
|
+
authorization.deny_access!
|
319
|
+
expect(authorization.error).to eq("access_denied")
|
320
|
+
expect(authorization.error_description).to eq("The user denied you access")
|
321
|
+
end
|
322
|
+
|
323
|
+
it "does not create an Authorization" do
|
324
|
+
expect(RockOAuth::Model::Authorization).not_to receive(:create)
|
325
|
+
expect(RockOAuth::Model::Authorization).not_to receive(:new)
|
326
|
+
authorization.deny_access!
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
describe "#params" do
|
331
|
+
before do
|
332
|
+
params['scope'] = params['state'] = 'valid'
|
333
|
+
params['controller'] = 'invalid'
|
334
|
+
end
|
335
|
+
|
336
|
+
it "only exposes OAuth-related parameters" do
|
337
|
+
expect(authorization.params).to eq({
|
338
|
+
'response_type' => 'code',
|
339
|
+
'client_id' => @client.client_id,
|
340
|
+
'redirect_uri' => @client.redirect_uri,
|
341
|
+
'state' => 'valid',
|
342
|
+
'scope' => 'valid'
|
343
|
+
})
|
344
|
+
end
|
345
|
+
|
346
|
+
it "does not expose parameters with no value" do
|
347
|
+
params.delete('scope')
|
348
|
+
expect(authorization.params).to eq({
|
349
|
+
'response_type' => 'code',
|
350
|
+
'client_id' => @client.client_id,
|
351
|
+
'redirect_uri' => @client.redirect_uri,
|
352
|
+
'state' => 'valid'
|
353
|
+
})
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|