doorkeeper 4.2.5 → 5.0.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 (215) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +25 -0
  3. data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
  4. data/.gitignore +2 -1
  5. data/.gitlab-ci.yml +16 -0
  6. data/.hound.yml +2 -13
  7. data/.rubocop.yml +17 -0
  8. data/.travis.yml +19 -4
  9. data/Appraisals +8 -4
  10. data/CODE_OF_CONDUCT.md +46 -0
  11. data/Gemfile +1 -1
  12. data/NEWS.md +115 -0
  13. data/README.md +175 -39
  14. data/RELEASING.md +5 -12
  15. data/Rakefile +6 -0
  16. data/SECURITY.md +15 -0
  17. data/UPGRADE.md +2 -0
  18. data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
  19. data/app/controllers/doorkeeper/application_controller.rb +2 -5
  20. data/app/controllers/doorkeeper/application_metal_controller.rb +4 -0
  21. data/app/controllers/doorkeeper/applications_controller.rb +47 -13
  22. data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
  23. data/app/controllers/doorkeeper/authorized_applications_controller.rb +19 -2
  24. data/app/controllers/doorkeeper/tokens_controller.rb +24 -17
  25. data/app/helpers/doorkeeper/dashboard_helper.rb +8 -6
  26. data/app/validators/redirect_uri_validator.rb +13 -2
  27. data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
  28. data/app/views/doorkeeper/applications/_form.html.erb +31 -19
  29. data/app/views/doorkeeper/applications/edit.html.erb +1 -1
  30. data/app/views/doorkeeper/applications/index.html.erb +18 -6
  31. data/app/views/doorkeeper/applications/new.html.erb +1 -1
  32. data/app/views/doorkeeper/applications/show.html.erb +8 -5
  33. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  34. data/app/views/doorkeeper/authorizations/new.html.erb +5 -1
  35. data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
  36. data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
  37. data/config/locales/en.yml +19 -6
  38. data/doorkeeper.gemspec +20 -20
  39. data/gemfiles/rails_4_2.gemfile +6 -4
  40. data/gemfiles/rails_5_0.gemfile +4 -4
  41. data/gemfiles/rails_5_1.gemfile +6 -7
  42. data/gemfiles/rails_5_2.gemfile +12 -0
  43. data/gemfiles/rails_master.gemfile +17 -0
  44. data/lib/doorkeeper/config.rb +123 -71
  45. data/lib/doorkeeper/engine.rb +7 -3
  46. data/lib/doorkeeper/errors.rb +20 -5
  47. data/lib/doorkeeper/grape/helpers.rb +14 -9
  48. data/lib/doorkeeper/helpers/controller.rb +16 -22
  49. data/lib/doorkeeper/models/access_grant_mixin.rb +67 -23
  50. data/lib/doorkeeper/models/access_token_mixin.rb +54 -55
  51. data/lib/doorkeeper/models/application_mixin.rb +16 -30
  52. data/lib/doorkeeper/models/concerns/expirable.rb +7 -5
  53. data/lib/doorkeeper/models/concerns/orderable.rb +13 -0
  54. data/lib/doorkeeper/models/concerns/scopes.rb +1 -1
  55. data/lib/doorkeeper/oauth/authorization/code.rb +31 -8
  56. data/lib/doorkeeper/oauth/authorization/context.rb +15 -0
  57. data/lib/doorkeeper/oauth/authorization/token.rb +46 -20
  58. data/lib/doorkeeper/oauth/authorization_code_request.rb +33 -3
  59. data/lib/doorkeeper/oauth/base_request.rb +23 -7
  60. data/lib/doorkeeper/oauth/client/credentials.rb +6 -4
  61. data/lib/doorkeeper/oauth/client.rb +2 -4
  62. data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -1
  63. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +6 -3
  64. data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -6
  65. data/lib/doorkeeper/oauth/client_credentials_request.rb +0 -4
  66. data/lib/doorkeeper/oauth/error.rb +2 -2
  67. data/lib/doorkeeper/oauth/error_response.rb +11 -4
  68. data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
  69. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +0 -8
  70. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +15 -0
  71. data/lib/doorkeeper/oauth/invalid_token_response.rb +3 -4
  72. data/lib/doorkeeper/oauth/password_access_token_request.rb +8 -4
  73. data/lib/doorkeeper/oauth/pre_authorization.rb +45 -13
  74. data/lib/doorkeeper/oauth/refresh_token_request.rb +7 -1
  75. data/lib/doorkeeper/oauth/scopes.rb +19 -9
  76. data/lib/doorkeeper/oauth/token.rb +6 -3
  77. data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
  78. data/lib/doorkeeper/oauth/token_response.rb +4 -2
  79. data/lib/doorkeeper/oauth.rb +13 -0
  80. data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -0
  81. data/lib/doorkeeper/orm/active_record/access_token.rb +21 -20
  82. data/lib/doorkeeper/orm/active_record/application.rb +45 -0
  83. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
  84. data/lib/doorkeeper/orm/active_record.rb +21 -8
  85. data/lib/doorkeeper/rails/helpers.rb +7 -10
  86. data/lib/doorkeeper/rails/routes.rb +21 -7
  87. data/lib/doorkeeper/rake/db.rake +40 -0
  88. data/lib/doorkeeper/rake/setup.rake +6 -0
  89. data/lib/doorkeeper/rake.rb +14 -0
  90. data/lib/doorkeeper/request/authorization_code.rb +0 -2
  91. data/lib/doorkeeper/request/client_credentials.rb +0 -2
  92. data/lib/doorkeeper/request/code.rb +0 -2
  93. data/lib/doorkeeper/request/password.rb +1 -13
  94. data/lib/doorkeeper/request/refresh_token.rb +0 -2
  95. data/lib/doorkeeper/request/token.rb +0 -2
  96. data/lib/doorkeeper/request.rb +29 -30
  97. data/lib/doorkeeper/validations.rb +3 -2
  98. data/lib/doorkeeper/version.rb +14 -1
  99. data/lib/doorkeeper.rb +21 -17
  100. data/lib/generators/doorkeeper/application_owner_generator.rb +26 -12
  101. data/lib/generators/doorkeeper/confidential_applications_generator.rb +32 -0
  102. data/lib/generators/doorkeeper/install_generator.rb +17 -9
  103. data/lib/generators/doorkeeper/migration_generator.rb +26 -9
  104. data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
  105. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +31 -20
  106. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  107. data/lib/generators/doorkeeper/templates/{add_owner_to_application_migration.rb → add_owner_to_application_migration.rb.erb} +1 -1
  108. data/lib/generators/doorkeeper/templates/{add_previous_refresh_token_to_access_tokens.rb → add_previous_refresh_token_to_access_tokens.rb.erb} +1 -1
  109. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
  110. data/lib/generators/doorkeeper/templates/initializer.rb +104 -12
  111. data/lib/generators/doorkeeper/templates/{migration.rb → migration.rb.erb} +2 -1
  112. data/lib/generators/doorkeeper/views_generator.rb +3 -1
  113. data/spec/controllers/application_metal_controller_spec.rb +50 -0
  114. data/spec/controllers/applications_controller_spec.rb +141 -17
  115. data/spec/controllers/authorizations_controller_spec.rb +304 -27
  116. data/spec/controllers/protected_resources_controller_spec.rb +44 -35
  117. data/spec/controllers/token_info_controller_spec.rb +17 -21
  118. data/spec/controllers/tokens_controller_spec.rb +201 -17
  119. data/spec/dummy/app/assets/config/manifest.js +2 -0
  120. data/spec/dummy/config/environments/test.rb +4 -5
  121. data/spec/dummy/config/initializers/doorkeeper.rb +23 -2
  122. data/spec/dummy/config/initializers/{active_record_belongs_to_required_by_default.rb → new_framework_defaults.rb} +5 -1
  123. data/spec/dummy/config/initializers/secret_token.rb +0 -1
  124. data/spec/dummy/config/routes.rb +3 -42
  125. data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
  126. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
  127. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -1
  128. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +3 -1
  129. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +3 -1
  130. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
  131. data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +13 -0
  132. data/spec/dummy/db/schema.rb +38 -37
  133. data/spec/factories.rb +1 -1
  134. data/spec/generators/application_owner_generator_spec.rb +25 -6
  135. data/spec/generators/confidential_applications_generator_spec.rb +45 -0
  136. data/spec/generators/install_generator_spec.rb +1 -1
  137. data/spec/generators/migration_generator_spec.rb +25 -4
  138. data/spec/generators/pkce_generator_spec.rb +43 -0
  139. data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
  140. data/spec/generators/views_generator_spec.rb +1 -1
  141. data/spec/grape/grape_integration_spec.rb +135 -0
  142. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +2 -2
  143. data/spec/lib/config_spec.rb +199 -22
  144. data/spec/lib/doorkeeper_spec.rb +1 -126
  145. data/spec/lib/models/expirable_spec.rb +0 -4
  146. data/spec/lib/models/revocable_spec.rb +2 -4
  147. data/spec/lib/models/scopes_spec.rb +0 -4
  148. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -4
  149. data/spec/lib/oauth/authorization_code_request_spec.rb +63 -13
  150. data/spec/lib/oauth/base_request_spec.rb +43 -10
  151. data/spec/lib/oauth/base_response_spec.rb +1 -1
  152. data/spec/lib/oauth/client/credentials_spec.rb +5 -5
  153. data/spec/lib/oauth/client_credentials/creator_spec.rb +6 -2
  154. data/spec/lib/oauth/client_credentials/issuer_spec.rb +26 -7
  155. data/spec/lib/oauth/client_credentials/validation_spec.rb +2 -3
  156. data/spec/lib/oauth/client_credentials_integration_spec.rb +2 -2
  157. data/spec/lib/oauth/client_credentials_request_spec.rb +4 -5
  158. data/spec/lib/oauth/client_spec.rb +0 -3
  159. data/spec/lib/oauth/code_request_spec.rb +5 -5
  160. data/spec/lib/oauth/error_response_spec.rb +0 -3
  161. data/spec/lib/oauth/error_spec.rb +1 -3
  162. data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
  163. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -3
  164. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
  165. data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -3
  166. data/spec/lib/oauth/invalid_token_response_spec.rb +2 -5
  167. data/spec/lib/oauth/password_access_token_request_spec.rb +46 -5
  168. data/spec/lib/oauth/pre_authorization_spec.rb +40 -6
  169. data/spec/lib/oauth/refresh_token_request_spec.rb +30 -14
  170. data/spec/lib/oauth/scopes_spec.rb +28 -4
  171. data/spec/lib/oauth/token_request_spec.rb +10 -13
  172. data/spec/lib/oauth/token_response_spec.rb +0 -1
  173. data/spec/lib/oauth/token_spec.rb +37 -14
  174. data/spec/lib/orm/active_record/stale_records_cleaner_spec.rb +79 -0
  175. data/spec/lib/request/strategy_spec.rb +0 -1
  176. data/spec/lib/server_spec.rb +10 -0
  177. data/spec/models/doorkeeper/access_grant_spec.rb +45 -2
  178. data/spec/models/doorkeeper/access_token_spec.rb +132 -64
  179. data/spec/models/doorkeeper/application_spec.rb +114 -23
  180. data/spec/requests/applications/applications_request_spec.rb +139 -6
  181. data/spec/requests/applications/authorized_applications_spec.rb +1 -1
  182. data/spec/requests/endpoints/authorization_spec.rb +1 -1
  183. data/spec/requests/endpoints/token_spec.rb +15 -6
  184. data/spec/requests/flows/authorization_code_errors_spec.rb +12 -2
  185. data/spec/requests/flows/authorization_code_spec.rb +199 -2
  186. data/spec/requests/flows/client_credentials_spec.rb +73 -5
  187. data/spec/requests/flows/implicit_grant_errors_spec.rb +3 -3
  188. data/spec/requests/flows/implicit_grant_spec.rb +38 -11
  189. data/spec/requests/flows/password_spec.rb +160 -24
  190. data/spec/requests/flows/refresh_token_spec.rb +6 -6
  191. data/spec/requests/flows/revoke_token_spec.rb +26 -26
  192. data/spec/requests/flows/skip_authorization_spec.rb +16 -11
  193. data/spec/requests/protected_resources/metal_spec.rb +2 -2
  194. data/spec/requests/protected_resources/private_api_spec.rb +2 -2
  195. data/spec/routing/custom_controller_routes_spec.rb +63 -7
  196. data/spec/routing/default_routes_spec.rb +6 -2
  197. data/spec/routing/scoped_routes_spec.rb +16 -2
  198. data/spec/spec_helper.rb +54 -3
  199. data/spec/spec_helper_integration.rb +2 -64
  200. data/spec/support/dependencies/factory_bot.rb +2 -0
  201. data/spec/support/doorkeeper_rspec.rb +19 -0
  202. data/spec/support/helpers/access_token_request_helper.rb +1 -1
  203. data/spec/support/helpers/authorization_request_helper.rb +4 -4
  204. data/spec/support/helpers/model_helper.rb +9 -4
  205. data/spec/support/helpers/request_spec_helper.rb +18 -6
  206. data/spec/support/helpers/url_helper.rb +15 -10
  207. data/spec/support/http_method_shim.rb +12 -16
  208. data/spec/support/shared/controllers_shared_context.rb +2 -6
  209. data/spec/support/shared/models_shared_examples.rb +4 -4
  210. data/spec/validators/redirect_uri_validator_spec.rb +58 -7
  211. data/spec/version/version_spec.rb +15 -0
  212. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  213. metadata +75 -33
  214. data/spec/controllers/application_metal_controller.rb +0 -10
  215. data/spec/support/dependencies/factory_girl.rb +0 -2
