doorkeeper 5.1.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +3 -3
  3. data/CHANGELOG.md +881 -0
  4. data/CONTRIBUTING.md +11 -9
  5. data/Dangerfile +2 -2
  6. data/Dockerfile +29 -0
  7. data/Gemfile +4 -3
  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 +2 -2
  12. data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
  13. data/app/controllers/doorkeeper/applications_controller.rb +1 -0
  14. data/app/controllers/doorkeeper/authorizations_controller.rb +16 -9
  15. data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -2
  16. data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
  17. data/app/views/doorkeeper/applications/_form.html.erb +0 -6
  18. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  19. data/config/locales/en.yml +8 -2
  20. data/doorkeeper.gemspec +9 -1
  21. data/gemfiles/rails_5_0.gemfile +3 -2
  22. data/gemfiles/rails_5_1.gemfile +3 -2
  23. data/gemfiles/rails_5_2.gemfile +3 -2
  24. data/gemfiles/rails_6_0.gemfile +4 -3
  25. data/gemfiles/rails_master.gemfile +3 -2
  26. data/lib/doorkeeper/config/option.rb +13 -7
  27. data/lib/doorkeeper/config.rb +157 -42
  28. data/lib/doorkeeper/errors.rb +13 -18
  29. data/lib/doorkeeper/grape/helpers.rb +6 -2
  30. data/lib/doorkeeper/helpers/controller.rb +28 -7
  31. data/lib/doorkeeper/models/access_grant_mixin.rb +7 -6
  32. data/lib/doorkeeper/models/access_token_mixin.rb +99 -20
  33. data/lib/doorkeeper/models/application_mixin.rb +3 -3
  34. data/lib/doorkeeper/models/concerns/ownership.rb +1 -1
  35. data/lib/doorkeeper/models/concerns/reusable.rb +1 -1
  36. data/lib/doorkeeper/models/concerns/revocable.rb +0 -27
  37. data/lib/doorkeeper/oauth/authorization/code.rb +12 -14
  38. data/lib/doorkeeper/oauth/authorization/token.rb +10 -7
  39. data/lib/doorkeeper/oauth/authorization_code_request.rb +31 -15
  40. data/lib/doorkeeper/oauth/base_request.rb +10 -4
  41. data/lib/doorkeeper/oauth/client.rb +7 -8
  42. data/lib/doorkeeper/oauth/client_credentials/creator.rb +23 -2
  43. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -7
  44. data/lib/doorkeeper/oauth/client_credentials/{validation.rb → validator.rb} +10 -2
  45. data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
  46. data/lib/doorkeeper/oauth/code_request.rb +5 -11
  47. data/lib/doorkeeper/oauth/code_response.rb +4 -4
  48. data/lib/doorkeeper/oauth/error.rb +1 -1
  49. data/lib/doorkeeper/oauth/error_response.rb +6 -6
  50. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +7 -5
  51. data/lib/doorkeeper/oauth/helpers/unique_token.rb +8 -5
  52. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +19 -5
  53. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  54. data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -2
  55. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  56. data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
  57. data/lib/doorkeeper/oauth/pre_authorization.rb +76 -38
  58. data/lib/doorkeeper/oauth/refresh_token_request.rb +17 -14
  59. data/lib/doorkeeper/oauth/token.rb +2 -2
  60. data/lib/doorkeeper/oauth/token_introspection.rb +26 -16
  61. data/lib/doorkeeper/oauth/token_request.rb +4 -18
  62. data/lib/doorkeeper/orm/active_record/access_grant.rb +4 -43
  63. data/lib/doorkeeper/orm/active_record/access_token.rb +4 -35
  64. data/lib/doorkeeper/orm/active_record/application.rb +5 -83
  65. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +53 -0
  66. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +47 -0
  67. data/lib/doorkeeper/orm/active_record/mixins/application.rb +128 -0
  68. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
  69. data/lib/doorkeeper/orm/active_record.rb +21 -6
  70. data/lib/doorkeeper/rails/helpers.rb +4 -4
  71. data/lib/doorkeeper/rails/routes.rb +5 -7
  72. data/lib/doorkeeper/rake/db.rake +3 -3
  73. data/lib/doorkeeper/request/authorization_code.rb +5 -3
  74. data/lib/doorkeeper/request/client_credentials.rb +2 -2
  75. data/lib/doorkeeper/request/password.rb +2 -2
  76. data/lib/doorkeeper/request/refresh_token.rb +3 -3
  77. data/lib/doorkeeper/request.rb +7 -12
  78. data/lib/doorkeeper/server.rb +3 -7
  79. data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
  80. data/lib/doorkeeper/version.rb +1 -1
  81. data/lib/doorkeeper.rb +6 -3
  82. data/lib/generators/doorkeeper/application_owner_generator.rb +1 -1
  83. data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
  84. data/lib/generators/doorkeeper/migration_generator.rb +1 -1
  85. data/lib/generators/doorkeeper/pkce_generator.rb +1 -1
  86. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -7
  87. data/lib/generators/doorkeeper/templates/initializer.rb +149 -33
  88. data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
  89. data/spec/controllers/application_metal_controller_spec.rb +1 -1
  90. data/spec/controllers/applications_controller_spec.rb +95 -1
  91. data/spec/controllers/authorizations_controller_spec.rb +156 -75
  92. data/spec/controllers/protected_resources_controller_spec.rb +28 -20
  93. data/spec/controllers/token_info_controller_spec.rb +1 -1
  94. data/spec/controllers/tokens_controller_spec.rb +206 -38
  95. data/spec/dummy/config/application.rb +3 -1
  96. data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
  97. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -3
  98. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +1 -1
  99. data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +1 -1
  100. data/spec/generators/install_generator_spec.rb +1 -1
  101. data/spec/generators/previous_refresh_token_generator_spec.rb +2 -2
  102. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  103. data/spec/lib/config_spec.rb +104 -7
  104. data/spec/lib/doorkeeper_spec.rb +1 -1
  105. data/spec/lib/models/revocable_spec.rb +3 -3
  106. data/spec/lib/oauth/authorization_code_request_spec.rb +129 -115
  107. data/spec/lib/oauth/base_request_spec.rb +154 -135
  108. data/spec/lib/oauth/base_response_spec.rb +27 -29
  109. data/spec/lib/oauth/client/credentials_spec.rb +1 -1
  110. data/spec/lib/oauth/client_credentials/creator_spec.rb +43 -3
  111. data/spec/lib/oauth/client_credentials/issuer_spec.rb +12 -12
  112. data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
  113. data/spec/lib/oauth/client_credentials_integration_spec.rb +16 -18
  114. data/spec/lib/oauth/client_credentials_request_spec.rb +78 -80
  115. data/spec/lib/oauth/client_spec.rb +26 -26
  116. data/spec/lib/oauth/code_request_spec.rb +34 -35
  117. data/spec/lib/oauth/code_response_spec.rb +21 -25
  118. data/spec/lib/oauth/error_response_spec.rb +42 -44
  119. data/spec/lib/oauth/error_spec.rb +12 -14
  120. data/spec/lib/oauth/forbidden_token_response_spec.rb +11 -13
  121. data/spec/lib/oauth/helpers/scope_checker_spec.rb +30 -18
  122. data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
  123. data/spec/lib/oauth/invalid_request_response_spec.rb +73 -0
  124. data/spec/lib/oauth/invalid_token_response_spec.rb +32 -34
  125. data/spec/lib/oauth/password_access_token_request_spec.rb +145 -147
  126. data/spec/lib/oauth/pre_authorization_spec.rb +161 -153
  127. data/spec/lib/oauth/refresh_token_request_spec.rb +138 -138
  128. data/spec/lib/oauth/scopes_spec.rb +104 -106
  129. data/spec/lib/oauth/token_request_spec.rb +117 -110
  130. data/spec/lib/oauth/token_response_spec.rb +71 -73
  131. data/spec/lib/oauth/token_spec.rb +121 -123
  132. data/spec/lib/server_spec.rb +0 -12
  133. data/spec/models/doorkeeper/access_grant_spec.rb +21 -4
  134. data/spec/models/doorkeeper/access_token_spec.rb +42 -11
  135. data/spec/models/doorkeeper/application_spec.rb +114 -77
  136. data/spec/requests/applications/applications_request_spec.rb +1 -1
  137. data/spec/requests/endpoints/authorization_spec.rb +26 -8
  138. data/spec/requests/endpoints/token_spec.rb +1 -1
  139. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
  140. data/spec/requests/flows/authorization_code_spec.rb +123 -45
  141. data/spec/requests/flows/client_credentials_spec.rb +38 -0
  142. data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
  143. data/spec/requests/flows/implicit_grant_spec.rb +9 -8
  144. data/spec/requests/flows/password_spec.rb +67 -10
  145. data/spec/requests/flows/refresh_token_spec.rb +20 -20
  146. data/spec/requests/flows/revoke_token_spec.rb +37 -23
  147. data/spec/spec_helper.rb +1 -4
  148. data/spec/support/doorkeeper_rspec.rb +1 -1
  149. data/spec/support/helpers/request_spec_helper.rb +14 -2
  150. data/spec/support/shared/controllers_shared_context.rb +33 -23
  151. data/spec/validators/redirect_uri_validator_spec.rb +41 -16
  152. metadata +19 -16
  153. data/.coveralls.yml +0 -1
  154. data/.github/ISSUE_TEMPLATE.md +0 -25
  155. data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  156. data/.gitignore +0 -20
  157. data/.gitlab-ci.yml +0 -16
  158. data/.hound.yml +0 -3
  159. data/.rspec +0 -1
  160. data/.rubocop.yml +0 -50
  161. data/.travis.yml +0 -35
  162. data/app/validators/redirect_uri_validator.rb +0 -50
  163. data/spec/support/http_method_shim.rb +0 -29
