doorkeeper 4.4.3 → 5.0.3

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 (223) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.gitlab-ci.yml +16 -0
  4. data/.travis.yml +7 -0
  5. data/Appraisals +2 -2
  6. data/Dangerfile +64 -0
  7. data/Gemfile +1 -1
  8. data/NEWS.md +98 -8
  9. data/README.md +110 -12
  10. data/Rakefile +6 -0
  11. data/UPGRADE.md +2 -0
  12. data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
  13. data/app/controllers/doorkeeper/application_controller.rb +6 -3
  14. data/app/controllers/doorkeeper/application_metal_controller.rb +6 -0
  15. data/app/controllers/doorkeeper/applications_controller.rb +46 -24
  16. data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
  17. data/app/controllers/doorkeeper/authorized_applications_controller.rb +21 -2
  18. data/app/controllers/doorkeeper/token_info_controller.rb +2 -0
  19. data/app/controllers/doorkeeper/tokens_controller.rb +4 -6
  20. data/app/helpers/doorkeeper/dashboard_helper.rb +9 -7
  21. data/app/validators/redirect_uri_validator.rb +5 -2
  22. data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
  23. data/app/views/doorkeeper/applications/_form.html.erb +25 -24
  24. data/app/views/doorkeeper/applications/edit.html.erb +1 -1
  25. data/app/views/doorkeeper/applications/index.html.erb +17 -7
  26. data/app/views/doorkeeper/applications/new.html.erb +1 -1
  27. data/app/views/doorkeeper/applications/show.html.erb +6 -6
  28. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  29. data/app/views/doorkeeper/authorizations/new.html.erb +4 -0
  30. data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
  31. data/config/locales/en.yml +10 -1
  32. data/doorkeeper.gemspec +25 -26
  33. data/gemfiles/rails_5_2.gemfile +1 -1
  34. data/gemfiles/rails_master.gemfile +4 -1
  35. data/lib/doorkeeper/config.rb +81 -40
  36. data/lib/doorkeeper/engine.rb +6 -0
  37. data/lib/doorkeeper/errors.rb +17 -3
  38. data/lib/doorkeeper/grape/authorization_decorator.rb +2 -0
  39. data/lib/doorkeeper/grape/helpers.rb +3 -1
  40. data/lib/doorkeeper/helpers/controller.rb +9 -2
  41. data/lib/doorkeeper/models/access_grant_mixin.rb +73 -0
  42. data/lib/doorkeeper/models/access_token_mixin.rb +44 -25
  43. data/lib/doorkeeper/models/application_mixin.rb +2 -0
  44. data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
  45. data/lib/doorkeeper/models/concerns/expirable.rb +2 -0
  46. data/lib/doorkeeper/models/concerns/orderable.rb +2 -0
  47. data/lib/doorkeeper/models/concerns/ownership.rb +2 -0
  48. data/lib/doorkeeper/models/concerns/revocable.rb +2 -0
  49. data/lib/doorkeeper/models/concerns/scopes.rb +3 -1
  50. data/lib/doorkeeper/oauth/authorization/code.rb +33 -8
  51. data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
  52. data/lib/doorkeeper/oauth/authorization/token.rb +38 -14
  53. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +2 -0
  54. data/lib/doorkeeper/oauth/authorization_code_request.rb +29 -2
  55. data/lib/doorkeeper/oauth/base_request.rb +22 -9
  56. data/lib/doorkeeper/oauth/base_response.rb +2 -0
  57. data/lib/doorkeeper/oauth/client/credentials.rb +3 -1
  58. data/lib/doorkeeper/oauth/client.rb +1 -1
  59. data/lib/doorkeeper/oauth/client_credentials/creator.rb +4 -1
  60. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -2
  61. data/lib/doorkeeper/oauth/client_credentials/validation.rb +5 -5
  62. data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -3
  63. data/lib/doorkeeper/oauth/code_request.rb +2 -0
  64. data/lib/doorkeeper/oauth/code_response.rb +2 -0
  65. data/lib/doorkeeper/oauth/error.rb +2 -0
  66. data/lib/doorkeeper/oauth/error_response.rb +21 -3
  67. data/lib/doorkeeper/oauth/forbidden_token_response.rb +9 -2
  68. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +2 -8
  69. data/lib/doorkeeper/oauth/helpers/unique_token.rb +2 -0
  70. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +5 -2
  71. data/lib/doorkeeper/oauth/invalid_token_response.rb +18 -0
  72. data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
  73. data/lib/doorkeeper/oauth/pre_authorization.rb +43 -11
  74. data/lib/doorkeeper/oauth/refresh_token_request.rb +16 -3
  75. data/lib/doorkeeper/oauth/scopes.rb +3 -1
  76. data/lib/doorkeeper/oauth/token.rb +7 -2
  77. data/lib/doorkeeper/oauth/token_introspection.rb +4 -2
  78. data/lib/doorkeeper/oauth/token_request.rb +2 -0
  79. data/lib/doorkeeper/oauth/token_response.rb +6 -2
  80. data/lib/doorkeeper/oauth.rb +13 -0
  81. data/lib/doorkeeper/orm/active_record/application.rb +75 -12
  82. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
  83. data/lib/doorkeeper/orm/active_record.rb +4 -0
  84. data/lib/doorkeeper/rails/helpers.rb +6 -4
  85. data/lib/doorkeeper/rails/routes/mapper.rb +2 -0
  86. data/lib/doorkeeper/rails/routes/mapping.rb +2 -0
  87. data/lib/doorkeeper/rails/routes.rb +23 -8
  88. data/lib/doorkeeper/rake/db.rake +40 -0
  89. data/lib/doorkeeper/rake/setup.rake +6 -0
  90. data/lib/doorkeeper/rake.rb +14 -0
  91. data/lib/doorkeeper/request/authorization_code.rb +1 -1
  92. data/lib/doorkeeper/request/client_credentials.rb +1 -1
  93. data/lib/doorkeeper/request/code.rb +1 -1
  94. data/lib/doorkeeper/request/password.rb +1 -1
  95. data/lib/doorkeeper/request/refresh_token.rb +1 -1
  96. data/lib/doorkeeper/request/strategy.rb +2 -0
  97. data/lib/doorkeeper/request/token.rb +1 -1
  98. data/lib/doorkeeper/request.rb +29 -34
  99. data/lib/doorkeeper/server.rb +2 -0
  100. data/lib/doorkeeper/stale_records_cleaner.rb +20 -0
  101. data/lib/doorkeeper/validations.rb +2 -0
  102. data/lib/doorkeeper/version.rb +6 -24
  103. data/lib/doorkeeper.rb +20 -17
  104. data/lib/generators/doorkeeper/application_owner_generator.rb +23 -18
  105. data/lib/generators/doorkeeper/confidential_applications_generator.rb +32 -0
  106. data/lib/generators/doorkeeper/install_generator.rb +17 -9
  107. data/lib/generators/doorkeeper/migration_generator.rb +23 -18
  108. data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
  109. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -24
  110. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  111. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
  112. data/lib/generators/doorkeeper/templates/initializer.rb +96 -13
  113. data/lib/generators/doorkeeper/templates/migration.rb.erb +2 -3
  114. data/lib/generators/doorkeeper/views_generator.rb +3 -1
  115. data/spec/controllers/application_metal_controller_spec.rb +50 -0
  116. data/spec/controllers/applications_controller_spec.rb +123 -14
  117. data/spec/controllers/authorizations_controller_spec.rb +334 -51
  118. data/spec/controllers/protected_resources_controller_spec.rb +60 -18
  119. data/spec/controllers/token_info_controller_spec.rb +4 -12
  120. data/spec/controllers/tokens_controller_spec.rb +17 -20
  121. data/spec/dummy/Rakefile +1 -1
  122. data/spec/dummy/app/assets/config/manifest.js +2 -0
  123. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +1 -1
  124. data/spec/dummy/app/controllers/home_controller.rb +1 -2
  125. data/spec/dummy/config/application.rb +1 -1
  126. data/spec/dummy/config/boot.rb +2 -4
  127. data/spec/dummy/config/environment.rb +1 -1
  128. data/spec/dummy/config/environments/test.rb +5 -6
  129. data/spec/dummy/config/initializers/doorkeeper.rb +12 -6
  130. data/spec/dummy/config/initializers/new_framework_defaults.rb +2 -0
  131. data/spec/dummy/config/initializers/secret_token.rb +1 -1
  132. data/spec/dummy/config/routes.rb +3 -42
  133. data/spec/dummy/config.ru +1 -1
  134. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +4 -4
  135. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +1 -1
  136. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
  137. data/spec/dummy/db/migrate/{20180210183654_add_confidential_to_application.rb → 20180210183654_add_confidential_to_applications.rb} +1 -1
  138. data/spec/dummy/db/schema.rb +36 -36
  139. data/spec/dummy/script/rails +4 -3
  140. data/spec/factories.rb +6 -6
  141. data/spec/generators/application_owner_generator_spec.rb +1 -1
  142. data/spec/generators/confidential_applications_generator_spec.rb +45 -0
  143. data/spec/generators/install_generator_spec.rb +5 -2
  144. data/spec/generators/migration_generator_spec.rb +1 -1
  145. data/spec/generators/pkce_generator_spec.rb +43 -0
  146. data/spec/generators/previous_refresh_token_generator_spec.rb +1 -1
  147. data/spec/generators/templates/routes.rb +0 -1
  148. data/spec/generators/views_generator_spec.rb +2 -2
  149. data/spec/grape/grape_integration_spec.rb +2 -2
  150. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  151. data/spec/lib/config_spec.rb +105 -39
  152. data/spec/lib/doorkeeper_spec.rb +6 -131
  153. data/spec/lib/models/expirable_spec.rb +0 -3
  154. data/spec/lib/models/revocable_spec.rb +0 -2
  155. data/spec/lib/models/scopes_spec.rb +0 -4
  156. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -4
  157. data/spec/lib/oauth/authorization_code_request_spec.rb +17 -7
  158. data/spec/lib/oauth/base_request_spec.rb +49 -11
  159. data/spec/lib/oauth/base_response_spec.rb +1 -1
  160. data/spec/lib/oauth/client/credentials_spec.rb +2 -4
  161. data/spec/lib/oauth/client_credentials/creator_spec.rb +5 -1
  162. data/spec/lib/oauth/client_credentials/issuer_spec.rb +24 -7
  163. data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
  164. data/spec/lib/oauth/client_credentials_integration_spec.rb +2 -2
  165. data/spec/lib/oauth/client_credentials_request_spec.rb +3 -5
  166. data/spec/lib/oauth/client_spec.rb +0 -3
  167. data/spec/lib/oauth/code_request_spec.rb +5 -3
  168. data/spec/lib/oauth/code_response_spec.rb +1 -1
  169. data/spec/lib/oauth/error_response_spec.rb +0 -3
  170. data/spec/lib/oauth/error_spec.rb +0 -2
  171. data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
  172. data/spec/lib/oauth/helpers/scope_checker_spec.rb +8 -11
  173. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
  174. data/spec/lib/oauth/helpers/uri_checker_spec.rb +22 -13
  175. data/spec/lib/oauth/invalid_token_response_spec.rb +1 -4
  176. data/spec/lib/oauth/password_access_token_request_spec.rb +53 -6
  177. data/spec/lib/oauth/pre_authorization_spec.rb +33 -4
  178. data/spec/lib/oauth/refresh_token_request_spec.rb +22 -14
  179. data/spec/lib/oauth/scopes_spec.rb +0 -3
  180. data/spec/lib/oauth/token_request_spec.rb +8 -9
  181. data/spec/lib/oauth/token_response_spec.rb +0 -1
  182. data/spec/lib/oauth/token_spec.rb +40 -14
  183. data/spec/lib/request/strategy_spec.rb +0 -1
  184. data/spec/lib/server_spec.rb +7 -7
  185. data/spec/lib/stale_records_cleaner_spec.rb +89 -0
  186. data/spec/models/doorkeeper/access_grant_spec.rb +44 -1
  187. data/spec/models/doorkeeper/access_token_spec.rb +80 -32
  188. data/spec/models/doorkeeper/application_spec.rb +293 -221
  189. data/spec/requests/applications/applications_request_spec.rb +134 -1
  190. data/spec/requests/applications/authorized_applications_spec.rb +1 -1
  191. data/spec/requests/endpoints/authorization_spec.rb +3 -3
  192. data/spec/requests/endpoints/token_spec.rb +7 -5
  193. data/spec/requests/flows/authorization_code_errors_spec.rb +2 -2
  194. data/spec/requests/flows/authorization_code_spec.rb +258 -2
  195. data/spec/requests/flows/client_credentials_spec.rb +46 -6
  196. data/spec/requests/flows/implicit_grant_errors_spec.rb +3 -3
  197. data/spec/requests/flows/implicit_grant_spec.rb +38 -11
  198. data/spec/requests/flows/password_spec.rb +61 -3
  199. data/spec/requests/flows/refresh_token_spec.rb +59 -2
  200. data/spec/requests/flows/revoke_token_spec.rb +20 -20
  201. data/spec/requests/flows/skip_authorization_spec.rb +16 -11
  202. data/spec/requests/protected_resources/metal_spec.rb +1 -1
  203. data/spec/requests/protected_resources/private_api_spec.rb +3 -3
  204. data/spec/routing/custom_controller_routes_spec.rb +59 -7
  205. data/spec/routing/default_routes_spec.rb +2 -2
  206. data/spec/routing/scoped_routes_spec.rb +16 -2
  207. data/spec/spec_helper.rb +54 -3
  208. data/spec/spec_helper_integration.rb +2 -74
  209. data/spec/support/dependencies/{factory_girl.rb → factory_bot.rb} +0 -0
  210. data/spec/support/doorkeeper_rspec.rb +20 -0
  211. data/spec/support/helpers/authorization_request_helper.rb +4 -4
  212. data/spec/support/helpers/model_helper.rb +8 -4
  213. data/spec/support/helpers/request_spec_helper.rb +10 -2
  214. data/spec/support/helpers/url_helper.rb +18 -14
  215. data/spec/support/http_method_shim.rb +12 -16
  216. data/spec/support/shared/controllers_shared_context.rb +56 -0
  217. data/spec/validators/redirect_uri_validator_spec.rb +9 -3
  218. data/spec/version/version_spec.rb +3 -3
  219. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  220. metadata +54 -35
  221. data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +0 -31
  222. data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +0 -11
  223. data/spec/controllers/application_metal_controller.rb +0 -10