@@ -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,45 +21,142 @@ 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
28
- FactoryGirl.create :application, name: 'Oauth Dude'
29
- FactoryGirl.create :application, name: 'Awesome App'
100
+ i_am_logged_in
101
+
102
+ FactoryBot.create :application, name: 'Oauth Dude'
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
41
- FactoryGirl.create :application, name: 'Just another oauth app'
133
+ i_am_logged_in
134
+
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
49
144
 
50
145
  feature 'Edit application' do
51
146
  let :app do
52
- FactoryGirl.create :application, name: 'OMG my app'
147
+ FactoryBot.create :application, name: 'OMG my app'
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
76
- @app = FactoryGirl.create :application
175
+ i_am_logged_in
176
+
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
@@ -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
@@ -9,15 +9,24 @@ describe 'Token endpoint' do
9
9
  it 'respond with correct headers' do
10
10
  post token_endpoint_url(code: @authorization.token, client: @client)
11
11
  should_have_header 'Pragma', 'no-cache'
12
- should_have_header 'Cache-Control', 'no-store'
12
+
13
+ # Rails 5.2 changed headers
14
+ if ::Rails::VERSION::MAJOR >= 5 && ::Rails::VERSION::MINOR >= 2 || ::Rails::VERSION::MAJOR >= 6
15
+ should_have_header 'Cache-Control', 'private, no-store'
16
+ else
17
+ should_have_header 'Cache-Control', 'no-store'
18
+ end
19
+
13
20
  should_have_header 'Content-Type', 'application/json; charset=utf-8'