@@ -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"
@@ -92,12 +149,15 @@ feature "Authorization Code Flow" do
92
149
  click_on "Authorize"
93
150
 
94
151
  authorization_code = Doorkeeper::AccessGrant.first.token
95
- page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
96
- redirect_uri: @client.redirect_uri)
152
+ page.driver.post token_endpoint_url(
153
+ code: authorization_code, client_id: @client.uid,
154
+ redirect_uri: @client.redirect_uri,
155
+ )
97
156
 
98
157
  expect(Doorkeeper::AccessToken.count).to be_zero
99
158
 
100
159
  should_have_json "error", "invalid_client"
160
+ should_have_json "error_description", translated_error_message(:invalid_client)
101
161
  end
102
162
 
103
163
  scenario "resource owner requests an access token with authorization code but without client id" do
@@ -105,12 +165,15 @@ feature "Authorization Code Flow" do
105
165
  click_on "Authorize"
106
166
 
107
167
  authorization_code = Doorkeeper::AccessGrant.first.token
108
- page.driver.post token_endpoint_url(code: authorization_code, client_secret: @client.secret,
109
- redirect_uri: @client.redirect_uri)
168
+ page.driver.post token_endpoint_url(
169
+ code: authorization_code, client_secret: @client.secret,
170
+ redirect_uri: @client.redirect_uri,
171
+ )
110
172
 