@@ -1,8 +1,9 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  feature 'Adding applications' do
4
4
  context 'in application form' do
5
5
  background do
6
+ i_am_logged_in
6
7
  visit '/oauth/applications/new'
7
8
  end
8
9
 
@@ -20,29 +21,123 @@ feature 'Adding applications' do
20
21
  click_button 'Submit'
21
22
  i_should_see 'Whoops! Check your form for possible errors'
22
23
  end
24
+
25
+ scenario "adding app ignoring bad scope" do
26
+ config_is_set("enforce_configured_scopes", false)
27
+
28
+ fill_in "doorkeeper_application[name]", with: "My Application"
29
+ fill_in "doorkeeper_application[redirect_uri]",
30
+ with: "https://example.com"
31
+ fill_in "doorkeeper_application[scopes]", with: "blahblah"
32
+
33
+ click_button "Submit"
34
+ i_should_see "Application created"
35
+ i_should_see "My Application"
36
+ end
37
+
38
+ scenario "adding app validating bad scope" do
39
+ config_is_set("enforce_configured_scopes", true)
40
+
41
+ fill_in "doorkeeper_application[name]", with: "My Application"
42
+ fill_in "doorkeeper_application[redirect_uri]",
43
+ with: "https://example.com"
44
+ fill_in "doorkeeper_application[scopes]", with: "blahblah"
45
+
46
+ click_button "Submit"
47
+ i_should_see "Whoops! Check your form for possible errors"
48
+ end
49
+
50
+ scenario "adding app validating scope, blank scope is accepted" do
51
+ config_is_set("enforce_configured_scopes", true)
52
+
53
+ fill_in "doorkeeper_application[name]", with: "My Application"
54
+ fill_in "doorkeeper_application[redirect_uri]",
55
+ with: "https://example.com"
56
+ fill_in "doorkeeper_application[scopes]", with: ""
57
+
58
+ click_button "Submit"
59
+ i_should_see "Application created"
60
+ i_should_see "My Application"
61
+ end
62
+
63
+ scenario "adding app validating scope, multiple scopes configured" do
64
+ config_is_set("enforce_configured_scopes", true)
65
+ scopes = Doorkeeper::OAuth::Scopes.from_array(%w[read write admin])
66
+ config_is_set("optional_scopes", scopes)
67
+
68
+ fill_in "doorkeeper_application[name]", with: "My Application"
69
+ fill_in "doorkeeper_application[redirect_uri]",
70
+ with: "https://example.com"
71
+ fill_in "doorkeeper_application[scopes]", with: "read write"
72
+
73
+ click_button "Submit"
74
+ i_should_see "Application created"
75
+ i_should_see "My Application"
76
+ end
77
+
78
+ scenario "adding app validating scope, bad scope with multiple scopes configured" do
79
+ config_is_set("enforce_configured_scopes", true)
80
+ scopes = Doorkeeper::OAuth::Scopes.from_array(%w[read write admin])
81
+ config_is_set("optional_scopes", scopes)
82
+
83
+ fill_in "doorkeeper_application[name]", with: "My Application"
84
+ fill_in "doorkeeper_application[redirect_uri]",
85
+ with: "https://example.com"
86
+ fill_in "doorkeeper_application[scopes]", with: "read blah"
87
+
88
+ click_button "Submit"
89
+ i_should_see "Whoops! Check your form for possible errors"
90
+ i_should_see Regexp.new(
91
+ I18n.t('activerecord.errors.models.doorkeeper/application.attributes.scopes.not_match_configured'),
92
+ true
93
+ )
94
+ end
23
95
  end
