doorkeeper 4.4.2 → 5.0.0.rc1

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 (169) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +2 -0
  4. data/Appraisals +2 -2
  5. data/Gemfile +1 -1
  6. data/NEWS.md +36 -12
  7. data/README.md +85 -3
  8. data/Rakefile +6 -0
  9. data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
  10. data/app/controllers/doorkeeper/application_controller.rb +4 -3
  11. data/app/controllers/doorkeeper/application_metal_controller.rb +4 -0
  12. data/app/controllers/doorkeeper/applications_controller.rb +42 -22
  13. data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
  14. data/app/controllers/doorkeeper/authorized_applications_controller.rb +15 -1
  15. data/app/controllers/doorkeeper/tokens_controller.rb +12 -15
  16. data/app/helpers/doorkeeper/dashboard_helper.rb +7 -7
  17. data/app/validators/redirect_uri_validator.rb +3 -2
  18. data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
  19. data/app/views/doorkeeper/applications/_form.html.erb +25 -24
  20. data/app/views/doorkeeper/applications/edit.html.erb +1 -1
  21. data/app/views/doorkeeper/applications/index.html.erb +17 -7
  22. data/app/views/doorkeeper/applications/new.html.erb +1 -1
  23. data/app/views/doorkeeper/applications/show.html.erb +6 -6
  24. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  25. data/app/views/doorkeeper/authorizations/new.html.erb +4 -0
  26. data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
  27. data/config/locales/en.yml +9 -1
  28. data/doorkeeper.gemspec +0 -2
  29. data/gemfiles/rails_5_2.gemfile +1 -1
  30. data/lib/doorkeeper/config.rb +60 -23
  31. data/lib/doorkeeper/engine.rb +4 -0
  32. data/lib/doorkeeper/errors.rb +2 -5
  33. data/lib/doorkeeper/grape/helpers.rb +1 -1
  34. data/lib/doorkeeper/helpers/controller.rb +7 -2
  35. data/lib/doorkeeper/models/access_grant_mixin.rb +56 -0
  36. data/lib/doorkeeper/models/access_token_mixin.rb +38 -21
  37. data/lib/doorkeeper/models/concerns/scopes.rb +1 -1
  38. data/lib/doorkeeper/oauth/authorization/code.rb +31 -8
  39. data/lib/doorkeeper/oauth/authorization/context.rb +15 -0
  40. data/lib/doorkeeper/oauth/authorization/token.rb +23 -6
  41. data/lib/doorkeeper/oauth/authorization_code_request.rb +27 -2
  42. data/lib/doorkeeper/oauth/base_request.rb +18 -8
  43. data/lib/doorkeeper/oauth/client/credentials.rb +1 -1
  44. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +6 -1
  45. data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -2
  46. data/lib/doorkeeper/oauth/error_response.rb +11 -3
  47. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +0 -8
  48. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -4
  49. data/lib/doorkeeper/oauth/pre_authorization.rb +41 -11
  50. data/lib/doorkeeper/oauth/refresh_token_request.rb +6 -1
  51. data/lib/doorkeeper/oauth/scopes.rb +1 -1
  52. data/lib/doorkeeper/oauth/token.rb +5 -2
  53. data/lib/doorkeeper/oauth/token_introspection.rb +2 -2
  54. data/lib/doorkeeper/oauth/token_response.rb +4 -2
  55. data/lib/doorkeeper/oauth.rb +13 -0
  56. data/lib/doorkeeper/orm/active_record/application.rb +13 -16
  57. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
  58. data/lib/doorkeeper/orm/active_record.rb +2 -0
  59. data/lib/doorkeeper/rails/helpers.rb +2 -4
  60. data/lib/doorkeeper/rails/routes.rb +13 -1
  61. data/lib/doorkeeper/rake/db.rake +40 -0
  62. data/lib/doorkeeper/rake/setup.rake +6 -0
  63. data/lib/doorkeeper/rake.rb +14 -0
  64. data/lib/doorkeeper/request.rb +28 -28
  65. data/lib/doorkeeper/version.rb +5 -25
  66. data/lib/doorkeeper.rb +4 -17
  67. data/lib/generators/doorkeeper/application_owner_generator.rb +23 -18
  68. data/lib/generators/doorkeeper/confidential_applications_generator.rb +32 -0
  69. data/lib/generators/doorkeeper/install_generator.rb +17 -9
  70. data/lib/generators/doorkeeper/migration_generator.rb +23 -18
  71. data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
  72. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -24
  73. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  74. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
  75. data/lib/generators/doorkeeper/templates/initializer.rb +60 -9
  76. data/lib/generators/doorkeeper/views_generator.rb +3 -1
  77. data/spec/controllers/application_metal_controller_spec.rb +50 -0
  78. data/spec/controllers/applications_controller_spec.rb +126 -13
  79. data/spec/controllers/authorizations_controller_spec.rb +252 -17
  80. data/spec/controllers/protected_resources_controller_spec.rb +16 -16
  81. data/spec/controllers/token_info_controller_spec.rb +4 -12
  82. data/spec/controllers/tokens_controller_spec.rb +19 -73
  83. data/spec/dummy/app/assets/config/manifest.js +2 -0
  84. data/spec/dummy/config/environments/test.rb +4 -5
  85. data/spec/dummy/config/initializers/doorkeeper.rb +6 -0
  86. data/spec/dummy/config/initializers/new_framework_defaults.rb +4 -0
  87. data/spec/dummy/config/routes.rb +3 -42
  88. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
  89. data/spec/dummy/db/migrate/{20180210183654_add_confidential_to_application.rb → 20180210183654_add_confidential_to_applications.rb} +1 -1
  90. data/spec/dummy/db/schema.rb +36 -36
  91. data/spec/generators/application_owner_generator_spec.rb +1 -1
  92. data/spec/generators/confidential_applications_generator_spec.rb +45 -0
  93. data/spec/generators/install_generator_spec.rb +1 -1
  94. data/spec/generators/migration_generator_spec.rb +1 -1
  95. data/spec/generators/pkce_generator_spec.rb +43 -0
  96. data/spec/generators/previous_refresh_token_generator_spec.rb +1 -1
  97. data/spec/generators/views_generator_spec.rb +1 -1
  98. data/spec/grape/grape_integration_spec.rb +1 -1
  99. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  100. data/spec/lib/config_spec.rb +59 -14
  101. data/spec/lib/doorkeeper_spec.rb +1 -126
  102. data/spec/lib/models/expirable_spec.rb +0 -3
  103. data/spec/lib/models/revocable_spec.rb +0 -2
  104. data/spec/lib/models/scopes_spec.rb +0 -4
  105. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -4
  106. data/spec/lib/oauth/authorization_code_request_spec.rb +9 -2
  107. data/spec/lib/oauth/base_request_spec.rb +16 -2
  108. data/spec/lib/oauth/base_response_spec.rb +1 -1
  109. data/spec/lib/oauth/client/credentials_spec.rb +1 -3
  110. data/spec/lib/oauth/client_credentials/creator_spec.rb +5 -1
  111. data/spec/lib/oauth/client_credentials/issuer_spec.rb +26 -7
  112. data/spec/lib/oauth/client_credentials/validation_spec.rb +2 -3
  113. data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
  114. data/spec/lib/oauth/client_credentials_request_spec.rb +3 -5
  115. data/spec/lib/oauth/client_spec.rb +0 -3
  116. data/spec/lib/oauth/code_request_spec.rb +4 -2
  117. data/spec/lib/oauth/error_response_spec.rb +0 -3
  118. data/spec/lib/oauth/error_spec.rb +0 -2
  119. data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
  120. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -3
  121. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
  122. data/spec/lib/oauth/helpers/uri_checker_spec.rb +5 -7
  123. data/spec/lib/oauth/invalid_token_response_spec.rb +1 -4
  124. data/spec/lib/oauth/password_access_token_request_spec.rb +37 -2
  125. data/spec/lib/oauth/pre_authorization_spec.rb +33 -4
  126. data/spec/lib/oauth/refresh_token_request_spec.rb +11 -7
  127. data/spec/lib/oauth/scopes_spec.rb +0 -3
  128. data/spec/lib/oauth/token_request_spec.rb +4 -5
  129. data/spec/lib/oauth/token_response_spec.rb +0 -1
  130. data/spec/lib/oauth/token_spec.rb +37 -14
  131. data/spec/lib/orm/active_record/stale_records_cleaner_spec.rb +79 -0
  132. data/spec/lib/request/strategy_spec.rb +0 -1
  133. data/spec/lib/server_spec.rb +1 -1
  134. data/spec/models/doorkeeper/access_grant_spec.rb +1 -1
  135. data/spec/models/doorkeeper/access_token_spec.rb +50 -16
  136. data/spec/models/doorkeeper/application_spec.rb +1 -47
  137. data/spec/requests/applications/applications_request_spec.rb +89 -1
  138. data/spec/requests/applications/authorized_applications_spec.rb +1 -1
  139. data/spec/requests/endpoints/authorization_spec.rb +1 -1
  140. data/spec/requests/endpoints/token_spec.rb +7 -5
  141. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -1
  142. data/spec/requests/flows/authorization_code_spec.rb +198 -2
  143. data/spec/requests/flows/client_credentials_spec.rb +46 -6
  144. data/spec/requests/flows/implicit_grant_errors_spec.rb +1 -1
  145. data/spec/requests/flows/implicit_grant_spec.rb +38 -11
  146. data/spec/requests/flows/password_spec.rb +56 -2
  147. data/spec/requests/flows/refresh_token_spec.rb +2 -2
  148. data/spec/requests/flows/revoke_token_spec.rb +11 -11
  149. data/spec/requests/flows/skip_authorization_spec.rb +16 -11
  150. data/spec/requests/protected_resources/metal_spec.rb +1 -1
  151. data/spec/requests/protected_resources/private_api_spec.rb +1 -1
  152. data/spec/routing/custom_controller_routes_spec.rb +59 -7
  153. data/spec/routing/default_routes_spec.rb +2 -2
  154. data/spec/routing/scoped_routes_spec.rb +16 -2
  155. data/spec/spec_helper.rb +54 -3
  156. data/spec/spec_helper_integration.rb +2 -74
  157. data/spec/support/doorkeeper_rspec.rb +19 -0
  158. data/spec/support/helpers/authorization_request_helper.rb +4 -4
  159. data/spec/support/helpers/request_spec_helper.rb +2 -2
  160. data/spec/support/helpers/url_helper.rb +7 -3
  161. data/spec/support/http_method_shim.rb +12 -16
  162. data/spec/validators/redirect_uri_validator_spec.rb +7 -1
  163. data/spec/version/version_spec.rb +3 -3
  164. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  165. metadata +33 -31
  166. data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +0 -31
  167. data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +0 -11
  168. data/spec/controllers/application_metal_controller.rb +0 -10
  169. /data/spec/support/dependencies/{factory_girl.rb → factory_bot.rb} +0 -0
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  feature 'Adding applications' do
4
4
  context 'in application form' do