111
173
  expect(Doorkeeper::AccessToken.count).to be_zero
112
174
 
113
175
  should_have_json "error", "invalid_client"
176
+ should_have_json "error_description", translated_error_message(:invalid_client)
114
177
  end
115
178
 
116
179
  scenario "silently authorizes if matching token exists" do
@@ -136,7 +199,7 @@ feature "Authorization Code Flow" do
136
199
  visit authorization_endpoint_url(
137
200
  client: @client,
138
201
  code_challenge: code_challenge,
139
- code_challenge_method: "plain"
202
+ code_challenge_method: "plain",
140
203
  )
141
204
  click_on "Authorize"
142
205
 
@@ -153,13 +216,14 @@ feature "Authorization Code Flow" do
153
216
  create_access_token authorization_code, @client, code_verifier
154
217
 
155
218
  should_have_json "error", "invalid_grant"
219
+ should_have_json "error_description", translated_error_message(:invalid_grant)
156
220
  end
157
221
 
158
222
  scenario "mobile app requests an access token with authorization code and plain code challenge method" do
159
223
  visit authorization_endpoint_url(
160
224
  client: @client,
161
225
  code_challenge: code_challenge,
162
- code_challenge_method: "plain"
226
+ code_challenge_method: "plain",
163
227
  )
164
228
  click_on "Authorize"
165
229
 
@@ -175,17 +239,36 @@ feature "Authorization Code Flow" do
175
239
  should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
176
240
  end
177
241
 
178
- scenario "mobile app requests an access token with authorization code and code_challenge" do
179
- visit authorization_endpoint_url(client: @client,
180
- code_challenge: code_verifier,
181
- code_challenge_method: "plain")
242
+ scenario "mobile app requests an access token with authorization code but without code_verifier" do
243
+ visit authorization_endpoint_url(
244
+ client: @client,
245
+ code_challenge: code_challenge,
246
+ code_challenge_method: "plain",
247
+ )
248
+ click_on "Authorize"
249
+
250
+ authorization_code = current_params["code"]
251
+ create_access_token authorization_code, @client, nil
252
+
253
+ should_not_have_json "access_token"
254
+ should_have_json "error", "invalid_request"
255
+ should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code_verifier)
256
+ end
257
+
258
+ scenario "mobile app requests an access token with authorization code with wrong code_verifier" do
259
+ visit authorization_endpoint_url(
260
+ client: @client,
261
+ code_challenge: code_challenge,
262
+ code_challenge_method: "plain",
263
+ )
182
264
  click_on "Authorize"