24
96
  end
25
97
 
26
98
  feature 'Listing applications' do
27
99
  background do
100
+ i_am_logged_in
101
+
28
102
  FactoryBot.create :application, name: 'Oauth Dude'
29
103
  FactoryBot.create :application, name: 'Awesome App'
30
104
  end
31
105
 
32
106
  scenario 'application list' do
33
107
  visit '/oauth/applications'
108
+
34
109
  i_should_see 'Awesome App'
35
110
  i_should_see 'Oauth Dude'
36
111
  end
37
112
  end
38
113
 
114
+ feature 'Renders assets' do
115
+ scenario 'admin stylesheets' do
116
+ visit '/assets/doorkeeper/admin/application.css'
117
+
118
+ i_should_see 'Bootstrap'
119
+ i_should_see '.doorkeeper-admin'
120
+ end
121
+
122
+ scenario 'application stylesheets' do
123
+ visit '/assets/doorkeeper/application.css'
124
+
125
+ i_should_see 'Bootstrap'
126
+ i_should_see '#oauth-permissions'
127
+ i_should_see '#container'
128
+ end
129
+ end
130
+
39
131
  feature 'Show application' do
40
132
  given :app do
133
+ i_am_logged_in
134
+
41
135
  FactoryBot.create :application, name: 'Just another oauth app'
