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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.gitlab-ci.yml +16 -0
- data/.travis.yml +7 -0
- data/Appraisals +2 -2
- data/Dangerfile +64 -0
- data/Gemfile +1 -1
- data/NEWS.md +98 -8
- data/README.md +110 -12
- data/Rakefile +6 -0
- data/UPGRADE.md +2 -0
- data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
- data/app/controllers/doorkeeper/application_controller.rb +6 -3
- data/app/controllers/doorkeeper/application_metal_controller.rb +6 -0
- data/app/controllers/doorkeeper/applications_controller.rb +46 -24
- data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +21 -2
- data/app/controllers/doorkeeper/token_info_controller.rb +2 -0
- data/app/controllers/doorkeeper/tokens_controller.rb +4 -6
- data/app/helpers/doorkeeper/dashboard_helper.rb +9 -7
- data/app/validators/redirect_uri_validator.rb +5 -2
- data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
- data/app/views/doorkeeper/applications/_form.html.erb +25 -24
- data/app/views/doorkeeper/applications/edit.html.erb +1 -1
- data/app/views/doorkeeper/applications/index.html.erb +17 -7
- data/app/views/doorkeeper/applications/new.html.erb +1 -1
- data/app/views/doorkeeper/applications/show.html.erb +6 -6
- data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
- data/app/views/doorkeeper/authorizations/new.html.erb +4 -0
- data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
- data/config/locales/en.yml +10 -1
- data/doorkeeper.gemspec +25 -26
- data/gemfiles/rails_5_2.gemfile +1 -1
- data/gemfiles/rails_master.gemfile +4 -1
- data/lib/doorkeeper/config.rb +81 -40
- data/lib/doorkeeper/engine.rb +6 -0
- data/lib/doorkeeper/errors.rb +17 -3
- data/lib/doorkeeper/grape/authorization_decorator.rb +2 -0
- data/lib/doorkeeper/grape/helpers.rb +3 -1
- data/lib/doorkeeper/helpers/controller.rb +9 -2
- data/lib/doorkeeper/models/access_grant_mixin.rb +73 -0
- data/lib/doorkeeper/models/access_token_mixin.rb +44 -25
- data/lib/doorkeeper/models/application_mixin.rb +2 -0
- data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
- data/lib/doorkeeper/models/concerns/expirable.rb +2 -0
- data/lib/doorkeeper/models/concerns/orderable.rb +2 -0
- data/lib/doorkeeper/models/concerns/ownership.rb +2 -0
- data/lib/doorkeeper/models/concerns/revocable.rb +2 -0
- data/lib/doorkeeper/models/concerns/scopes.rb +3 -1
- data/lib/doorkeeper/oauth/authorization/code.rb +33 -8
- data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +38 -14
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +2 -0
- data/lib/doorkeeper/oauth/authorization_code_request.rb +29 -2
- data/lib/doorkeeper/oauth/base_request.rb +22 -9
- data/lib/doorkeeper/oauth/base_response.rb +2 -0
- data/lib/doorkeeper/oauth/client/credentials.rb +3 -1
- data/lib/doorkeeper/oauth/client.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +4 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -2
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +5 -5
- data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -3
- data/lib/doorkeeper/oauth/code_request.rb +2 -0
- data/lib/doorkeeper/oauth/code_response.rb +2 -0
- data/lib/doorkeeper/oauth/error.rb +2 -0
- data/lib/doorkeeper/oauth/error_response.rb +21 -3
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +9 -2
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +2 -8
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +2 -0
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +5 -2
- data/lib/doorkeeper/oauth/invalid_token_response.rb +18 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +43 -11
- data/lib/doorkeeper/oauth/refresh_token_request.rb +16 -3
- data/lib/doorkeeper/oauth/scopes.rb +3 -1
- data/lib/doorkeeper/oauth/token.rb +7 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +4 -2
- data/lib/doorkeeper/oauth/token_request.rb +2 -0
- data/lib/doorkeeper/oauth/token_response.rb +6 -2
- data/lib/doorkeeper/oauth.rb +13 -0
- data/lib/doorkeeper/orm/active_record/application.rb +75 -12
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
- data/lib/doorkeeper/orm/active_record.rb +4 -0
- data/lib/doorkeeper/rails/helpers.rb +6 -4
- data/lib/doorkeeper/rails/routes/mapper.rb +2 -0
- data/lib/doorkeeper/rails/routes/mapping.rb +2 -0
- data/lib/doorkeeper/rails/routes.rb +23 -8
- data/lib/doorkeeper/rake/db.rake +40 -0
- data/lib/doorkeeper/rake/setup.rake +6 -0
- data/lib/doorkeeper/rake.rb +14 -0
- data/lib/doorkeeper/request/authorization_code.rb +1 -1
- data/lib/doorkeeper/request/client_credentials.rb +1 -1
- data/lib/doorkeeper/request/code.rb +1 -1
- data/lib/doorkeeper/request/password.rb +1 -1
- data/lib/doorkeeper/request/refresh_token.rb +1 -1
- data/lib/doorkeeper/request/strategy.rb +2 -0
- data/lib/doorkeeper/request/token.rb +1 -1
- data/lib/doorkeeper/request.rb +29 -34
- data/lib/doorkeeper/server.rb +2 -0
- data/lib/doorkeeper/stale_records_cleaner.rb +20 -0
- data/lib/doorkeeper/validations.rb +2 -0
- data/lib/doorkeeper/version.rb +6 -24
- data/lib/doorkeeper.rb +20 -17
- data/lib/generators/doorkeeper/application_owner_generator.rb +23 -18
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +32 -0
- data/lib/generators/doorkeeper/install_generator.rb +17 -9
- data/lib/generators/doorkeeper/migration_generator.rb +23 -18
- data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -24
- data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
- data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +96 -13
- data/lib/generators/doorkeeper/templates/migration.rb.erb +2 -3
- data/lib/generators/doorkeeper/views_generator.rb +3 -1
- data/spec/controllers/application_metal_controller_spec.rb +50 -0
- data/spec/controllers/applications_controller_spec.rb +123 -14
- data/spec/controllers/authorizations_controller_spec.rb +334 -51
- data/spec/controllers/protected_resources_controller_spec.rb +60 -18
- data/spec/controllers/token_info_controller_spec.rb +4 -12
- data/spec/controllers/tokens_controller_spec.rb +17 -20
- data/spec/dummy/Rakefile +1 -1
- data/spec/dummy/app/assets/config/manifest.js +2 -0
- data/spec/dummy/app/controllers/custom_authorizations_controller.rb +1 -1
- data/spec/dummy/app/controllers/home_controller.rb +1 -2
- data/spec/dummy/config/application.rb +1 -1
- data/spec/dummy/config/boot.rb +2 -4
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/dummy/config/environments/test.rb +5 -6
- data/spec/dummy/config/initializers/doorkeeper.rb +12 -6
- data/spec/dummy/config/initializers/new_framework_defaults.rb +2 -0
- data/spec/dummy/config/initializers/secret_token.rb +1 -1
- data/spec/dummy/config/routes.rb +3 -42
- data/spec/dummy/config.ru +1 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +4 -4
- data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +1 -1
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
- data/spec/dummy/db/migrate/{20180210183654_add_confidential_to_application.rb → 20180210183654_add_confidential_to_applications.rb} +1 -1
- data/spec/dummy/db/schema.rb +36 -36
- data/spec/dummy/script/rails +4 -3
- data/spec/factories.rb +6 -6
- data/spec/generators/application_owner_generator_spec.rb +1 -1
- data/spec/generators/confidential_applications_generator_spec.rb +45 -0
- data/spec/generators/install_generator_spec.rb +5 -2
- data/spec/generators/migration_generator_spec.rb +1 -1
- data/spec/generators/pkce_generator_spec.rb +43 -0
- data/spec/generators/previous_refresh_token_generator_spec.rb +1 -1
- data/spec/generators/templates/routes.rb +0 -1
- data/spec/generators/views_generator_spec.rb +2 -2
- data/spec/grape/grape_integration_spec.rb +2 -2
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +105 -39
- data/spec/lib/doorkeeper_spec.rb +6 -131
- data/spec/lib/models/expirable_spec.rb +0 -3
- data/spec/lib/models/revocable_spec.rb +0 -2
- data/spec/lib/models/scopes_spec.rb +0 -4
- data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -4
- data/spec/lib/oauth/authorization_code_request_spec.rb +17 -7
- data/spec/lib/oauth/base_request_spec.rb +49 -11
- data/spec/lib/oauth/base_response_spec.rb +1 -1
- data/spec/lib/oauth/client/credentials_spec.rb +2 -4
- data/spec/lib/oauth/client_credentials/creator_spec.rb +5 -1
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +24 -7
- data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
- data/spec/lib/oauth/client_credentials_integration_spec.rb +2 -2
- data/spec/lib/oauth/client_credentials_request_spec.rb +3 -5
- data/spec/lib/oauth/client_spec.rb +0 -3
- data/spec/lib/oauth/code_request_spec.rb +5 -3
- data/spec/lib/oauth/code_response_spec.rb +1 -1
- data/spec/lib/oauth/error_response_spec.rb +0 -3
- data/spec/lib/oauth/error_spec.rb +0 -2
- data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +8 -11
- data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +22 -13
- data/spec/lib/oauth/invalid_token_response_spec.rb +1 -4
- data/spec/lib/oauth/password_access_token_request_spec.rb +53 -6
- data/spec/lib/oauth/pre_authorization_spec.rb +33 -4
- data/spec/lib/oauth/refresh_token_request_spec.rb +22 -14
- data/spec/lib/oauth/scopes_spec.rb +0 -3
- data/spec/lib/oauth/token_request_spec.rb +8 -9
- data/spec/lib/oauth/token_response_spec.rb +0 -1
- data/spec/lib/oauth/token_spec.rb +40 -14
- data/spec/lib/request/strategy_spec.rb +0 -1
- data/spec/lib/server_spec.rb +7 -7
- data/spec/lib/stale_records_cleaner_spec.rb +89 -0
- data/spec/models/doorkeeper/access_grant_spec.rb +44 -1
- data/spec/models/doorkeeper/access_token_spec.rb +80 -32
- data/spec/models/doorkeeper/application_spec.rb +293 -221
- data/spec/requests/applications/applications_request_spec.rb +134 -1
- data/spec/requests/applications/authorized_applications_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +3 -3
- data/spec/requests/endpoints/token_spec.rb +7 -5
- data/spec/requests/flows/authorization_code_errors_spec.rb +2 -2
- data/spec/requests/flows/authorization_code_spec.rb +258 -2
- data/spec/requests/flows/client_credentials_spec.rb +46 -6
- data/spec/requests/flows/implicit_grant_errors_spec.rb +3 -3
- data/spec/requests/flows/implicit_grant_spec.rb +38 -11
- data/spec/requests/flows/password_spec.rb +61 -3
- data/spec/requests/flows/refresh_token_spec.rb +59 -2
- data/spec/requests/flows/revoke_token_spec.rb +20 -20
- data/spec/requests/flows/skip_authorization_spec.rb +16 -11
- data/spec/requests/protected_resources/metal_spec.rb +1 -1
- data/spec/requests/protected_resources/private_api_spec.rb +3 -3
- data/spec/routing/custom_controller_routes_spec.rb +59 -7
- data/spec/routing/default_routes_spec.rb +2 -2
- data/spec/routing/scoped_routes_spec.rb +16 -2
- data/spec/spec_helper.rb +54 -3
- data/spec/spec_helper_integration.rb +2 -74
- data/spec/support/dependencies/{factory_girl.rb → factory_bot.rb} +0 -0
- data/spec/support/doorkeeper_rspec.rb +20 -0
- data/spec/support/helpers/authorization_request_helper.rb +4 -4
- data/spec/support/helpers/model_helper.rb +8 -4
- data/spec/support/helpers/request_spec_helper.rb +10 -2
- data/spec/support/helpers/url_helper.rb +18 -14
- data/spec/support/http_method_shim.rb +12 -16
- data/spec/support/shared/controllers_shared_context.rb +56 -0
- data/spec/validators/redirect_uri_validator_spec.rb +9 -3
- data/spec/version/version_spec.rb +3 -3
- data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
- metadata +54 -35
- data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +0 -31
- data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +0 -11
- data/spec/controllers/application_metal_controller.rb +0 -10
@@ -1,8 +1,9 @@
|
|
1
|
-
require '
|
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 '
|
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
|
-
|
67
|
+
end.to raise_error(ActionController::InvalidAuthenticityToken)
|
68
68
|
end
|
69
69
|
end
|
70
70
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
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
|
-
|
26
|
-
|
27
|
-
|
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 '
|
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
|
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 '
|
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)
|
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 '
|
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 '
|
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
|
-
[
|
18
|
-
[
|
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')
|