@@ -20,6 +20,77 @@ feature 'Adding applications' do
20
20
  click_button 'Submit'
21
21
  i_should_see 'Whoops! Check your form for possible errors'
22
22
  end
23
+
24
+ scenario "adding app ignoring bad scope" do
25
+ config_is_set("enforce_configured_scopes", false)
26
+
27
+ fill_in "doorkeeper_application[name]", with: "My Application"
28
+ fill_in "doorkeeper_application[redirect_uri]",
29
+ with: "https://example.com"
30
+ fill_in "doorkeeper_application[scopes]", with: "blahblah"
31
+
32
+ click_button "Submit"
33
+ i_should_see "Application created"
34
+ i_should_see "My Application"
35
+ end
36
+
37
+ scenario "adding app validating bad scope" do
38
+ config_is_set("enforce_configured_scopes", true)
39
+
40
+ fill_in "doorkeeper_application[name]", with: "My Application"
41
+ fill_in "doorkeeper_application[redirect_uri]",
42
+ with: "https://example.com"
43
+ fill_in "doorkeeper_application[scopes]", with: "blahblah"
44
+
45
+ click_button "Submit"
46
+ i_should_see "Whoops! Check your form for possible errors"
47
+ end
48
+
49
+ scenario "adding app validating scope, blank scope is accepted" do
50
+ config_is_set("enforce_configured_scopes", true)
51
+
52
+ fill_in "doorkeeper_application[name]", with: "My Application"
53
+ fill_in "doorkeeper_application[redirect_uri]",
54
+ with: "https://example.com"
55
+ fill_in "doorkeeper_application[scopes]", with: ""
56
+
57
+ click_button "Submit"
58
+ i_should_see "Application created"
59
+ i_should_see "My Application"
60
+ end
61
+
62
+ scenario "adding app validating scope, multiple scopes configured" do
63
+ config_is_set("enforce_configured_scopes", true)
64
+ scopes = Doorkeeper::OAuth::Scopes.from_array(%w(read write admin))
65
+ config_is_set("optional_scopes", scopes)
66
+
67
+ fill_in "doorkeeper_application[name]", with: "My Application"
68
+ fill_in "doorkeeper_application[redirect_uri]",
69
+ with: "https://example.com"
70
+ fill_in "doorkeeper_application[scopes]", with: "read write"
71
+
72
+ click_button "Submit"
73
+ i_should_see "Application created"
74
+ i_should_see "My Application"
75
+ end
76
+
77
+ scenario "adding app validating scope, bad scope with multiple scopes configured" do
78
+ config_is_set("enforce_configured_scopes", true)
79
+ scopes = Doorkeeper::OAuth::Scopes.from_array(%w(read write admin))
80
+ config_is_set("optional_scopes", scopes)
81
+
82
+ fill_in "doorkeeper_application[name]", with: "My Application"
83
+ fill_in "doorkeeper_application[redirect_uri]",
84
+ with: "https://example.com"
85
+ fill_in "doorkeeper_application[scopes]", with: "read blah"
86
+
87
+ click_button "Submit"
88
+ i_should_see "Whoops! Check your form for possible errors"
89
+ i_should_see Regexp.new(
90
+ I18n.t('activerecord.errors.models.doorkeeper/application.attributes.scopes.not_match_configured'),
91
+ true
92
+ )
93
+ end
23
94
  end