42
136
  end
43
137
 
44
138
  scenario 'visiting application page' do
45
139
  visit "/oauth/applications/#{app.id}"
140
+
46
141
  i_should_see 'Just another oauth app'
47
142
  end
48
143
  end
@@ -53,12 +148,15 @@ feature 'Edit application' do
53
148
  end
54
149
 
55
150
  background do
151
+ i_am_logged_in
152
+
56
153
  visit "/oauth/applications/#{app.id}/edit"
57
154
  end
58
155
 
59
156
  scenario 'updating a valid app' do
60
157
  fill_in 'doorkeeper_application[name]', with: 'Serious app'
61
158
  click_button 'Submit'
159
+
62
160
  i_should_see 'Application updated'
63
161
  i_should_see 'Serious app'
64
162
  i_should_not_see 'OMG my app'
@@ -67,21 +165,27 @@ feature 'Edit application' do
67
165
  scenario 'updating an invalid app' do
68
166
  fill_in 'doorkeeper_application[name]', with: ''
69
167
  click_button 'Submit'
168
+
70
169
  i_should_see 'Whoops! Check your form for possible errors'
71
170
  end
72
171
  end
73
172
 
74
173
  feature 'Remove application' do
75
174
  background do
175
+ i_am_logged_in
176
+
76
177
  @app = FactoryBot.create :application