14
21
  end
15
22
 
16
23
  it 'accepts client credentials with basic auth header' do
17
- post token_endpoint_url(
18
- code: @authorization.token,
19
- redirect_uri: @client.redirect_uri
20
- ), {}, '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) }
21
30
 
22
31
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
23
32
  end
@@ -1,9 +1,10 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  feature 'Authorization Code Flow Errors' do
4
+ let(:client_params) { {} }
4
5
  background do
5
6
  config_is_set(:authenticate_resource_owner) { User.first || redirect_to('/sign_in') }
6
- client_exists
7
+ client_exists client_params
7
8
  create_resource_owner
8
9
  sign_in
9
10
  end
@@ -12,6 +13,15 @@ feature 'Authorization Code Flow Errors' do
12
13
  access_grant_should_not_exist
13
14
  end
14
15
 
16
+ context "with a client trying to xss resource owner" do
17
+ let(:client_name) { "<div id='xss'>XSS</div>" }
18
+ let(:client_params) { { name: client_name } }
19
+ scenario "resource owner visit authorization endpoint" do
20
+ visit authorization_endpoint_url(client: @client)
21
+ expect(page).not_to have_css("#xss")
22
+ end
23
+ end
24
+
15
25
  context 'when access was denied' do
16
26
  scenario 'redirects with error' do