183
265
 
184
266
  authorization_code = current_params["code"]
185
- create_access_token authorization_code, @client, code_verifier: nil
267
+ create_access_token authorization_code, @client, "wrong_code_verifier"
186
268
 
187
269
  should_not_have_json "access_token"
188
270
  should_have_json "error", "invalid_grant"
271
+ should_have_json "error_description", translated_error_message(:invalid_grant)
189
272
  end
190
273
  end
191
274
 
@@ -197,7 +280,7 @@ feature "Authorization Code Flow" do
197
280
  visit authorization_endpoint_url(
198
281
  client: @client,
199
282
  code_challenge: code_challenge,
200
- code_challenge_method: "S256"
283
+ code_challenge_method: "S256",
201
284
  )
202
285
  click_on "Authorize"
203
286
 
@@ -210,7 +293,7 @@ feature "Authorization Code Flow" do
210
293
  visit authorization_endpoint_url(
211
294
  client: @client,
212
295
  code_challenge: code_challenge,
213
- code_challenge_method: "S256"
296
+ code_challenge_method: "S256",
214
297
  )
215
298
  click_on "Authorize"
216
299
 
@@ -226,32 +309,24 @@ feature "Authorization Code Flow" do
226
309
  should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
227
310
  end
228
311
 
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
312
  scenario "mobile app requests an access token with authorization code and without secret" do
243
313
  visit authorization_endpoint_url(
244
314
  client: @client,
245
315
  code_challenge: code_challenge,
246
- code_challenge_method: "S256"
316
+ code_challenge_method: "S256",
247
317
  )
248
318
  click_on "Authorize"
249
319
 
250
320
  authorization_code = current_params["code"]
251
- page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
252
- redirect_uri: @client.redirect_uri, code_verifier: code_verifier)
253
- should_have_json "error", "invalid_client"
321
+ page.driver.post token_endpoint_url(
322
+ code: authorization_code,
323
+ client_id: @client.uid,
324
+ redirect_uri: @client.redirect_uri,
325
+ code_verifier: code_verifier,
326
+ )
254
327
  should_not_have_json "access_token"
328
+ should_have_json "error", "invalid_client"
329
+ should_have_json "error_description", translated_error_message(:invalid_client)
255
330
  end
256
331
 
257
332
  scenario "mobile app requests an access token with authorization code and without secret but is marked as not confidential" do
@@ -264,7 +339,7 @@ feature "Authorization Code Flow" do
264
339
  code: authorization_code,
265
340
  client_id: @client.uid,
266
341
  redirect_uri: @client.redirect_uri,
267
- code_verifier: code_verifier
342
+ code_verifier: code_verifier,
268
343
  )
269
344
  should_not_have_json "error"
270
345
 
@@ -277,7 +352,7 @@ feature "Authorization Code Flow" do
277
352
  visit authorization_endpoint_url(
278
353
  client: @client,
279
354
  code_challenge: code_challenge,
280
- code_challenge_method: "S256"
355
+ code_challenge_method: "S256",
281
356
  )
282
357
  click_on "Authorize"
283
358
 
@@ -286,13 +361,14 @@ feature "Authorization Code Flow" do
286
361
 
287
362
  should_not_have_json "access_token"
288
363
  should_have_json "error", "invalid_request"
364
+ should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code_verifier)
289
365
  end
290
366
 
291
367
  scenario "mobile app requests an access token with authorization code with wrong verifier" do
292
368
  visit authorization_endpoint_url(
293
369
  client: @client,
294
370
  code_challenge: code_challenge,
295
- code_challenge_method: "S256"
371
+ code_challenge_method: "S256",
296
372
  )
297
373
  click_on "Authorize"
298
374
 
@@ -301,13 +377,14 @@ feature "Authorization Code Flow" do
301
377
 
302
378
  should_not_have_json "access_token"
303
379
  should_have_json "error", "invalid_grant"
380
+ should_have_json "error_description", translated_error_message(:invalid_grant)
304
381
  end
305
382
 
306
383
  scenario "code_challenge_mehthod in token request is totally ignored" do
307
384
  visit authorization_endpoint_url(
308
385
  client: @client,
309
386
  code_challenge: code_challenge,
310
- code_challenge_method: "S256"
387
+ code_challenge_method: "S256",
311
388
  )
312
389
  click_on "Authorize"
313
390
 
@@ -316,11 +393,12 @@ feature "Authorization Code Flow" do
316
393
  code: authorization_code,