77
178
  end
78
179
 
79
180
  scenario 'deleting an application from list' do
80
181
  visit '/oauth/applications'
182
+
81
183
  i_should_see @app.name
184
+
82
185
  within(:css, "tr#application_#{@app.id}") do
83
186
  click_button 'Destroy'
84
187
  end
188
+
85
189
  i_should_see 'Application deleted'
86
190
  i_should_not_see @app.name
87
191
  end
@@ -89,6 +193,35 @@ feature 'Remove application' do
89
193
  scenario 'deleting an application from show' do
90
194
  visit "/oauth/applications/#{@app.id}"
91
195
  click_button 'Destroy'
196
+
92
197
  i_should_see 'Application deleted'
93
198
  end
94
199
  end
200
+
201
+ context 'when admin authenticator block is default' do
202
+ let(:app) { FactoryBot.create :application, name: 'app' }
203
+
204
+ feature 'application list' do
205
+ scenario 'fails with forbidden' do
206
+ visit '/oauth/applications'
207
+
208
+ should_have_status 403
209
+ end
210
+ end
211
+
212
+ feature 'adding an app' do
213
+ scenario 'fails with forbidden' do
214
+ visit '/oauth/applications/new'
215
+
216
+ should_have_status 403
217
+ end
218
+ end
219
+
220
+ feature 'editing an app' do
221
+ scenario 'fails with forbidden' do
222
+ visit "/oauth/applications/#{app.id}/edit"
223
+
224
+ should_have_status 403
225
+ end
226
+ end
227
+ end
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  feature 'Authorized applications' do
4
4
  background do
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  feature 'Authorization endpoint' do
4
4
  background do
@@ -60,11 +60,11 @@ feature 'Authorization endpoint' do
60
60
 
61
61
  scenario 'raises exception on forged requests' do
62
62
  allowing_forgery_protection do
63
- expect {
63
+ expect do
64
64
  page.driver.post authorization_endpoint_url(client_id: @client.uid,
65
65
  redirect_uri: @client.redirect_uri,
66
66
  response_type: 'code')
67
- }.to raise_error(ActionController::InvalidAuthenticityToken)
67
+ end.to raise_error(ActionController::InvalidAuthenticityToken)
68
68
  end
69
69
  end
70
70
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  describe 'Token endpoint' do
4
4
  before do
@@ -21,10 +21,12 @@ describe 'Token endpoint' do
21
21
  end
22
22
 
23
23
  it 'accepts client credentials with basic auth header' do
24
- post token_endpoint_url(
25
- code: @authorization.token,
26
- redirect_uri: @client.redirect_uri
27
- ), {}, 'HTTP_AUTHORIZATION' => basic_auth_header_for_client(@client)
24
+ post token_endpoint_url,
25
+ params: {
26
+ code: @authorization.token,
27
+ redirect_uri: @client.redirect_uri
28
+ },
29
+ headers: { 'HTTP_AUTHORIZATION' => basic_auth_header_for_client(@client) }
28
30
 
29
31
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
30
32
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  feature 'Authorization Code Flow Errors' do
4
4
  let(:client_params) { {} }
@@ -57,7 +57,7 @@ describe 'Authorization Code Flow Errors', 'after authorization' do
57
57
  # Second attempt with same token
58
58
  expect do
59
59
  post token_endpoint_url(code: @authorization.token, client: @client)
60
- end.to_not change { Doorkeeper::AccessToken.count }
60
+ end.to_not(change { Doorkeeper::AccessToken.count })
61
61
 
62
62
  should_not_have_json 'access_token'
63
63
  should_have_json 'error', 'invalid_grant'
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  feature 'Authorization Code Flow' do
4
4
  background do
@@ -39,6 +39,7 @@ feature 'Authorization Code Flow' do
39
39
  click_on 'Authorize'
40
40
  url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
41
41
  url_should_have_param('state', 'return-me')
42
+ url_should_not_have_param('code_challenge_method')
42
43
  end
43
44
 
44
45
  scenario 'resource owner requests an access token with authorization code' do
