doorkeeper 5.1.2 → 5.2.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of doorkeeper might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Appraisals +1 -1
- data/CHANGELOG.md +854 -0
- data/CONTRIBUTING.md +11 -9
- data/Dangerfile +2 -2
- data/Dockerfile +29 -0
- data/Gemfile +3 -2
- data/NEWS.md +1 -819
- data/README.md +11 -3
- data/RELEASING.md +6 -5
- data/app/controllers/doorkeeper/application_controller.rb +1 -1
- data/app/controllers/doorkeeper/application_metal_controller.rb +2 -1
- data/app/controllers/doorkeeper/applications_controller.rb +5 -3
- data/app/controllers/doorkeeper/authorizations_controller.rb +14 -7
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
- data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
- data/app/views/doorkeeper/applications/_form.html.erb +0 -6
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/config/locales/en.yml +8 -2
- data/doorkeeper.gemspec +9 -1
- data/gemfiles/rails_5_0.gemfile +1 -0
- data/gemfiles/rails_5_1.gemfile +1 -0
- data/gemfiles/rails_5_2.gemfile +1 -0
- data/gemfiles/rails_6_0.gemfile +2 -1
- data/gemfiles/rails_master.gemfile +1 -0
- data/lib/doorkeeper/config/option.rb +13 -7
- data/lib/doorkeeper/config.rb +88 -6
- data/lib/doorkeeper/errors.rb +13 -18
- data/lib/doorkeeper/grape/helpers.rb +5 -1
- data/lib/doorkeeper/helpers/controller.rb +23 -4
- data/lib/doorkeeper/models/access_token_mixin.rb +43 -2
- data/lib/doorkeeper/oauth/authorization/code.rb +11 -13
- data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -9
- data/lib/doorkeeper/oauth/base_request.rb +2 -0
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +14 -0
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +8 -0
- data/lib/doorkeeper/oauth/code_request.rb +5 -11
- data/lib/doorkeeper/oauth/code_response.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +18 -4
- data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
- data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
- data/lib/doorkeeper/oauth/pre_authorization.rb +70 -37
- data/lib/doorkeeper/oauth/refresh_token_request.rb +13 -10
- data/lib/doorkeeper/oauth/token_introspection.rb +23 -13
- data/lib/doorkeeper/oauth/token_request.rb +4 -18
- data/lib/doorkeeper/orm/active_record/access_grant.rb +1 -1
- data/lib/doorkeeper/orm/active_record/access_token.rb +2 -2
- data/lib/doorkeeper/orm/active_record/application.rb +15 -69
- data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
- data/lib/doorkeeper/orm/active_record.rb +19 -3
- data/lib/doorkeeper/request/authorization_code.rb +2 -0
- data/lib/doorkeeper/request.rb +6 -11
- data/lib/doorkeeper/server.rb +2 -6
- data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/doorkeeper.rb +4 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +6 -6
- data/lib/generators/doorkeeper/templates/initializer.rb +110 -33
- data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
- data/spec/controllers/applications_controller_spec.rb +93 -0
- data/spec/controllers/authorizations_controller_spec.rb +143 -62
- data/spec/controllers/protected_resources_controller_spec.rb +3 -3
- data/spec/controllers/tokens_controller_spec.rb +205 -37
- data/spec/dummy/config/application.rb +3 -1
- data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +1 -1
- data/spec/lib/config_spec.rb +43 -1
- data/spec/lib/oauth/authorization_code_request_spec.rb +13 -1
- data/spec/lib/oauth/base_request_spec.rb +33 -16
- data/spec/lib/oauth/client_credentials/creator_spec.rb +3 -0
- data/spec/lib/oauth/code_request_spec.rb +27 -28
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
- data/spec/lib/oauth/invalid_request_response_spec.rb +75 -0
- data/spec/lib/oauth/pre_authorization_spec.rb +76 -66
- data/spec/lib/oauth/refresh_token_request_spec.rb +1 -0
- data/spec/lib/oauth/token_request_spec.rb +20 -17
- data/spec/lib/server_spec.rb +0 -12
- data/spec/models/doorkeeper/access_grant_spec.rb +21 -2
- data/spec/models/doorkeeper/access_token_spec.rb +35 -4
- data/spec/models/doorkeeper/application_spec.rb +275 -370
- data/spec/requests/endpoints/authorization_spec.rb +21 -5
- data/spec/requests/endpoints/token_spec.rb +1 -1
- data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
- data/spec/requests/flows/authorization_code_spec.rb +93 -27
- data/spec/requests/flows/client_credentials_spec.rb +38 -0
- data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
- data/spec/requests/flows/implicit_grant_spec.rb +9 -8
- data/spec/requests/flows/password_spec.rb +37 -0
- data/spec/requests/flows/refresh_token_spec.rb +1 -1
- data/spec/requests/flows/revoke_token_spec.rb +19 -11
- data/spec/support/doorkeeper_rspec.rb +1 -1
- data/spec/support/helpers/request_spec_helper.rb +14 -2
- data/spec/validators/redirect_uri_validator_spec.rb +40 -15
- metadata +16 -15
- data/.coveralls.yml +0 -1
- data/.github/ISSUE_TEMPLATE.md +0 -25
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
- data/.gitignore +0 -20
- data/.gitlab-ci.yml +0 -16
- data/.hound.yml +0 -3
- data/.rspec +0 -1
- data/.rubocop.yml +0 -50
- data/.travis.yml +0 -35
- data/app/validators/redirect_uri_validator.rb +0 -50
@@ -4,6 +4,7 @@ require "spec_helper"
|
|
4
4
|
|
5
5
|
feature "Authorization endpoint" do
|
6
6
|
background do
|
7
|
+
default_scopes_exist :default
|
7
8
|
config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
|
8
9
|
client_exists(name: "MyApp")
|
9
10
|
end
|
@@ -34,16 +35,31 @@ feature "Authorization endpoint" do
|
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
37
|
-
context "with a invalid request" do
|
38
|
+
context "with a invalid request's param" do
|
38
39
|
background do
|
39
40
|
create_resource_owner
|
40
41
|
sign_in
|
41
42
|
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
context "when missing required param" do
|
45
|
+
scenario "displays invalid_request error when missing client" do
|
46
|
+
visit authorization_endpoint_url(client: nil, response_type: "code")
|
47
|
+
i_should_not_see "Authorize"
|
48
|
+
i_should_see_translated_invalid_request_error_message :missing_param, :client_id
|
49
|
+
end
|
50
|
+
|
51
|
+
scenario "displays invalid_request error when missing response_type param" do
|
52
|
+
visit authorization_endpoint_url(client: @client, response_type: "")
|
53
|
+
i_should_not_see "Authorize"
|
54
|
+
i_should_see_translated_invalid_request_error_message :missing_param, :response_type
|
55
|
+
end
|
56
|
+
|
57
|
+
scenario "displays invalid_request error when missing scope param and authorization server has no default scopes" do
|
58
|
+
config_is_set(:default_scopes, [])
|
59
|
+
visit authorization_endpoint_url(client: @client, response_type: "code", scope: "")
|
60
|
+
i_should_not_see "Authorize"
|
61
|
+
i_should_see_translated_invalid_request_error_message :missing_param, :scope
|
62
|
+
end
|
47
63
|
end
|
48
64
|
|
49
65
|
scenario "displays unsupported_response_type error when using a disabled response type" do
|
@@ -70,6 +70,6 @@ describe "Token endpoint" do
|
|
70
70
|
|
71
71
|
should_not_have_json "access_token"
|
72
72
|
should_have_json "error", "invalid_request"
|
73
|
-
should_have_json "error_description",
|
73
|
+
should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :grant_type)
|
74
74
|
end
|
75
75
|
end
|
@@ -5,6 +5,7 @@ require "spec_helper"
|
|
5
5
|
feature "Authorization Code Flow Errors" do
|
6
6
|
let(:client_params) { {} }
|
7
7
|
background do
|
8
|
+
default_scopes_exist :default
|
8
9
|
config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
|
9
10
|
client_exists client_params
|
10
11
|
create_resource_owner
|
@@ -4,6 +4,7 @@ require "spec_helper"
|
|
4
4
|
|
5
5
|
feature "Authorization Code Flow" do
|
6
6
|
background do
|
7
|
+
default_scopes_exist :default
|
7
8
|
config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
|
8
9
|
client_exists
|
9
10
|
create_resource_owner
|
@@ -23,13 +24,46 @@ feature "Authorization Code Flow" do
|
|
23
24
|
url_should_not_have_param("error")
|
24
25
|
end
|
25
26
|
|
27
|
+
context "when configured to check application supported grant flow" do
|
28
|
+
before do
|
29
|
+
config_is_set(:allow_grant_flow_for_client, ->(_grant_flow, client) { client.name == "admin" })
|
30
|
+
end
|
31
|
+
|
32
|
+
scenario "forbids the request when doesn't satisfy condition" do
|
33
|
+
@client.update(name: "sample app")
|
34
|
+
|
35
|
+
visit authorization_endpoint_url(client: @client)
|
36
|
+
|
37
|
+
i_should_see_translated_error_message("unauthorized_client")
|
38
|
+
end
|
39
|
+
|
40
|
+
scenario "allows the request when satisfies condition" do
|
41
|
+
@client.update(name: "admin")
|
42
|
+
|
43
|
+
visit authorization_endpoint_url(client: @client)
|
44
|
+
i_should_not_see_translated_error_message("unauthorized_client")
|
45
|
+
click_on "Authorize"
|
46
|
+
|
47
|
+
authorization_code = Doorkeeper::AccessGrant.first.token
|
48
|
+
create_access_token authorization_code, @client
|
49
|
+
|
50
|
+
access_token_should_exist_for(@client, @resource_owner)
|
51
|
+
|
52
|
+
should_not_have_json "error"
|
53
|
+
|
54
|
+
should_have_json "access_token", Doorkeeper::AccessToken.first.token
|
55
|
+
should_have_json "token_type", "Bearer"
|
56
|
+
should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
26
60
|
context "with grant hashing enabled" do
|
27
61
|
background do
|
28
62
|
config_is_set(:token_secret_strategy, ::Doorkeeper::SecretStoring::Sha256Hash)
|
29
63
|
end
|
30
64
|
|
31
|
-
|
32
|
-
@client.redirect_uri =
|
65
|
+
def authorize(redirect_url)
|
66
|
+
@client.redirect_uri = redirect_url
|
33
67
|
@client.save!
|
34
68
|
visit authorization_endpoint_url(client: @client)
|
35
69
|
click_on "Authorize"
|
@@ -42,16 +76,28 @@ feature "Authorization Code Flow" do
|
|
42
76
|
hashed_code = Doorkeeper::AccessGrant.secret_strategy.transform_secret code
|
43
77
|
expect(hashed_code).to eq Doorkeeper::AccessGrant.first.token
|
44
78
|
|
79
|
+
[code, hashed_code]
|
80
|
+
end
|
81
|
+
|
82
|
+
scenario "using redirect_url urn:ietf:wg:oauth:2.0:oob" do
|
83
|
+
code, hashed_code = authorize("urn:ietf:wg:oauth:2.0:oob")
|
45
84
|
expect(code).not_to eq(hashed_code)
|
85
|
+
i_should_see "Authorization code:"
|
86
|
+
i_should_see code
|
87
|
+
i_should_not_see hashed_code
|
88
|
+
end
|
46
89
|
|
90
|
+
scenario "using redirect_url urn:ietf:wg:oauth:2.0:oob:auto" do
|
91
|
+
code, hashed_code = authorize("urn:ietf:wg:oauth:2.0:oob:auto")
|
92
|
+
expect(code).not_to eq(hashed_code)
|
47
93
|
i_should_see "Authorization code:"
|
48
94
|
i_should_see code
|
49
95
|
i_should_not_see hashed_code
|
50
96
|
end
|
51
97
|
end
|
52
98
|
|
53
|
-
scenario "resource owner authorizes using
|
54
|
-
@client.redirect_uri =
|
99
|
+
scenario "resource owner authorizes using oob url" do
|
100
|
+
@client.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
|
55
101
|
@client.save!
|
56
102
|
visit authorization_endpoint_url(client: @client)
|
57
103
|
click_on "Authorize"
|
@@ -71,6 +117,17 @@ feature "Authorization Code Flow" do
|
|
71
117
|
url_should_not_have_param("code_challenge_method")
|
72
118
|
end
|
73
119
|
|
120
|
+
scenario "resource owner requests an access token without authorization code" do
|
121
|
+
create_access_token "", @client
|
122
|
+
|
123
|
+
access_token_should_not_exist
|
124
|
+
|
125
|
+
expect(Doorkeeper::AccessToken.count).to be_zero
|
126
|
+
|
127
|
+
should_have_json "error", "invalid_request"
|
128
|
+
should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code)
|
129
|
+
end
|
130
|
+
|
74
131
|
scenario "resource owner requests an access token with authorization code" do
|
75
132
|
visit authorization_endpoint_url(client: @client)
|
76
133
|
click_on "Authorize"
|
@@ -98,6 +155,7 @@ feature "Authorization Code Flow" do
|
|
98
155
|
expect(Doorkeeper::AccessToken.count).to be_zero
|
99
156
|
|
100
157
|
should_have_json "error", "invalid_client"
|
158
|
+
should_have_json "error_description", translated_error_message(:invalid_client)
|
101
159
|
end
|
102
160
|
|
103
161
|
scenario "resource owner requests an access token with authorization code but without client id" do
|
@@ -111,6 +169,7 @@ feature "Authorization Code Flow" do
|
|
111
169
|
expect(Doorkeeper::AccessToken.count).to be_zero
|
112
170
|
|
113
171
|
should_have_json "error", "invalid_client"
|
172
|
+
should_have_json "error_description", translated_error_message(:invalid_client)
|
114
173
|
end
|
115
174
|
|
116
175
|
scenario "silently authorizes if matching token exists" do
|
@@ -153,6 +212,7 @@ feature "Authorization Code Flow" do
|
|
153
212
|
create_access_token authorization_code, @client, code_verifier
|
154
213
|
|
155
214
|
should_have_json "error", "invalid_grant"
|
215
|
+
should_have_json "error_description", translated_error_message(:invalid_grant)
|
156
216
|
end
|
157
217
|
|
158
218
|
scenario "mobile app requests an access token with authorization code and plain code challenge method" do
|
@@ -175,17 +235,32 @@ feature "Authorization Code Flow" do
|
|
175
235
|
should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
|
176
236
|
end
|
177
237
|
|
178
|
-
scenario "mobile app requests an access token with authorization code
|
238
|
+
scenario "mobile app requests an access token with authorization code but without code_verifier" do
|
179
239
|
visit authorization_endpoint_url(client: @client,
|
180
|
-
code_challenge:
|
240
|
+
code_challenge: code_challenge,
|
181
241
|
code_challenge_method: "plain")
|
182
242
|
click_on "Authorize"
|
183
243
|
|
184
244
|
authorization_code = current_params["code"]
|
185
|
-
create_access_token authorization_code, @client,
|
245
|
+
create_access_token authorization_code, @client, nil
|
246
|
+
|
247
|
+
should_not_have_json "access_token"
|
248
|
+
should_have_json "error", "invalid_request"
|
249
|
+
should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code_verifier)
|
250
|
+
end
|
251
|
+
|
252
|
+
scenario "mobile app requests an access token with authorization code with wrong code_verifier" do
|
253
|
+
visit authorization_endpoint_url(client: @client,
|
254
|
+
code_challenge: code_challenge,
|
255
|
+
code_challenge_method: "plain")
|
256
|
+
click_on "Authorize"
|
257
|
+
|
258
|
+
authorization_code = current_params["code"]
|
259
|
+
create_access_token authorization_code, @client, "wrong_code_verifier"
|
186
260
|
|
187
261
|
should_not_have_json "access_token"
|
188
262
|
should_have_json "error", "invalid_grant"
|
263
|
+
should_have_json "error_description", translated_error_message(:invalid_grant)
|
189
264
|
end
|
190
265
|
end
|
191
266
|
|
@@ -226,19 +301,6 @@ feature "Authorization Code Flow" do
|
|
226
301
|
should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
|
227
302
|
end
|
228
303
|
|
229
|
-
scenario "mobile app requests an access token with authorization code and without code_verifier" do
|
230
|
-
visit authorization_endpoint_url(
|
231
|
-
client: @client,
|
232
|
-
code_challenge: code_challenge,
|
233
|
-
code_challenge_method: "S256"
|
234
|
-
)
|
235
|
-
click_on "Authorize"
|
236
|
-
authorization_code = current_params["code"]
|
237
|
-
create_access_token authorization_code, @client
|
238
|
-
should_have_json "error", "invalid_request"
|
239
|
-
should_not_have_json "access_token"
|
240
|
-
end
|
241
|
-
|
242
304
|
scenario "mobile app requests an access token with authorization code and without secret" do
|
243
305
|
visit authorization_endpoint_url(
|
244
306
|
client: @client,
|
@@ -250,8 +312,9 @@ feature "Authorization Code Flow" do
|
|
250
312
|
authorization_code = current_params["code"]
|
251
313
|
page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
|
252
314
|
redirect_uri: @client.redirect_uri, code_verifier: code_verifier)
|
253
|
-
should_have_json "error", "invalid_client"
|
254
315
|
should_not_have_json "access_token"
|
316
|
+
should_have_json "error", "invalid_client"
|
317
|
+
should_have_json "error_description", translated_error_message(:invalid_client)
|
255
318
|
end
|
256
319
|
|
257
320
|
scenario "mobile app requests an access token with authorization code and without secret but is marked as not confidential" do
|
@@ -286,6 +349,7 @@ feature "Authorization Code Flow" do
|
|
286
349
|
|
287
350
|
should_not_have_json "access_token"
|
288
351
|
should_have_json "error", "invalid_request"
|
352
|
+
should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code_verifier)
|
289
353
|
end
|
290
354
|
|
291
355
|
scenario "mobile app requests an access token with authorization code with wrong verifier" do
|
@@ -301,6 +365,7 @@ feature "Authorization Code Flow" do
|
|
301
365
|
|
302
366
|
should_not_have_json "access_token"
|
303
367
|
should_have_json "error", "invalid_grant"
|
368
|
+
should_have_json "error_description", translated_error_message(:invalid_grant)
|
304
369
|
end
|
305
370
|
|
306
371
|
scenario "code_challenge_mehthod in token request is totally ignored" do
|
@@ -321,6 +386,7 @@ feature "Authorization Code Flow" do
|
|
321
386
|
|
322
387
|
should_not_have_json "access_token"
|
323
388
|
should_have_json "error", "invalid_grant"
|
389
|
+
should_have_json "error_description", translated_error_message(:invalid_grant)
|
324
390
|
end
|
325
391
|
|
326
392
|
scenario "expects to set code_challenge_method explicitely without fallback" do
|
@@ -332,16 +398,15 @@ feature "Authorization Code Flow" do
|
|
332
398
|
|
333
399
|
context "when application scopes are present and no scope is passed" do
|
334
400
|
background do
|
335
|
-
@client.update(scopes: "public write read")
|
401
|
+
@client.update(scopes: "public write read default")
|
336
402
|
end
|
337
403
|
|
338
|
-
scenario "
|
404
|
+
scenario "scope is invalid because default scope is different from application scope" do
|
339
405
|
default_scopes_exist :admin
|
340
406
|
visit authorization_endpoint_url(client: @client)
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
expect(grant.scopes).to be_empty
|
407
|
+
response_status_should_be 200
|
408
|
+
i_should_not_see "Authorize"
|
409
|
+
i_should_see_translated_error_message :invalid_scope
|
345
410
|
end
|
346
411
|
|
347
412
|
scenario "access grant have scopes which are common in application scopees and default scopes" do
|
@@ -442,6 +507,7 @@ describe "Authorization Code Flow" do
|
|
442
507
|
|
443
508
|
should_not_have_json "access_token"
|
444
509
|
should_have_json "error", "invalid_grant"
|
510
|
+
should_have_json "error_description", translated_error_message(:invalid_grant)
|
445
511
|
end
|
446
512
|
end
|
447
513
|
end
|
@@ -66,6 +66,44 @@ describe "Client Credentials Request" do
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
+
context "when configured to check application supported grant flow" do
|
70
|
+
before do
|
71
|
+
Doorkeeper.configuration.instance_variable_set(
|
72
|
+
:@allow_grant_flow_for_client,
|
73
|
+
->(_grant_flow, client) { client.name == "admin" }
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
scenario "forbids the request when doesn't satisfy condition" do
|
78
|
+
client.update(name: "sample app")
|
79
|
+
|
80
|
+
headers = authorization client.uid, client.secret
|
81
|
+
params = { grant_type: "client_credentials" }
|
82
|
+
|
83
|
+
post "/oauth/token", params: params, headers: headers
|
84
|
+
|
85
|
+
should_have_json "error", "unauthorized_client"
|
86
|
+
should_have_json "error_description", translated_error_message(:unauthorized_client)
|
87
|
+
end
|
88
|
+
|
89
|
+
scenario "allows the request when satisfies condition" do
|
90
|
+
client.update(name: "admin")
|
91
|
+
|
92
|
+
headers = authorization client.uid, client.secret
|
93
|
+
params = { grant_type: "client_credentials" }
|
94
|
+
|
95
|
+
post "/oauth/token", params: params, headers: headers
|
96
|
+
|
97
|
+
should_have_json "access_token", Doorkeeper::AccessToken.first.token
|
98
|
+
should_have_json_within "expires_in", Doorkeeper.configuration.access_token_expires_in, 1
|
99
|
+
should_not_have_json "scope"
|
100
|
+
should_not_have_json "refresh_token"
|
101
|
+
|
102
|
+
should_not_have_json "error"
|
103
|
+
should_not_have_json "error_description"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
69
107
|
context "when application scopes contain some of the default scopes and no scope is passed" do
|
70
108
|
before do
|
71
109
|
client.update(scopes: "read write public")
|
@@ -4,6 +4,7 @@ require "spec_helper"
|
|
4
4
|
|
5
5
|
feature "Implicit Grant Flow Errors" do
|
6
6
|
background do
|
7
|
+
default_scopes_exist :default
|
7
8
|
config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
|
8
9
|
config_is_set(:grant_flows, ["implicit"])
|
9
10
|
client_exists
|
@@ -15,20 +16,31 @@ feature "Implicit Grant Flow Errors" do
|
|
15
16
|
access_token_should_not_exist
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
].each do |error|
|
22
|
-
scenario "displays #{error.last} error for invalid #{error.first}" do
|
23
|
-
visit authorization_endpoint_url(client: @client, error.first => "invalid", response_type: "token")
|
19
|
+
context "when validate client_id param" do
|
20
|
+
scenario "displays invalid_client error for invalid client_id" do
|
21
|
+
visit authorization_endpoint_url(client_id: "invalid", response_type: "token")
|
24
22
|
i_should_not_see "Authorize"
|
25
|
-
i_should_see_translated_error_message
|
23
|
+
i_should_see_translated_error_message :invalid_client
|
26
24
|
end
|
27
25
|
|
28
|
-
scenario "displays
|
29
|
-
visit authorization_endpoint_url(
|
26
|
+
scenario "displays invalid_request error when client_id is missing" do
|
27
|
+
visit authorization_endpoint_url(client_id: "", response_type: "token")
|
30
28
|
i_should_not_see "Authorize"
|
31
|
-
|
29
|
+
i_should_see_translated_invalid_request_error_message :missing_param, :client_id
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when validate redirect_uri param" do
|
34
|
+
scenario "displays invalid_redirect_uri error for invalid redirect_uri" do
|
35
|
+
visit authorization_endpoint_url(client: @client, redirect_uri: "invalid", response_type: "token")
|
36
|
+
i_should_not_see "Authorize"
|
37
|
+
i_should_see_translated_error_message :invalid_redirect_uri
|
38
|
+
end
|
39
|
+
|
40
|
+
scenario "displays invalid_redirect_uri error when redirect_uri is missing" do
|
41
|
+
visit authorization_endpoint_url(client: @client, redirect_uri: "", response_type: "token")
|
42
|
+
i_should_not_see "Authorize"
|
43
|
+
i_should_see_translated_error_message :invalid_redirect_uri
|
32
44
|
end
|
33
45
|
end
|
34
46
|
end
|
@@ -4,6 +4,7 @@ require "spec_helper"
|
|
4
4
|
|
5
5
|
feature "Implicit Grant Flow (feature spec)" do
|
6
6
|
background do
|
7
|
+
default_scopes_exist :default
|
7
8
|
config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
|
8
9
|
config_is_set(:grant_flows, ["implicit"])
|
9
10
|
client_exists
|
@@ -25,16 +26,15 @@ feature "Implicit Grant Flow (feature spec)" do
|
|
25
26
|
@client.update(scopes: "public write read")
|
26
27
|
end
|
27
28
|
|
28
|
-
scenario "
|
29
|
+
scenario "scope is invalid because default scope is different from application scope" do
|
29
30
|
default_scopes_exist :admin
|
30
31
|
visit authorization_endpoint_url(client: @client, response_type: "token")
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
expect(token.scopes).to be_empty
|
32
|
+
response_status_should_be 200
|
33
|
+
i_should_not_see "Authorize"
|
34
|
+
i_should_see_translated_error_message :invalid_scope
|
35
35
|
end
|
36
36
|
|
37
|
-
scenario "access token has scopes which are common in application
|
37
|
+
scenario "access token has scopes which are common in application scopes and default scopes" do
|
38
38
|
default_scopes_exist :public, :write
|
39
39
|
visit authorization_endpoint_url(client: @client, response_type: "token")
|
40
40
|
click_on "Authorize"
|
@@ -46,6 +46,7 @@ end
|
|
46
46
|
|
47
47
|
describe "Implicit Grant Flow (request spec)" do
|
48
48
|
before do
|
49
|
+
default_scopes_exist :default
|
49
50
|
config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
|
50
51
|
config_is_set(:grant_flows, ["implicit"])
|
51
52
|
client_exists
|
@@ -56,7 +57,7 @@ describe "Implicit Grant Flow (request spec)" do
|
|
56
57
|
it "should return a new token each request" do
|
57
58
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(false)
|
58
59
|
|
59
|
-
token = client_is_authorized(@client, @resource_owner)
|
60
|
+
token = client_is_authorized(@client, @resource_owner, scopes: "default")
|
60
61
|
|
61
62
|
post "/oauth/authorize",
|
62
63
|
params: {
|
@@ -73,7 +74,7 @@ describe "Implicit Grant Flow (request spec)" do
|
|
73
74
|
it "should return the same token if it is still accessible" do
|
74
75
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
75
76
|
|
76
|
-
token = client_is_authorized(@client, @resource_owner)
|
77
|
+
token = client_is_authorized(@client, @resource_owner, scopes: "default")
|
77
78
|
|
78
79
|
post "/oauth/authorize",
|
79
80
|
params: {
|
@@ -31,6 +31,43 @@ describe "Resource Owner Password Credentials Flow" do
|
|
31
31
|
context "with non-confidential/public client" do
|
32
32
|
let(:client_attributes) { { confidential: false } }
|
33
33
|
|
34
|
+
context "when configured to check application supported grant flow" do
|
35
|
+
before do
|
36
|
+
Doorkeeper.configuration.instance_variable_set(
|
37
|
+
:@allow_grant_flow_for_client,
|
38
|
+
->(_grant_flow, client) { client.name == "admin" }
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
scenario "forbids the request when doesn't satisfy condition" do
|
43
|
+
@client.update(name: "sample app")
|
44
|
+
|
45
|
+
expect do
|
46
|
+
post password_token_endpoint_url(
|
47
|
+
client_id: @client.uid,
|
48
|
+
client_secret: "foobar",
|
49
|
+
resource_owner: @resource_owner
|
50
|
+
)
|
51
|
+
end.not_to(change { Doorkeeper::AccessToken.count })
|
52
|
+
|
53
|
+
expect(response.status).to eq(401)
|
54
|
+
should_have_json "error", "invalid_client"
|
55
|
+
end
|
56
|
+
|
57
|
+
scenario "allows the request when satisfies condition" do
|
58
|
+
@client.update(name: "admin")
|
59
|
+
|
60
|
+
expect do
|
61
|
+
post password_token_endpoint_url(client_id: @client.uid, resource_owner: @resource_owner)
|
62
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
63
|
+
|
64
|
+
token = Doorkeeper::AccessToken.first
|
65
|
+
|
66
|
+
expect(token.application_id).to eq @client.id
|
67
|
+
should_have_json "access_token", token.token
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
34
71
|
context "when client_secret absent" do
|
35
72
|
it "should issue new token" do
|
36
73
|
expect do
|
@@ -172,7 +172,7 @@ describe "Refresh Token Flow" do
|
|
172
172
|
post refresh_token_endpoint_url(client: @client, refresh_token: @token.refresh_token)
|
173
173
|
|
174
174
|
should_not_have_json "refresh_token"
|
175
|
-
should_have_json "error", "
|
175
|
+
should_have_json "error", "invalid_grant"
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
@@ -40,16 +40,14 @@ describe "Revoke Token Flow" do
|
|
40
40
|
end
|
41
41
|
|
42
42
|
context "with invalid token to revoke" do
|
43
|
-
it "should not revoke any tokens and respond
|
43
|
+
it "should not revoke any tokens and respond with forbidden" do
|
44
44
|
expect do
|
45
45
|
post revocation_token_endpoint_url,
|
46
46
|
params: { token: "I_AM_AN_INVALID_TOKEN" },
|
47
47
|
headers: headers
|
48
48
|
end.not_to(change { Doorkeeper::AccessToken.where(revoked_at: nil).count })
|
49
49
|
|
50
|
-
|
51
|
-
# token is invalid
|
52
|
-
expect(response).to be_successful
|
50
|
+
expect(response).to be_forbidden
|
53
51
|
end
|
54
52
|
end
|
55
53
|
|
@@ -59,19 +57,23 @@ describe "Revoke Token Flow" do
|
|
59
57
|
credentials = Base64.encode64("#{client_id}:poop")
|
60
58
|
{ "HTTP_AUTHORIZATION" => "Basic #{credentials}" }
|
61
59
|
end
|
62
|
-
it "should not revoke any tokens and respond
|
60
|
+
it "should not revoke any tokens and respond with forbidden" do
|
63
61
|
post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers
|
64
62
|
|
65
|
-
expect(response).to
|
63
|
+
expect(response).to be_forbidden
|
64
|
+
expect(response.body).to include("unauthorized_client")
|
65
|
+
expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
|
66
66
|
expect(access_token.reload.revoked?).to be_falsey
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
70
|
context "with no credentials and a valid token" do
|
71
|
-
it "should not revoke any tokens and respond
|
71
|
+
it "should not revoke any tokens and respond with forbidden" do
|
72
72
|
post revocation_token_endpoint_url, params: { token: access_token.token }
|
73
73
|
|
74
|
-
expect(response).to
|
74
|
+
expect(response).to be_forbidden
|
75
|
+
expect(response.body).to include("unauthorized_client")
|
76
|
+
expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
|
75
77
|
expect(access_token.reload.revoked?).to be_falsey
|
76
78
|
end
|
77
79
|
end
|
@@ -88,7 +90,9 @@ describe "Revoke Token Flow" do
|
|
88
90
|
it "should not revoke the token as its unauthorized" do
|
89
91
|
post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers
|
90
92
|
|
91
|
-
expect(response).to
|
93
|
+
expect(response).to be_forbidden
|
94
|
+
expect(response.body).to include("unauthorized_client")
|
95
|
+
expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
|
92
96
|
expect(access_token.reload.revoked?).to be_falsey
|
93
97
|
end
|
94
98
|
end
|
@@ -127,14 +131,18 @@ describe "Revoke Token Flow" do
|
|
127
131
|
it "should not revoke the access token provided" do
|
128
132
|
post revocation_token_endpoint_url, params: { token: access_token.token }
|
129
133
|
|
130
|
-
expect(response).to
|
134
|
+
expect(response).to be_forbidden
|
135
|
+
expect(response.body).to include("unauthorized_client")
|
136
|
+
expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
|
131
137
|
expect(access_token.reload.revoked?).to be_falsey
|
132
138
|
end
|
133
139
|
|
134
140
|
it "should not revoke the refresh token provided" do
|
135
141
|
post revocation_token_endpoint_url, params: { token: access_token.token }
|
136
142
|
|
137
|
-
expect(response).to
|
143
|
+
expect(response).to be_forbidden
|
144
|
+
expect(response.body).to include("unauthorized_client")
|
145
|
+
expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
|
138
146
|
expect(access_token.reload.revoked?).to be_falsey
|
139
147
|
end
|
140
148
|
end
|
@@ -16,7 +16,7 @@ module Doorkeeper
|
|
16
16
|
# Tries to find ORM from the Gemfile used to run test suite
|
17
17
|
def self.detect_orm
|
18
18
|
orm = (ENV["BUNDLE_GEMFILE"] || "").match(/Gemfile\.(.+)\.rb/)
|
19
|
-
(orm && orm[1] || :active_record).to_sym
|
19
|
+
(orm && orm[1] || ENV["ORM"] || :active_record).to_sym
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -54,7 +54,7 @@ module RequestSpecHelper
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def with_header(header, value)
|
57
|
-
page.driver.header
|
57
|
+
page.driver.header(header, value)
|
58
58
|
end
|
59
59
|
|
60
60
|
def basic_auth_header_for_client(client)
|
@@ -86,8 +86,20 @@ module RequestSpecHelper
|
|
86
86
|
i_should_see translated_error_message(key)
|
87
87
|
end
|
88
88
|
|
89
|
+
def i_should_not_see_translated_error_message(key)
|
90
|
+
i_should_not_see translated_error_message(key)
|
91
|
+
end
|
92
|
+
|
89
93
|
def translated_error_message(key)
|
90
|
-
I18n.translate
|
94
|
+
I18n.translate(key, scope: %i[doorkeeper errors messages])
|
95
|
+
end
|
96
|
+
|
97
|
+
def i_should_see_translated_invalid_request_error_message(key, value)
|
98
|
+
i_should_see translated_invalid_request_error_message(key, value)
|
99
|
+
end
|
100
|
+
|
101
|
+
def translated_invalid_request_error_message(key, value)
|
102
|
+
I18n.translate key, scope: %i[doorkeeper errors messages invalid_request], value: value
|
91
103
|
end
|
92
104
|
|
93
105
|
def response_status_should_be(status)
|