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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE.md +25 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- data/.gitignore +2 -1
- data/.gitlab-ci.yml +16 -0
- data/.hound.yml +2 -13
- data/.rubocop.yml +17 -0
- data/.travis.yml +19 -4
- data/Appraisals +8 -4
- data/CODE_OF_CONDUCT.md +46 -0
- data/Gemfile +1 -1
- data/NEWS.md +115 -0
- data/README.md +175 -39
- data/RELEASING.md +5 -12
- data/Rakefile +6 -0
- data/SECURITY.md +15 -0
- data/UPGRADE.md +2 -0
- data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
- data/app/controllers/doorkeeper/application_controller.rb +2 -5
- data/app/controllers/doorkeeper/application_metal_controller.rb +4 -0
- data/app/controllers/doorkeeper/applications_controller.rb +47 -13
- data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +19 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +24 -17
- data/app/helpers/doorkeeper/dashboard_helper.rb +8 -6
- data/app/validators/redirect_uri_validator.rb +13 -2
- data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
- data/app/views/doorkeeper/applications/_form.html.erb +31 -19
- data/app/views/doorkeeper/applications/edit.html.erb +1 -1
- data/app/views/doorkeeper/applications/index.html.erb +18 -6
- data/app/views/doorkeeper/applications/new.html.erb +1 -1
- data/app/views/doorkeeper/applications/show.html.erb +8 -5
- data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
- data/app/views/doorkeeper/authorizations/new.html.erb +5 -1
- data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
- data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
- data/config/locales/en.yml +19 -6
- data/doorkeeper.gemspec +20 -20
- data/gemfiles/rails_4_2.gemfile +6 -4
- data/gemfiles/rails_5_0.gemfile +4 -4
- data/gemfiles/rails_5_1.gemfile +6 -7
- data/gemfiles/rails_5_2.gemfile +12 -0
- data/gemfiles/rails_master.gemfile +17 -0
- data/lib/doorkeeper/config.rb +123 -71
- data/lib/doorkeeper/engine.rb +7 -3
- data/lib/doorkeeper/errors.rb +20 -5
- data/lib/doorkeeper/grape/helpers.rb +14 -9
- data/lib/doorkeeper/helpers/controller.rb +16 -22
- data/lib/doorkeeper/models/access_grant_mixin.rb +67 -23
- data/lib/doorkeeper/models/access_token_mixin.rb +54 -55
- data/lib/doorkeeper/models/application_mixin.rb +16 -30
- data/lib/doorkeeper/models/concerns/expirable.rb +7 -5
- data/lib/doorkeeper/models/concerns/orderable.rb +13 -0
- data/lib/doorkeeper/models/concerns/scopes.rb +1 -1
- data/lib/doorkeeper/oauth/authorization/code.rb +31 -8
- data/lib/doorkeeper/oauth/authorization/context.rb +15 -0
- data/lib/doorkeeper/oauth/authorization/token.rb +46 -20
- data/lib/doorkeeper/oauth/authorization_code_request.rb +33 -3
- data/lib/doorkeeper/oauth/base_request.rb +23 -7
- data/lib/doorkeeper/oauth/client/credentials.rb +6 -4
- data/lib/doorkeeper/oauth/client.rb +2 -4
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +6 -3
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -6
- data/lib/doorkeeper/oauth/client_credentials_request.rb +0 -4
- data/lib/doorkeeper/oauth/error.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +11 -4
- data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +0 -8
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +15 -0
- data/lib/doorkeeper/oauth/invalid_token_response.rb +3 -4
- data/lib/doorkeeper/oauth/password_access_token_request.rb +8 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +45 -13
- data/lib/doorkeeper/oauth/refresh_token_request.rb +7 -1
- data/lib/doorkeeper/oauth/scopes.rb +19 -9
- data/lib/doorkeeper/oauth/token.rb +6 -3
- data/lib/doorkeeper/oauth/token_introspection.rb +128 -0
- data/lib/doorkeeper/oauth/token_response.rb +4 -2
- data/lib/doorkeeper/oauth.rb +13 -0
- data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -0
- data/lib/doorkeeper/orm/active_record/access_token.rb +21 -20
- data/lib/doorkeeper/orm/active_record/application.rb +45 -0
- data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
- data/lib/doorkeeper/orm/active_record.rb +21 -8
- data/lib/doorkeeper/rails/helpers.rb +7 -10
- data/lib/doorkeeper/rails/routes.rb +21 -7
- 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 +0 -2
- data/lib/doorkeeper/request/client_credentials.rb +0 -2
- data/lib/doorkeeper/request/code.rb +0 -2
- data/lib/doorkeeper/request/password.rb +1 -13
- data/lib/doorkeeper/request/refresh_token.rb +0 -2
- data/lib/doorkeeper/request/token.rb +0 -2
- data/lib/doorkeeper/request.rb +29 -30
- data/lib/doorkeeper/validations.rb +3 -2
- data/lib/doorkeeper/version.rb +14 -1
- data/lib/doorkeeper.rb +21 -17
- data/lib/generators/doorkeeper/application_owner_generator.rb +26 -12
- 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 +26 -9
- data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +31 -20
- data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
- data/lib/generators/doorkeeper/templates/{add_owner_to_application_migration.rb → add_owner_to_application_migration.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/{add_previous_refresh_token_to_access_tokens.rb → add_previous_refresh_token_to_access_tokens.rb.erb} +1 -1
- data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
- data/lib/generators/doorkeeper/templates/initializer.rb +104 -12
- data/lib/generators/doorkeeper/templates/{migration.rb → migration.rb.erb} +2 -1
- 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 +141 -17
- data/spec/controllers/authorizations_controller_spec.rb +304 -27
- data/spec/controllers/protected_resources_controller_spec.rb +44 -35
- data/spec/controllers/token_info_controller_spec.rb +17 -21
- data/spec/controllers/tokens_controller_spec.rb +201 -17
- data/spec/dummy/app/assets/config/manifest.js +2 -0
- data/spec/dummy/config/environments/test.rb +4 -5
- data/spec/dummy/config/initializers/doorkeeper.rb +23 -2
- data/spec/dummy/config/initializers/{active_record_belongs_to_required_by_default.rb → new_framework_defaults.rb} +5 -1
- data/spec/dummy/config/initializers/secret_token.rb +0 -1
- data/spec/dummy/config/routes.rb +3 -42
- data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
- data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -1
- data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +3 -1
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +3 -1
- data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +13 -0
- data/spec/dummy/db/schema.rb +38 -37
- data/spec/factories.rb +1 -1
- data/spec/generators/application_owner_generator_spec.rb +25 -6
- data/spec/generators/confidential_applications_generator_spec.rb +45 -0
- data/spec/generators/install_generator_spec.rb +1 -1
- data/spec/generators/migration_generator_spec.rb +25 -4
- data/spec/generators/pkce_generator_spec.rb +43 -0
- data/spec/generators/previous_refresh_token_generator_spec.rb +57 -0
- data/spec/generators/views_generator_spec.rb +1 -1
- data/spec/grape/grape_integration_spec.rb +135 -0
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +2 -2
- data/spec/lib/config_spec.rb +199 -22
- data/spec/lib/doorkeeper_spec.rb +1 -126
- data/spec/lib/models/expirable_spec.rb +0 -4
- data/spec/lib/models/revocable_spec.rb +2 -4
- 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 +63 -13
- data/spec/lib/oauth/base_request_spec.rb +43 -10
- data/spec/lib/oauth/base_response_spec.rb +1 -1
- data/spec/lib/oauth/client/credentials_spec.rb +5 -5
- data/spec/lib/oauth/client_credentials/creator_spec.rb +6 -2
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +26 -7
- data/spec/lib/oauth/client_credentials/validation_spec.rb +2 -3
- data/spec/lib/oauth/client_credentials_integration_spec.rb +2 -2
- data/spec/lib/oauth/client_credentials_request_spec.rb +4 -5
- data/spec/lib/oauth/client_spec.rb +0 -3
- data/spec/lib/oauth/code_request_spec.rb +5 -5
- data/spec/lib/oauth/error_response_spec.rb +0 -3
- data/spec/lib/oauth/error_spec.rb +1 -3
- data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -3
- data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -3
- data/spec/lib/oauth/invalid_token_response_spec.rb +2 -5
- data/spec/lib/oauth/password_access_token_request_spec.rb +46 -5
- data/spec/lib/oauth/pre_authorization_spec.rb +40 -6
- data/spec/lib/oauth/refresh_token_request_spec.rb +30 -14
- data/spec/lib/oauth/scopes_spec.rb +28 -4
- data/spec/lib/oauth/token_request_spec.rb +10 -13
- data/spec/lib/oauth/token_response_spec.rb +0 -1
- data/spec/lib/oauth/token_spec.rb +37 -14
- data/spec/lib/orm/active_record/stale_records_cleaner_spec.rb +79 -0
- data/spec/lib/request/strategy_spec.rb +0 -1
- data/spec/lib/server_spec.rb +10 -0
- data/spec/models/doorkeeper/access_grant_spec.rb +45 -2
- data/spec/models/doorkeeper/access_token_spec.rb +132 -64
- data/spec/models/doorkeeper/application_spec.rb +114 -23
- data/spec/requests/applications/applications_request_spec.rb +139 -6
- data/spec/requests/applications/authorized_applications_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +1 -1
- data/spec/requests/endpoints/token_spec.rb +15 -6
- data/spec/requests/flows/authorization_code_errors_spec.rb +12 -2
- data/spec/requests/flows/authorization_code_spec.rb +199 -2
- data/spec/requests/flows/client_credentials_spec.rb +73 -5
- 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 +160 -24
- data/spec/requests/flows/refresh_token_spec.rb +6 -6
- data/spec/requests/flows/revoke_token_spec.rb +26 -26
- data/spec/requests/flows/skip_authorization_spec.rb +16 -11
- data/spec/requests/protected_resources/metal_spec.rb +2 -2
- data/spec/requests/protected_resources/private_api_spec.rb +2 -2
- data/spec/routing/custom_controller_routes_spec.rb +63 -7
- data/spec/routing/default_routes_spec.rb +6 -2
- data/spec/routing/scoped_routes_spec.rb +16 -2
- data/spec/spec_helper.rb +54 -3
- data/spec/spec_helper_integration.rb +2 -64
- data/spec/support/dependencies/factory_bot.rb +2 -0
- data/spec/support/doorkeeper_rspec.rb +19 -0
- data/spec/support/helpers/access_token_request_helper.rb +1 -1
- data/spec/support/helpers/authorization_request_helper.rb +4 -4
- data/spec/support/helpers/model_helper.rb +9 -4
- data/spec/support/helpers/request_spec_helper.rb +18 -6
- data/spec/support/helpers/url_helper.rb +15 -10
- data/spec/support/http_method_shim.rb +12 -16
- data/spec/support/shared/controllers_shared_context.rb +2 -6
- data/spec/support/shared/models_shared_examples.rb +4 -4
- data/spec/validators/redirect_uri_validator_spec.rb +58 -7
- data/spec/version/version_spec.rb +15 -0
- data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
- metadata +75 -33
- data/spec/controllers/application_metal_controller.rb +0 -10
- data/spec/support/dependencies/factory_girl.rb +0 -2
|
@@ -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,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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 '
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 '
|
|
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 '
|
|
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', '
|
|
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 '
|
|
1
|
+
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe 'Client Credentials Request' do
|
|
4
|
-
let(:client) {
|
|
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 '
|
|
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
|
|
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
|
|
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
|