@@ -57,6 +58,259 @@ feature 'Authorization Code Flow' do
57
58
  should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
58
59
  end
59
60
 
61
+ scenario 'resource owner requests an access token with authorization code but without secret' do
62
+ visit authorization_endpoint_url(client: @client)
63
+ click_on 'Authorize'
64
+
65
+ authorization_code = Doorkeeper::AccessGrant.first.token
66
+ page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
67
+ redirect_uri: @client.redirect_uri)
68
+
69
+ expect(Doorkeeper::AccessToken.count).to be_zero
70
+
71
+ should_have_json 'error', 'invalid_client'
72
+ end
73
+
74
+ scenario 'silently authorizes if matching token exists' do
75
+ default_scopes_exist :public, :write
76
+
77
+ access_token_exists application: @client,
78
+ expires_in: -100, # even expired token
79
+ resource_owner_id: @resource_owner.id,
80
+ scopes: 'public write'
81
+
82
+ visit authorization_endpoint_url(client: @client, scope: 'public write')
83
+
84
+ response_status_should_be 200
85
+ i_should_not_see 'Authorize'
86
+ end
87
+
88
+ context 'with PKCE' do
89
+ context 'plain' do
90
+ let(:code_challenge) { 'a45a9fea-0676-477e-95b1-a40f72ac3cfb' }
91
+ let(:code_verifier) { 'a45a9fea-0676-477e-95b1-a40f72ac3cfb' }
92
+
93
+ scenario 'resource owner authorizes the client with code_challenge parameter set' do
94
+ visit authorization_endpoint_url(
95
+ client: @client,
96
+ code_challenge: code_challenge,
97
+ code_challenge_method: 'plain'
98
+ )
99
+ click_on 'Authorize'
100
+
101
+ url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
102
+ url_should_not_have_param('code_challenge_method')
103
+ url_should_not_have_param('code_challenge')
104
+ end
105
+
106
+ scenario 'mobile app requests an access token with authorization code but not pkce token' do
107
+ visit authorization_endpoint_url(client: @client)
108
+ click_on 'Authorize'
109
+
110
+ authorization_code = current_params['code']
111
+ create_access_token authorization_code, @client, code_verifier
112
+
113
+ should_have_json 'error', 'invalid_grant'
114
+ end
115
+
116
+ scenario 'mobile app requests an access token with authorization code and plain code challenge method' do
117
+ visit authorization_endpoint_url(
118
+ client: @client,
119
+ code_challenge: code_challenge,
120
+ code_challenge_method: 'plain'
121
+ )
122
+ click_on 'Authorize'
123
+
124
+ authorization_code = current_params['code']
125
+ create_access_token authorization_code, @client, code_verifier
126
+
127
+ access_token_should_exist_for(@client, @resource_owner)
128
+
129
+ should_not_have_json 'error'
130
+
131
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
132
+ should_have_json 'token_type', 'Bearer'
133
+ should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
134
+ end
135
+
136
+ scenario 'mobile app requests an access token with authorization code and code_challenge' do
137
+ visit authorization_endpoint_url(client: @client,
138
+ code_challenge: code_verifier,
139
+ code_challenge_method: 'plain')
140
+ click_on 'Authorize'
141
+
142
+ authorization_code = current_params['code']
143
+ create_access_token authorization_code, @client, code_verifier: nil
144
+
145
+ should_not_have_json 'access_token'
146
+ should_have_json 'error', 'invalid_grant'
147
+ end
148
+ end
149
+
150
+ context 's256' do
151
+ let(:code_challenge) { 'Oz733NtQ0rJP8b04fgZMJMwprn6Iw8sMCT_9bR1q4tA' }
152
+ let(:code_verifier) { 'a45a9fea-0676-477e-95b1-a40f72ac3cfb' }
153
+
154
+ scenario 'resource owner authorizes the client with code_challenge parameter set' do
155
+ visit authorization_endpoint_url(
156
+ client: @client,
157
+ code_challenge: code_challenge,
158
+ code_challenge_method: 'S256'
159
+ )
160
+ click_on 'Authorize'
161
+
162
+ url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
163
+ url_should_not_have_param('code_challenge_method')
164
+ url_should_not_have_param('code_challenge')
165
+ end
166
+
167
+ scenario 'mobile app requests an access token with authorization code and S256 code challenge method' do
168
+ visit authorization_endpoint_url(
169
+ client: @client,
170
+ code_challenge: code_challenge,
171
+ code_challenge_method: 'S256'
172
+ )
173
+ click_on 'Authorize'
174
+
175
+ authorization_code = current_params['code']
176
+ create_access_token authorization_code, @client, code_verifier
177
+
178
+ access_token_should_exist_for(@client, @resource_owner)
179
+
180
+ should_not_have_json 'error'
181
+
182
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
183
+ should_have_json 'token_type', 'Bearer'
184
+ should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
185
+ end
186
+
187
+ scenario 'mobile app requests an access token with authorization code and without code_verifier' do
188
+ visit authorization_endpoint_url(
189
+ client: @client,
190
+ code_challenge: code_challenge,
191
+ code_challenge_method: 'S256'
192
+ )
193
+ click_on 'Authorize'
194
+ authorization_code = current_params['code']
195
+ create_access_token authorization_code, @client
196
+ should_have_json 'error', 'invalid_request'
197
+ should_not_have_json 'access_token'
198
+ end
199
+
200
+ scenario 'mobile app requests an access token with authorization code and without secret' do
201
+ visit authorization_endpoint_url(
202
+ client: @client,
203
+ code_challenge: code_challenge,
204
+ code_challenge_method: 'S256'
205
+ )
206
+ click_on 'Authorize'
207
+
208
+ authorization_code = current_params['code']
209
+ page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
210
+ redirect_uri: @client.redirect_uri, code_verifier: code_verifier)
211
+ should_have_json 'error', 'invalid_client'
212
+ should_not_have_json 'access_token'
213
+ end
214
+
215
+ scenario 'mobile app requests an access token with authorization code and without secret but is marked as not confidential' do
216
+ @client.update_attribute :confidential, false
217
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
218
+ click_on 'Authorize'
219
+
220
+ authorization_code = current_params['code']
221
+ page.driver.post token_endpoint_url(
222
+ code: authorization_code,
223
+ client_id: @client.uid,
224
+ redirect_uri: @client.redirect_uri,
225
+ code_verifier: code_verifier
226
+ )
227
+ should_not_have_json 'error'
228
+
229
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
230
+ should_have_json 'token_type', 'Bearer'
231
+ should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
232
+ end
233
+
234
+ scenario 'mobile app requests an access token with authorization code but no code verifier' do
235
+ visit authorization_endpoint_url(
236
+ client: @client,
237
+ code_challenge: code_challenge,
238
+ code_challenge_method: 'S256'
239
+ )
240
+ click_on 'Authorize'
241
+
242
+ authorization_code = current_params['code']
243
+ create_access_token authorization_code, @client
244
+
245
+ should_not_have_json 'access_token'
246
+ should_have_json 'error', 'invalid_request'
247
+ end
248
+
249
+ scenario 'mobile app requests an access token with authorization code with wrong verifier' do
250
+ visit authorization_endpoint_url(
251
+ client: @client,
252
+ code_challenge: code_challenge,
253
+ code_challenge_method: 'S256'
254
+ )
255
+ click_on 'Authorize'
256
+
257
+ authorization_code = current_params['code']
258
+ create_access_token authorization_code, @client, 'incorrect-code-verifier'
259
+
260
+ should_not_have_json 'access_token'
261
+ should_have_json 'error', 'invalid_grant'
262
+ end
263
+
264
+ scenario 'code_challenge_mehthod in token request is totally ignored' do
265
+ visit authorization_endpoint_url(
266
+ client: @client,
267
+ code_challenge: code_challenge,
268
+ code_challenge_method: 'S256'
269
+ )
270
+ click_on 'Authorize'
271
+
272
+ authorization_code = current_params['code']
273
+ page.driver.post token_endpoint_url(
274
+ code: authorization_code,
275
+ client: @client,
276
+ code_verifier: code_challenge,
277
+ code_challenge_method: 'plain'
278
+ )
279
+
280
+ should_not_have_json 'access_token'
281
+ should_have_json 'error', 'invalid_grant'
282
+ end
283
+
284
+ scenario 'expects to set code_challenge_method explicitely without fallback' do
285
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge)
286
+ expect(page).to have_content('The code challenge method must be plain or S256.')
287
+ end
288
+ end
289
+ end
290
+
291
+ context 'when application scopes are present and no scope is passed' do
292
+ background do
293
+ @client.update_attributes(scopes: 'public write read')
294
+ end
295
+
296
+ scenario 'access grant has no scope' do
297
+ default_scopes_exist :admin
298
+ visit authorization_endpoint_url(client: @client)
299
+ click_on 'Authorize'
300
+ access_grant_should_exist_for(@client, @resource_owner)
301
+ grant = Doorkeeper::AccessGrant.first
302
+ expect(grant.scopes).to be_empty
303
+ end
304
+
305
+ scenario 'access grant have scopes which are common in application scopees and default scopes' do
306
+ default_scopes_exist :public, :write
307
+ visit authorization_endpoint_url(client: @client)
308
+ click_on 'Authorize'
309
+ access_grant_should_exist_for(@client, @resource_owner)
310
+ access_grant_should_have_scopes :public, :write
311
+ end
312
+ end
313
+
60
314
  context 'with scopes' do