17
27
  visit authorization_endpoint_url(client: @client)
@@ -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
@@ -29,6 +29,7 @@ feature 'Authorization Code Flow' do
29
29
 
30
30
  access_grant_should_exist_for(@client, @resource_owner)
31
31
 
32
+ url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
32
33
  i_should_see 'Authorization code:'
33
34
  i_should_see Doorkeeper::AccessGrant.first.token
34
35
  end
@@ -38,6 +39,7 @@ feature 'Authorization Code Flow' do
38
39
  click_on 'Authorize'
39
40
  url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
40
41
  url_should_have_param('state', 'return-me')
42
+ url_should_not_have_param('code_challenge_method')
41
43
  end
42
44
 
43
45
  scenario 'resource owner requests an access token with authorization code' do
@@ -52,10 +54,205 @@ feature 'Authorization Code Flow' do
52
54
  should_not_have_json 'error'
53
55
 
54
56
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
55
- should_have_json 'token_type', 'bearer'
57
+ should_have_json 'token_type', 'Bearer'
56
58
  should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
57
59
  end
58
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).not_to exist
70
+
71
+ should_have_json 'error', 'invalid_client'
72
+ end
73
+
74
+ context 'with PKCE' do
75
+ context 'plain' do
76
+ let(:code_challenge) { 'a45a9fea-0676-477e-95b1-a40f72ac3cfb' }
77
+ let(:code_verifier) { 'a45a9fea-0676-477e-95b1-a40f72ac3cfb' }
78
+
79
+ scenario 'resource owner authorizes the client with code_challenge parameter set' do
80
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'plain')
81
+ click_on 'Authorize'
82
+
83
+ url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
84
+ url_should_not_have_param('code_challenge_method')
85
+ url_should_not_have_param('code_challenge')
86
+ end
87
+
88
+ scenario 'mobile app requests an access token with authorization code but not pkce token' do
89
+ visit authorization_endpoint_url(client: @client)
90
+ click_on 'Authorize'
91
+
92
+ authorization_code = current_params['code']
93
+ create_access_token authorization_code, @client, code_verifier
94
+
95
+ should_have_json 'error', 'invalid_grant'
96
+ end
97
+
98
+ scenario 'mobile app requests an access token with authorization code and plain code challenge method' do
99
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'plain')
100
+ click_on 'Authorize'
101
+
102
+ authorization_code = current_params['code']
103
+ create_access_token authorization_code, @client, code_verifier
104
+
105
+ access_token_should_exist_for(@client, @resource_owner)
106
+
107
+ should_not_have_json 'error'
108
+
109
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
110
+ should_have_json 'token_type', 'Bearer'
111
+ should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
112
+ end
113
+
114
+ scenario 'mobile app requests an access token with authorization code and code_challenge' do
115
+ visit authorization_endpoint_url(client: @client,
116
+ code_challenge: code_verifier,
117
+ code_challenge_method: 'plain')
118
+ click_on 'Authorize'
119
+
120
+ authorization_code = current_params['code']
121
+ create_access_token authorization_code, @client, code_verifier: nil
122
+
123
+ should_not_have_json 'access_token'
124
+ should_have_json 'error', 'invalid_grant'
125
+ end
126
+ end
127
+
128
+ context 's256' do
129
+ let(:code_challenge) { 'Oz733NtQ0rJP8b04fgZMJMwprn6Iw8sMCT_9bR1q4tA' }
130
+ let(:code_verifier) { 'a45a9fea-0676-477e-95b1-a40f72ac3cfb' }
131
+
132
+ scenario 'resource owner authorizes the client with code_challenge parameter set' do
133
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
134
+ click_on 'Authorize'
135
+
136
+ url_should_have_param('code', Doorkeeper::AccessGrant.first.token)
137
+ url_should_not_have_param('code_challenge_method')
138
+ url_should_not_have_param('code_challenge')
139
+ end
140
+
141
+ scenario 'mobile app requests an access token with authorization code and S256 code challenge method' do
142
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
143
+ click_on 'Authorize'
144
+
145
+ authorization_code = current_params['code']
146
+ create_access_token authorization_code, @client, code_verifier
147
+
148
+ access_token_should_exist_for(@client, @resource_owner)
149
+
150
+ should_not_have_json 'error'
151
+
152
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
153
+ should_have_json 'token_type', 'Bearer'
154
+ should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
155
+ end
156
+
157
+ scenario 'mobile app requests an access token with authorization code and without code_verifier' do
158
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
159
+ click_on 'Authorize'
160
+ authorization_code = current_params['code']
161
+ create_access_token authorization_code, @client
162
+ should_have_json 'error', 'invalid_request'
163
+ should_not_have_json 'access_token'
164
+ end
165
+
166
+ scenario 'mobile app requests an access token with authorization code and without secret' do
167
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
168
+ click_on 'Authorize'
169
+
170
+ authorization_code = current_params['code']
171
+ page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
172
+ redirect_uri: @client.redirect_uri, code_verifier: code_verifier)
173
+ should_have_json 'error', 'invalid_client'
174
+ should_not_have_json 'access_token'
175
+ end
176
+
177
+ scenario 'mobile app requests an access token with authorization code and without secret but is marked as not confidential' do
178
+ @client.update_attribute :confidential, false
179
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
180
+ click_on 'Authorize'
181
+
182
+ authorization_code = current_params['code']
183
+ page.driver.post token_endpoint_url(code: authorization_code, client_id: @client.uid,
184
+ redirect_uri: @client.redirect_uri, code_verifier: code_verifier)
185
+ should_not_have_json 'error'
186
+
187
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
188
+ should_have_json 'token_type', 'Bearer'
189
+ should_have_json_within 'expires_in', Doorkeeper::AccessToken.first.expires_in, 1
190
+ end
191
+
192
+ scenario 'mobile app requests an access token with authorization code but no code verifier' do
193
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
194
+ click_on 'Authorize'
195
+
196
+ authorization_code = current_params['code']
197
+ create_access_token authorization_code, @client
198
+
199
+ should_not_have_json 'access_token'
200
+ should_have_json 'error', 'invalid_request'
201
+ end
202
+
203
+ scenario 'mobile app requests an access token with authorization code with wrong verifier' do
204
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
205
+ click_on 'Authorize'
206
+
207
+ authorization_code = current_params['code']
208
+ create_access_token authorization_code, @client, 'incorrect-code-verifier'
209
+
210
+ should_not_have_json 'access_token'
211
+ should_have_json 'error', 'invalid_grant'
212
+ end
213
+
214
+ scenario 'code_challenge_mehthod in token request is totally ignored' do
215
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge, code_challenge_method: 'S256')
216
+ click_on 'Authorize'
217
+
218
+ authorization_code = current_params['code']
219
+ page.driver.post token_endpoint_url(code: authorization_code, client: @client, code_verifier: code_challenge,
220
+ code_challenge_method: 'plain')
221
+
222
+ should_not_have_json 'access_token'
223
+ should_have_json 'error', 'invalid_grant'
224
+ end
225
+
226
+ scenario 'expects to set code_challenge_method explicitely without fallback' do
227
+ visit authorization_endpoint_url(client: @client, code_challenge: code_challenge)
228
+ expect(page).to have_content('The code challenge method must be plain or S256.')
229
+ end
230
+ end
231
+ end
232
+
233
+ context 'when application scopes are present and no scope is passed' do
234
+ background do
235
+ @client.update_attributes(scopes: 'public write read')
236
+ end
237
+
238
+ scenario 'access grant has no scope' do
239
+ default_scopes_exist :admin
240
+ visit authorization_endpoint_url(client: @client)
241
+ click_on 'Authorize'
242
+ access_grant_should_exist_for(@client, @resource_owner)
243
+ grant = Doorkeeper::AccessGrant.first
244
+ expect(grant.scopes).to be_empty
245
+ end
246
+
247
+ scenario 'access grant have scopes which are common in application scopees and default scopes' do
248
+ default_scopes_exist :public, :write
249
+ visit authorization_endpoint_url(client: @client)
250
+ click_on 'Authorize'
251
+ access_grant_should_exist_for(@client, @resource_owner)
252
+ access_grant_should_have_scopes :public, :write
253
+ end
254
+ end
255
+
59
256
  context 'with scopes' do