24
95
  end
25
96
 
@@ -36,6 +107,23 @@ feature 'Listing applications' do
36
107
  end
37
108
  end
38
109
 
110
+ feature 'Renders assets' do
111
+ scenario 'admin stylesheets' do
112
+ visit '/assets/doorkeeper/admin/application.css'
113
+
114
+ i_should_see 'Bootstrap'
115
+ i_should_see '.doorkeeper-admin'
116
+ end
117
+
118
+ scenario 'application stylesheets' do
119
+ visit '/assets/doorkeeper/application.css'
120
+
121
+ i_should_see 'Bootstrap'
122
+ i_should_see '#oauth-permissions'
123
+ i_should_see '#container'
124
+ end
125
+ end
126
+
39
127
  feature 'Show application' do
40
128
  given :app do
41
129
  FactoryBot.create :application, name: 'Just another oauth app'
@@ -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
@@ -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) { {} }
@@ -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
@@ -53,10 +54,205 @@ feature 'Authorization Code Flow' do
53
54
  should_not_have_json 'error'
54
55
 
55
56
  should_have_json 'access_token', Doorkeeper::AccessToken.first.token
56
- should_have_json 'token_type', 'Bearer'
57
+ should_have_json 'token_type', 'bearer'
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).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
+
60
256
  context 'with scopes' do