61
315
  background do
62
316
  default_scopes_exist :public
@@ -128,6 +382,7 @@ describe 'Authorization Code Flow' do
128
382
  orm DOORKEEPER_ORM
129
383
  use_refresh_token
130
384
  end
385
+
131
386
  client_exists
132
387
  end
133
388
 
@@ -138,7 +393,8 @@ describe 'Authorization Code Flow' do
138
393
 
139
394
  it 'second of simultaneous client requests get an error for revoked acccess token' do
140
395
  authorization_code = Doorkeeper::AccessGrant.first.token
141
- allow_any_instance_of(Doorkeeper::AccessGrant).to receive(:revoked?).and_return(false, true)
396
+ allow_any_instance_of(Doorkeeper::AccessGrant)
397
+ .to receive(:revoked?).and_return(false, true)
142
398
 
143
399
  post token_endpoint_url(code: authorization_code, client: @client)
144
400
 
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  describe 'Client Credentials Request' do
4
4
  let(:client) { FactoryBot.create :application }
@@ -8,7 +8,7 @@ describe 'Client Credentials Request' do
8
8
  headers = authorization client.uid, client.secret
9
9
  params = { grant_type: 'client_credentials' }
10
10
 
11
- post '/oauth/token', params, headers
11
+ post '/oauth/token', params: params, headers: headers
12
12
 
