doorkeeper 5.1.0 → 5.2.0

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.

Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +1 -1
  3. data/CHANGELOG.md +843 -0
  4. data/CONTRIBUTING.md +11 -9
  5. data/Dangerfile +2 -2
  6. data/Dockerfile +29 -0
  7. data/Gemfile +2 -1
  8. data/NEWS.md +1 -814
  9. data/README.md +11 -3
  10. data/RELEASING.md +6 -5
  11. data/app/controllers/doorkeeper/application_controller.rb +1 -1
  12. data/app/controllers/doorkeeper/application_metal_controller.rb +2 -1
  13. data/app/controllers/doorkeeper/applications_controller.rb +2 -0
  14. data/app/controllers/doorkeeper/authorizations_controller.rb +14 -7
  15. data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
  16. data/app/views/doorkeeper/applications/_form.html.erb +0 -6
  17. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  18. data/config/locales/en.yml +8 -2
  19. data/doorkeeper.gemspec +9 -1
  20. data/gemfiles/rails_5_0.gemfile +1 -0
  21. data/gemfiles/rails_5_1.gemfile +1 -0
  22. data/gemfiles/rails_5_2.gemfile +1 -0
  23. data/gemfiles/rails_6_0.gemfile +2 -1
  24. data/gemfiles/rails_master.gemfile +1 -0
  25. data/lib/doorkeeper/config/option.rb +13 -7
  26. data/lib/doorkeeper/config.rb +88 -6
  27. data/lib/doorkeeper/errors.rb +13 -18
  28. data/lib/doorkeeper/grape/helpers.rb +5 -1
  29. data/lib/doorkeeper/helpers/controller.rb +20 -3
  30. data/lib/doorkeeper/models/access_token_mixin.rb +43 -2
  31. data/lib/doorkeeper/oauth/authorization/code.rb +11 -13
  32. data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
  33. data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -9
  34. data/lib/doorkeeper/oauth/base_request.rb +2 -0
  35. data/lib/doorkeeper/oauth/client_credentials/creator.rb +14 -0
  36. data/lib/doorkeeper/oauth/client_credentials/validation.rb +8 -0
  37. data/lib/doorkeeper/oauth/code_request.rb +5 -11
  38. data/lib/doorkeeper/oauth/code_response.rb +2 -2
  39. data/lib/doorkeeper/oauth/error_response.rb +1 -1
  40. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +18 -4
  41. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  42. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  43. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
  44. data/lib/doorkeeper/oauth/pre_authorization.rb +70 -37
  45. data/lib/doorkeeper/oauth/refresh_token_request.rb +13 -10
  46. data/lib/doorkeeper/oauth/token_introspection.rb +23 -13
  47. data/lib/doorkeeper/oauth/token_request.rb +4 -18
  48. data/lib/doorkeeper/orm/active_record/access_grant.rb +1 -1
  49. data/lib/doorkeeper/orm/active_record/access_token.rb +2 -2
  50. data/lib/doorkeeper/orm/active_record/application.rb +8 -2
  51. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
  52. data/lib/doorkeeper/orm/active_record.rb +19 -3
  53. data/lib/doorkeeper/request/authorization_code.rb +2 -0
  54. data/lib/doorkeeper/request.rb +6 -11
  55. data/lib/doorkeeper/server.rb +2 -6
  56. data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
  57. data/lib/doorkeeper/version.rb +1 -1
  58. data/lib/doorkeeper.rb +4 -0
  59. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +6 -6
  60. data/lib/generators/doorkeeper/templates/initializer.rb +110 -33
  61. data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
  62. data/spec/controllers/applications_controller_spec.rb +93 -0
  63. data/spec/controllers/authorizations_controller_spec.rb +140 -61
  64. data/spec/controllers/protected_resources_controller_spec.rb +3 -3
  65. data/spec/controllers/tokens_controller_spec.rb +205 -37
  66. data/spec/dummy/config/application.rb +3 -1
  67. data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
  68. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +1 -1
  69. data/spec/lib/config_spec.rb +43 -1
  70. data/spec/lib/oauth/authorization_code_request_spec.rb +11 -1
  71. data/spec/lib/oauth/base_request_spec.rb +33 -16
  72. data/spec/lib/oauth/client_credentials/creator_spec.rb +3 -0
  73. data/spec/lib/oauth/code_request_spec.rb +27 -28
  74. data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
  75. data/spec/lib/oauth/invalid_request_response_spec.rb +75 -0
  76. data/spec/lib/oauth/pre_authorization_spec.rb +76 -66
  77. data/spec/lib/oauth/refresh_token_request_spec.rb +1 -0
  78. data/spec/lib/oauth/token_request_spec.rb +20 -17
  79. data/spec/lib/server_spec.rb +0 -12
  80. data/spec/requests/endpoints/authorization_spec.rb +21 -5
  81. data/spec/requests/endpoints/token_spec.rb +1 -1
  82. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
  83. data/spec/requests/flows/authorization_code_spec.rb +93 -27
  84. data/spec/requests/flows/client_credentials_spec.rb +38 -0
  85. data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
  86. data/spec/requests/flows/implicit_grant_spec.rb +9 -8
  87. data/spec/requests/flows/password_spec.rb +37 -0
  88. data/spec/requests/flows/refresh_token_spec.rb +1 -1
  89. data/spec/requests/flows/revoke_token_spec.rb +19 -11
  90. data/spec/support/doorkeeper_rspec.rb +1 -1
  91. data/spec/support/helpers/request_spec_helper.rb +14 -2
  92. data/spec/validators/redirect_uri_validator_spec.rb +40 -15
  93. metadata +15 -13
  94. data/.coveralls.yml +0 -1
  95. data/.github/ISSUE_TEMPLATE.md +0 -25
  96. data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  97. data/.gitignore +0 -20
  98. data/.gitlab-ci.yml +0 -16
  99. data/.hound.yml +0 -3
  100. data/.rspec +0 -1
  101. data/.rubocop.yml +0 -50
  102. data/.travis.yml +0 -35
  103. data/app/validators/redirect_uri_validator.rb +0 -50
@@ -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
- scenario "Authorization Code Flow with hashing" do
32
- @client.redirect_uri = Doorkeeper.configuration.native_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 test url" do
54
- @client.redirect_uri = Doorkeeper.configuration.native_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 and code_challenge" do
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: code_verifier,
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, code_verifier: nil
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 "access grant has no scope" do
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
- click_on "Authorize"
342
- access_grant_should_exist_for(@client, @resource_owner)
343
- grant = Doorkeeper::AccessGrant.first
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
- %i[client_id invalid_client],
20
- %i[redirect_uri invalid_redirect_uri],
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 error.last
23
+ i_should_see_translated_error_message :invalid_client
26
24
  end
27
25
 
28
- scenario "displays #{error.last} error when #{error.first} is missing" do
29
- visit authorization_endpoint_url(client: @client, error.first => "", response_type: "token")
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
- i_should_see_translated_error_message error.last
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 "access token has no scopes" do
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
- click_on "Authorize"
32
- access_token_should_exist_for @client, @resource_owner
33
- token = Doorkeeper::AccessToken.first
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 scopees and default scopes" do
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", "invalid_request"
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 successfully" do
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
- # The authorization server responds with HTTP status code 200 even if
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 successfully" do
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 be_successful
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 successfully" do
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 be_successful
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 be_successful
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 be_successful
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 be_successful
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 header, value
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 key, scope: %i[doorkeeper errors messages]
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)