317
394
  client: @client,
318
395
  code_verifier: code_challenge,
319
- code_challenge_method: "plain"
396
+ code_challenge_method: "plain",
320
397
  )
321
398
 
322
399
  should_not_have_json "access_token"
323
400
  should_have_json "error", "invalid_grant"
401
+ should_have_json "error_description", translated_error_message(:invalid_grant)
324
402
  end
325
403
 
326
404
  scenario "expects to set code_challenge_method explicitely without fallback" do
@@ -332,16 +410,15 @@ feature "Authorization Code Flow" do
332
410
 
333
411
  context "when application scopes are present and no scope is passed" do
334
412
  background do
335
- @client.update(scopes: "public write read")
413
+ @client.update(scopes: "public write read default")
336
414
  end
337
415
 
338
- scenario "access grant has no scope" do
416
+ scenario "scope is invalid because default scope is different from application scope" do
339
417
  default_scopes_exist :admin
340
418
  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
419
+ response_status_should_be 200
420
+ i_should_not_see "Authorize"
421
+ i_should_see_translated_error_message :invalid_scope
345
422
  end
346
423
 
347
424
  scenario "access grant have scopes which are common in application scopees and default scopes" do
@@ -442,6 +519,7 @@ describe "Authorization Code Flow" do
442
519
 
443
520
  should_not_have_json "access_token"
444
521
  should_have_json "error", "invalid_grant"
522
+ should_have_json "error_description", translated_error_message(:invalid_grant)
445
523
  end
446
524
  end
447
525
  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
@@ -62,7 +99,7 @@ describe "Resource Owner Password Credentials Flow" do
62
99
  post password_token_endpoint_url(
63
100
  client_id: @client.uid,
64
101
  client_secret: "foobar",
65
- resource_owner: @resource_owner
102
+ resource_owner: @resource_owner,
66
103
  )
67
104
  end.not_to(change { Doorkeeper::AccessToken.count })
68
105
 
@@ -204,9 +241,11 @@ describe "Resource Owner Password Credentials Flow" do
204
241
 
205
242
  context "with invalid scopes" do
206
243
  subject do
207
- post password_token_endpoint_url(client: @client,
208
- resource_owner: @resource_owner,
209
- scope: "random")
244
+ post password_token_endpoint_url(
245
+ client: @client,
246
+ resource_owner: @resource_owner,
247
+ scope: "random",
248
+ )
210
249
  end
211
250
 
212
251
  it "should not issue new token" do
@@ -226,9 +265,11 @@ describe "Resource Owner Password Credentials Flow" do
226
265
  context "with invalid user credentials" do
227
266
  it "should not issue new token with bad password" do
228
267
  expect do
229
- post password_token_endpoint_url(client: @client,
230
- resource_owner_username: @resource_owner.name,
231
- resource_owner_password: "wrongpassword")
268
+ post password_token_endpoint_url(
269
+ client: @client,
270
+ resource_owner_username: @resource_owner.name,
271
+ resource_owner_password: "wrongpassword",
272
+ )
232
273
  end.to_not(change { Doorkeeper::AccessToken.count })
233
274
  end
234
275
 
@@ -237,14 +278,30 @@ describe "Resource Owner Password Credentials Flow" do
237
278
  post password_token_endpoint_url(client: @client)
238
279
  end.to_not(change { Doorkeeper::AccessToken.count })
239
280
  end
281
+
282
+ it "should not issue new token if resource_owner_from_credentials returned false or nil" do
283
+ config_is_set(:resource_owner_from_credentials) { false }
284
+
285
+ expect do
286
+ post password_token_endpoint_url(client: @client)
287
+ end.to_not(change { Doorkeeper::AccessToken.count })
288
+
289
+ config_is_set(:resource_owner_from_credentials) { nil }
290
+
291
+ expect do
292
+ post password_token_endpoint_url(client: @client)
293
+ end.to_not(change { Doorkeeper::AccessToken.count })
294
+ end
240
295
  end
241
296
 
242
297
  context "with invalid confidential client credentials" do
243
298
  it "should not issue new token with bad client credentials" do
244
299
  expect do
245
- post password_token_endpoint_url(client_id: @client.uid,
246
- client_secret: "bad_secret",
247
- resource_owner: @resource_owner)
300
+ post password_token_endpoint_url(
301
+ client_id: @client.uid,
302
+ client_secret: "bad_secret",
303
+ resource_owner: @resource_owner,
304
+ )
248
305
  end.to_not(change { Doorkeeper::AccessToken.count })
249
306
  end
250
307
  end