60
257
  background do
61
258
  default_scopes_exist :public
@@ -1,14 +1,14 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  describe 'Client Credentials Request' do
4
- let(:client) { FactoryGirl.create :application }
4
+ let(:client) { FactoryBot.create :application }
5
5
 
6
6
  context 'a valid request' do
7
7
  it 'authorizes the client and returns the token response' 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
@@ -22,17 +22,85 @@ describe 'Client Credentials Request' do
22
22
  context 'with scopes' do
23
23
  before do
24
24
  optional_scopes_exist :write
25
+ default_scopes_exist :public
25
26
  end
26
27
 
27
28
  it 'adds the scope to the token an returns in the response' do
28
29
  headers = authorization client.uid, client.secret
29
30
  params = { grant_type: 'client_credentials', scope: 'write' }
30
31
 
31
- post '/oauth/token', params, headers
32
+ post '/oauth/token', params: params, headers: headers
32
33
 
33
34
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
34
35
  should_have_json 'scope', 'write'
35
36
  end
37
+
38
+ context 'that are default' do
39
+ it 'adds the scope to the token an returns in the response' do
40
+ headers = authorization client.uid, client.secret
41
+ params = { grant_type: 'client_credentials', scope: 'public' }
42
+
43
+ post '/oauth/token', params: params, headers: headers
44
+
45
+ should_have_json 'access_token', Doorkeeper::AccessToken.first.token
46
+ should_have_json 'scope', 'public'
47
+ end
48
+ end
49
+
50
+ context 'that are invalid' do
51
+ it 'does not authorize the client and returns the error' do
52
+ headers = authorization client.uid, client.secret
53
+ params = { grant_type: 'client_credentials', scope: 'random' }
54
+
55
+ post '/oauth/token', params: params, headers: headers
56
+
57
+ should_have_json 'error', 'invalid_scope'
58
+ should_have_json 'error_description', translated_error_message(:invalid_scope)
59
+ should_not_have_json 'access_token'
60
+
61
+ expect(response.status).to eq(401)
62
+ end
63
+ end
64
+ end
65
+ end
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'
36
104
  end
37
105
  end
38
106
 
@@ -41,7 +109,7 @@ describe 'Client Credentials Request' do
41
109
  headers = {}
42
110
  params = { grant_type: 'client_credentials' }
43
111
 
44
- post '/oauth/token', params, headers
112
+ post '/oauth/token', params: params, headers: headers
45
113
 
46
114
  should_have_json 'error', 'invalid_client'
47
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
@@ -17,13 +17,13 @@ feature 'Implicit Grant Flow Errors' do
17
17
  [:client_id, :invalid_client],
18
18
  [:redirect_uri, :invalid_redirect_uri]
19
19
  ].each do |error|
20
- scenario "displays #{error.last.inspect} error for invalid #{error.first.inspect}" do
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')
22
22
  i_should_not_see 'Authorize'
23
23
  i_should_see_translated_error_message error.last
24
24
  end
25
25
 
26
- scenario "displays #{error.last.inspect} error when #{error.first.inspect} is missing" do
26
+ scenario "displays #{error.last} error when #{error.first} is missing" do
27
27
  visit authorization_endpoint_url(client: @client, error.first => '', response_type: 'token')
28
28
  i_should_not_see 'Authorize'
29
29
  i_should_see_translated_error_message error.last