61
257
  background do
62
258
  default_scopes_exist :public
@@ -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
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  feature 'Implicit Grant Flow (feature spec)' do
4
4
  background do
@@ -17,6 +17,29 @@ feature 'Implicit Grant Flow (feature spec)' do
17
17
 
18
18
  i_should_be_on_client_callback @client
19
19
  end
20
+
21
+ context 'when application scopes are present and no scope is passed' do
22
+ background do
23
+ @client.update_attributes(scopes: 'public write read')
24
+ end
25
+
26
+ scenario 'access token has no scopes' do
27
+ default_scopes_exist :admin
28
+ visit authorization_endpoint_url(client: @client, response_type: 'token')
29
+ click_on 'Authorize'
30
+ access_token_should_exist_for @client, @resource_owner
31
+ token = Doorkeeper::AccessToken.first
32
+ expect(token.scopes).to be_empty
33
+ end
34
+
35
+ scenario 'access token has scopes which are common in application scopees and default scopes' do
36
+ default_scopes_exist :public, :write
37
+ visit authorization_endpoint_url(client: @client, response_type: 'token')
38
+ click_on 'Authorize'
39
+ access_token_should_exist_for @client, @resource_owner
40
+ access_token_should_have_scopes :public, :write
41
+ end
42
+ end
20
43
  end
21
44
 
22
45
  describe 'Implicit Grant Flow (request spec)' do
@@ -34,11 +57,13 @@ describe 'Implicit Grant Flow (request spec)' do
34
57
  token = client_is_authorized(@client, @resource_owner)
35
58
 
36
59
  post "/oauth/authorize",