13
13
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
14
14
  should_have_json_within 'expires_in', Doorkeeper.configuration.access_token_expires_in, 1
@@ -29,7 +29,7 @@ describe 'Client Credentials Request' do
29
29
  headers = authorization client.uid, client.secret
30
30
  params = { grant_type: 'client_credentials', scope: 'write' }
31
31
 
32
- post '/oauth/token', params, headers
32
+ post '/oauth/token', params: params, headers: headers
33
33
 
34
34
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
35
35
  should_have_json 'scope', 'write'
@@ -40,7 +40,7 @@ describe 'Client Credentials Request' do
40
40
  headers = authorization client.uid, client.secret
41
41
  params = { grant_type: 'client_credentials', scope: 'public' }
42
42
 
43
- post '/oauth/token', params, headers
43
+ post '/oauth/token', params: params, headers: headers
44
44
 
45
45
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
46
46
  should_have_json 'scope', 'public'
@@ -52,7 +52,7 @@ describe 'Client Credentials Request' do
52
52
  headers = authorization client.uid, client.secret
53
53
  params = { grant_type: 'client_credentials', scope: 'random' }
54
54
 
55
- post '/oauth/token', params, headers
55
+ post '/oauth/token', params: params, headers: headers
56
56
 
57
57
  should_have_json 'error', 'invalid_scope'
58
58
  should_have_json 'error_description', translated_error_message(:invalid_scope)
@@ -64,12 +64,52 @@ describe 'Client Credentials Request' do
64
64
  end
65
65
  end
66
66
 
67
+ context 'when application scopes contain some of the default scopes and no scope is passed' do
68
+ before do
69
+ client.update_attributes(scopes: 'read write public')
70
+ end
71
+
72
+ it 'issues new token with one default scope that are present in application scopes' do
73
+ default_scopes_exist :public
74
+
75
+ headers = authorization client.uid, client.secret
76
+ params = { grant_type: 'client_credentials' }
77
+
78
+ expect do
79
+ post '/oauth/token', params: params, headers: headers
80
+ end.to change { Doorkeeper::AccessToken.count }.by(1)
81
+
82
+ token = Doorkeeper::AccessToken.first
83
+
84
+ expect(token.application_id).to eq client.id
85
+ should_have_json 'access_token', token.token
86
+ should_have_json 'scope', 'public'
87
+ end
88
+
89
+ it 'issues new token with multiple default scopes that are present in application scopes' do
90
+ default_scopes_exist :public, :read, :update
91
+
92
+ headers = authorization client.uid, client.secret
93
+ params = { grant_type: 'client_credentials' }
94
+
95
+ expect do
96
+ post '/oauth/token', params: params, headers: headers
97
+ end.to change { Doorkeeper::AccessToken.count }.by(1)
98
+
99
+ token = Doorkeeper::AccessToken.first
100
+
101
+ expect(token.application_id).to eq client.id
102
+ should_have_json 'access_token', token.token
103
+ should_have_json 'scope', 'public read'
104
+ end
105
+ end
106
+
67
107
  context 'an invalid request' do
68
108
  it 'does not authorize the client and returns the error' do
69
109
  headers = {}
70
110
  params = { grant_type: 'client_credentials' }
71
111
 
72
- post '/oauth/token', params, headers
112
+ post '/oauth/token', params: params, headers: headers
73
113
 
74
114
  should_have_json 'error', 'invalid_client'
75
115
  should_have_json 'error_description', translated_error_message(:invalid_client)
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  feature 'Implicit Grant Flow Errors' do
4
4
  background do
@@ -14,8 +14,8 @@ feature 'Implicit Grant Flow Errors' do
14
14
  end
15
15
 
16
16
  [
17
- [:client_id, :invalid_client],
18
- [:redirect_uri, :invalid_redirect_uri]
17
+ %i[client_id invalid_client],
18
+ %i[redirect_uri invalid_redirect_uri]
19
19
  ].each do |error|
20
20
  scenario "displays #{error.last} error for invalid #{error.first}" do
21
21
  visit authorization_endpoint_url(client: @client, error.first => 'invalid', response_type: 'token')