37
- client_id: @client.uid,
38
- state: '',
39
- redirect_uri: @client.redirect_uri,
40
- response_type: 'token',
41
- commit: 'Authorize'
60
+ params: {
61
+ client_id: @client.uid,
62
+ state: '',
63
+ redirect_uri: @client.redirect_uri,
64
+ response_type: 'token',
65
+ commit: 'Authorize'
66
+ }
42
67
 
43
68
  expect(response.location).not_to include(token.token)
44
69
  end
@@ -49,11 +74,13 @@ describe 'Implicit Grant Flow (request spec)' do
49
74
  token = client_is_authorized(@client, @resource_owner)
50
75
 
51
76
  post "/oauth/authorize",
52
- client_id: @client.uid,
53
- state: '',
54
- redirect_uri: @client.redirect_uri,
55
- response_type: 'token',
56
- commit: 'Authorize'
77
+ params: {
78
+ client_id: @client.uid,
79
+ state: '',
80
+ redirect_uri: @client.redirect_uri,
81
+ response_type: 'token',
82
+ commit: 'Authorize'
83
+ }
57
84
 
58
85
  expect(response.location).to include(token.token)
59
86
  end
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  describe 'Resource Owner Password Credentials Flow not set up' do
4
4
  before do
@@ -7,7 +7,7 @@ describe 'Resource Owner Password Credentials Flow not set up' do
7
7
  end
8
8
 
9
9
  context 'with valid user credentials' do
10
- it 'doesn\'t issue new token' do
10
+ it 'does not issue new token' do
11
11
  expect do
12
12
  post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
13
13
  end.to_not(change { Doorkeeper::AccessToken.count })
@@ -140,6 +140,60 @@ describe 'Resource Owner Password Credentials Flow' do
140
140
  end
141
141
  end
142
142
 
143
+ context 'when application scopes are present and differs from configured default scopes and no scope is passed' do
144
+ before do
145
+ default_scopes_exist :public
146
+ @client.update_attributes(scopes: 'abc')
147
+ end
148
+
149
+ it 'issues new token without any scope' do
150
+ expect do
151
+ post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
152
+ end.to change { Doorkeeper::AccessToken.count }.by(1)
153
+
154
+ token = Doorkeeper::AccessToken.first
155
+
156
+ expect(token.application_id).to eq @client.id
157
+ expect(token.scopes).to be_empty
158
+ should_have_json 'access_token', token.token
159
+ should_not_have_json 'scope'
160
+ end
161
+ end
162
+
163
+ context 'when application scopes contain some of the default scopes and no scope is passed' do
164
+ before do
165
+ @client.update_attributes(scopes: 'read write public')
166
+ end
167
+
168
+ it 'issues new token with one default scope that are present in application scopes' do
169
+ default_scopes_exist :public, :admin
170
+
171
+ expect do
172
+ post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
173
+ end.to change { Doorkeeper::AccessToken.count }.by(1)
174
+
175
+ token = Doorkeeper::AccessToken.first
176
+
177
+ expect(token.application_id).to eq @client.id
178
+ should_have_json 'access_token', token.token
179
+ should_have_json 'scope', 'public'
180
+ end
181
+
182
+ it 'issues new token with multiple default scopes that are present in application scopes' do
183
+ default_scopes_exist :public, :read, :update
184
+
185
+ expect do
186
+ post password_token_endpoint_url(client: @client, resource_owner: @resource_owner)
187
+ end.to change { Doorkeeper::AccessToken.count }.by(1)
188
+
189
+ token = Doorkeeper::AccessToken.first
190
+
191
+ expect(token.application_id).to eq @client.id
192
+ should_have_json 'access_token', token.token
193
+ should_have_json 'scope', 'public read'
194
+ end
195
+ end
196
+
143
197
  context 'with invalid scopes' do
144
198
  subject do
145
199
  post password_token_endpoint_url(client: @client,
@@ -1,4 +1,4 @@
1
- require 'spec_helper_integration'
1
+ require 'spec_helper'
2
2
 
3
3
  describe 'Refresh Token Flow' do
4
4
  before do
@@ -14,7 +14,7 @@ describe 'Refresh Token Flow' do
14
14
  authorization_code_exists application: @client
15
15
  end
16
16
 
17
- it 'client gets the refresh token and refreshses it' do
17
+ it 'client gets the refresh token and refreshes it' do
18
18
  post token_endpoint_url(code: @authorization.token, client: @client)
19
19
 
20
20
  token = Doorkeeper::AccessToken.first