doorkeeper 5.1.0 → 5.3.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/Appraisals +3 -3
- data/CHANGELOG.md +881 -0
- data/CONTRIBUTING.md +11 -9
- data/Dangerfile +2 -2
- data/Dockerfile +29 -0
- data/Gemfile +4 -3
- data/NEWS.md +1 -814
- data/README.md +11 -3
- data/RELEASING.md +6 -5
- data/app/controllers/doorkeeper/application_controller.rb +2 -2
- data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
- data/app/controllers/doorkeeper/applications_controller.rb +1 -0
- data/app/controllers/doorkeeper/authorizations_controller.rb +16 -9
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
- data/app/views/doorkeeper/applications/_form.html.erb +0 -6
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/config/locales/en.yml +8 -2
- data/doorkeeper.gemspec +9 -1
- data/gemfiles/rails_5_0.gemfile +3 -2
- data/gemfiles/rails_5_1.gemfile +3 -2
- data/gemfiles/rails_5_2.gemfile +3 -2
- data/gemfiles/rails_6_0.gemfile +4 -3
- data/gemfiles/rails_master.gemfile +3 -2
- data/lib/doorkeeper/config/option.rb +13 -7
- data/lib/doorkeeper/config.rb +157 -42
- data/lib/doorkeeper/errors.rb +13 -18
- data/lib/doorkeeper/grape/helpers.rb +6 -2
- data/lib/doorkeeper/helpers/controller.rb +28 -7
- data/lib/doorkeeper/models/access_grant_mixin.rb +7 -6
- data/lib/doorkeeper/models/access_token_mixin.rb +99 -20
- data/lib/doorkeeper/models/application_mixin.rb +3 -3
- data/lib/doorkeeper/models/concerns/ownership.rb +1 -1
- data/lib/doorkeeper/models/concerns/reusable.rb +1 -1
- data/lib/doorkeeper/models/concerns/revocable.rb +0 -27
- data/lib/doorkeeper/oauth/authorization/code.rb +12 -14
- data/lib/doorkeeper/oauth/authorization/token.rb +10 -7
- data/lib/doorkeeper/oauth/authorization_code_request.rb +31 -15
- data/lib/doorkeeper/oauth/base_request.rb +10 -4
- data/lib/doorkeeper/oauth/client.rb +7 -8
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +23 -2
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -7
- data/lib/doorkeeper/oauth/client_credentials/{validation.rb → validator.rb} +10 -2
- data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
- data/lib/doorkeeper/oauth/code_request.rb +5 -11
- data/lib/doorkeeper/oauth/code_response.rb +4 -4
- data/lib/doorkeeper/oauth/error.rb +1 -1
- data/lib/doorkeeper/oauth/error_response.rb +6 -6
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +7 -5
- data/lib/doorkeeper/oauth/helpers/unique_token.rb +8 -5
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +19 -5
- data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
- data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -2
- data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
- data/lib/doorkeeper/oauth/pre_authorization.rb +76 -38
- data/lib/doorkeeper/oauth/refresh_token_request.rb +17 -14
- data/lib/doorkeeper/oauth/token.rb +2 -2
- data/lib/doorkeeper/oauth/token_introspection.rb +26 -16
- data/lib/doorkeeper/oauth/token_request.rb +4 -18
- data/lib/doorkeeper/orm/active_record/access_grant.rb +4 -43
- data/lib/doorkeeper/orm/active_record/access_token.rb +4 -35
- data/lib/doorkeeper/orm/active_record/application.rb +5 -83
- data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +53 -0
- data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +47 -0
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +128 -0
- data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
- data/lib/doorkeeper/orm/active_record.rb +21 -6
- data/lib/doorkeeper/rails/helpers.rb +4 -4
- data/lib/doorkeeper/rails/routes.rb +5 -7
- data/lib/doorkeeper/rake/db.rake +3 -3
- data/lib/doorkeeper/request/authorization_code.rb +5 -3
- data/lib/doorkeeper/request/client_credentials.rb +2 -2
- data/lib/doorkeeper/request/password.rb +2 -2
- data/lib/doorkeeper/request/refresh_token.rb +3 -3
- data/lib/doorkeeper/request.rb +7 -12
- data/lib/doorkeeper/server.rb +3 -7
- data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/doorkeeper.rb +6 -3
- data/lib/generators/doorkeeper/application_owner_generator.rb +1 -1
- data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
- data/lib/generators/doorkeeper/migration_generator.rb +1 -1
- data/lib/generators/doorkeeper/pkce_generator.rb +1 -1
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -7
- data/lib/generators/doorkeeper/templates/initializer.rb +149 -33
- data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
- data/spec/controllers/application_metal_controller_spec.rb +1 -1
- data/spec/controllers/applications_controller_spec.rb +95 -1
- data/spec/controllers/authorizations_controller_spec.rb +156 -75
- data/spec/controllers/protected_resources_controller_spec.rb +28 -20
- data/spec/controllers/token_info_controller_spec.rb +1 -1
- data/spec/controllers/tokens_controller_spec.rb +206 -38
- data/spec/dummy/config/application.rb +3 -1
- data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -3
- data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +1 -1
- data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +1 -1
- data/spec/generators/install_generator_spec.rb +1 -1
- data/spec/generators/previous_refresh_token_generator_spec.rb +2 -2
- data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
- data/spec/lib/config_spec.rb +104 -7
- data/spec/lib/doorkeeper_spec.rb +1 -1
- data/spec/lib/models/revocable_spec.rb +3 -3
- data/spec/lib/oauth/authorization_code_request_spec.rb +129 -115
- data/spec/lib/oauth/base_request_spec.rb +154 -135
- data/spec/lib/oauth/base_response_spec.rb +27 -29
- data/spec/lib/oauth/client/credentials_spec.rb +1 -1
- data/spec/lib/oauth/client_credentials/creator_spec.rb +43 -3
- data/spec/lib/oauth/client_credentials/issuer_spec.rb +12 -12
- data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
- data/spec/lib/oauth/client_credentials_integration_spec.rb +16 -18
- data/spec/lib/oauth/client_credentials_request_spec.rb +78 -80
- data/spec/lib/oauth/client_spec.rb +26 -26
- data/spec/lib/oauth/code_request_spec.rb +34 -35
- data/spec/lib/oauth/code_response_spec.rb +21 -25
- data/spec/lib/oauth/error_response_spec.rb +42 -44
- data/spec/lib/oauth/error_spec.rb +12 -14
- data/spec/lib/oauth/forbidden_token_response_spec.rb +11 -13
- data/spec/lib/oauth/helpers/scope_checker_spec.rb +30 -18
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
- data/spec/lib/oauth/invalid_request_response_spec.rb +73 -0
- data/spec/lib/oauth/invalid_token_response_spec.rb +32 -34
- data/spec/lib/oauth/password_access_token_request_spec.rb +145 -147
- data/spec/lib/oauth/pre_authorization_spec.rb +161 -153
- data/spec/lib/oauth/refresh_token_request_spec.rb +138 -138
- data/spec/lib/oauth/scopes_spec.rb +104 -106
- data/spec/lib/oauth/token_request_spec.rb +117 -110
- data/spec/lib/oauth/token_response_spec.rb +71 -73
- data/spec/lib/oauth/token_spec.rb +121 -123
- data/spec/lib/server_spec.rb +0 -12
- data/spec/models/doorkeeper/access_grant_spec.rb +21 -4
- data/spec/models/doorkeeper/access_token_spec.rb +42 -11
- data/spec/models/doorkeeper/application_spec.rb +114 -77
- data/spec/requests/applications/applications_request_spec.rb +1 -1
- data/spec/requests/endpoints/authorization_spec.rb +26 -8
- data/spec/requests/endpoints/token_spec.rb +1 -1
- data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
- data/spec/requests/flows/authorization_code_spec.rb +123 -45
- data/spec/requests/flows/client_credentials_spec.rb +38 -0
- data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
- data/spec/requests/flows/implicit_grant_spec.rb +9 -8
- data/spec/requests/flows/password_spec.rb +67 -10
- data/spec/requests/flows/refresh_token_spec.rb +20 -20
- data/spec/requests/flows/revoke_token_spec.rb +37 -23
- data/spec/spec_helper.rb +1 -4
- data/spec/support/doorkeeper_rspec.rb +1 -1
- data/spec/support/helpers/request_spec_helper.rb +14 -2
- data/spec/support/shared/controllers_shared_context.rb +33 -23
- data/spec/validators/redirect_uri_validator_spec.rb +41 -16
- metadata +19 -16
- data/.coveralls.yml +0 -1
- data/.github/ISSUE_TEMPLATE.md +0 -25
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
- data/.gitignore +0 -20
- data/.gitlab-ci.yml +0 -16
- data/.hound.yml +0 -3
- data/.rspec +0 -1
- data/.rubocop.yml +0 -50
- data/.travis.yml +0 -35
- data/app/validators/redirect_uri_validator.rb +0 -50
- data/spec/support/http_method_shim.rb +0 -29
|
@@ -4,6 +4,7 @@ require "spec_helper"
|
|
|
4
4
|
|
|
5
5
|
feature "Authorization Code Flow" do
|
|
6
6
|
background do
|
|
7
|
+
default_scopes_exist :default
|
|
7
8
|
config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
|
|
8
9
|
client_exists
|
|
9
10
|
create_resource_owner
|
|
@@ -23,13 +24,46 @@ feature "Authorization Code Flow" do
|
|
|
23
24
|
url_should_not_have_param("error")
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
context "when configured to check application supported grant flow" do
|
|
28
|
+
before do
|
|
29
|
+
config_is_set(:allow_grant_flow_for_client, ->(_grant_flow, client) { client.name == "admin" })
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
scenario "forbids the request when doesn't satisfy condition" do
|
|
33
|
+
@client.update(name: "sample app")
|
|
34
|
+
|
|
35
|
+
visit authorization_endpoint_url(client: @client)
|
|
36
|
+
|
|
37
|
+
i_should_see_translated_error_message("unauthorized_client")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
scenario "allows the request when satisfies condition" do
|
|
41
|
+
@client.update(name: "admin")
|
|
42
|
+
|
|
43
|
+
visit authorization_endpoint_url(client: @client)
|
|
44
|
+
i_should_not_see_translated_error_message("unauthorized_client")
|
|
45
|
+
click_on "Authorize"
|
|
46
|
+
|
|
47
|
+
authorization_code = Doorkeeper::AccessGrant.first.token
|
|
48
|
+
create_access_token authorization_code, @client
|
|
49
|
+
|
|
50
|
+
access_token_should_exist_for(@client, @resource_owner)
|
|
51
|
+
|
|
52
|
+
should_not_have_json "error"
|
|
53
|
+
|
|
54
|
+
should_have_json "access_token", Doorkeeper::AccessToken.first.token
|
|
55
|
+
should_have_json "token_type", "Bearer"
|
|
56
|
+
should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
26
60
|
context "with grant hashing enabled" do
|
|
27
61
|
background do
|
|
28
62
|
config_is_set(:token_secret_strategy, ::Doorkeeper::SecretStoring::Sha256Hash)
|
|
29
63
|
end
|
|
30
64
|
|
|
31
|
-
|
|
32
|
-
@client.redirect_uri =
|
|
65
|
+
def authorize(redirect_url)
|
|
66
|
+
@client.redirect_uri = redirect_url
|
|
33
67
|
@client.save!
|
|
34
68
|
visit authorization_endpoint_url(client: @client)
|
|
35
69
|
click_on "Authorize"
|
|
@@ -42,16 +76,28 @@ feature "Authorization Code Flow" do
|
|
|
42
76
|
hashed_code = Doorkeeper::AccessGrant.secret_strategy.transform_secret code
|
|
43
77
|
expect(hashed_code).to eq Doorkeeper::AccessGrant.first.token
|
|
44
78
|
|
|
79
|
+
[code, hashed_code]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
scenario "using redirect_url urn:ietf:wg:oauth:2.0:oob" do
|
|
83
|
+
code, hashed_code = authorize("urn:ietf:wg:oauth:2.0:oob")
|
|
45
84
|
expect(code).not_to eq(hashed_code)
|
|
85
|
+
i_should_see "Authorization code:"
|
|
86
|
+
i_should_see code
|
|
87
|
+
i_should_not_see hashed_code
|
|
88
|
+
end
|
|
46
89
|
|
|
90
|
+
scenario "using redirect_url urn:ietf:wg:oauth:2.0:oob:auto" do
|
|
91
|
+
code, hashed_code = authorize("urn:ietf:wg:oauth:2.0:oob:auto")
|
|
92
|
+
expect(code).not_to eq(hashed_code)
|
|
47
93
|
i_should_see "Authorization code:"
|
|
48
94
|
i_should_see code
|
|
49
95
|
i_should_not_see hashed_code
|
|
50
96
|
end
|
|
51
97
|
end
|
|
52
98
|
|
|
53
|
-
scenario "resource owner authorizes using
|
|
54
|
-
@client.redirect_uri =
|
|
99
|
+
scenario "resource owner authorizes using oob url" do
|
|
100
|
+
@client.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
|
|
55
101
|
@client.save!
|
|
56
102
|
visit authorization_endpoint_url(client: @client)
|
|
57
103
|
click_on "Authorize"
|
|
@@ -71,6 +117,17 @@ feature "Authorization Code Flow" do
|
|
|
71
117
|
url_should_not_have_param("code_challenge_method")
|
|
72
118
|
end
|
|
73
119
|
|
|
120
|
+
scenario "resource owner requests an access token without authorization code" do
|
|
121
|
+
create_access_token "", @client
|
|
122
|
+
|
|
123
|
+
access_token_should_not_exist
|
|
124
|
+
|
|
125
|
+
expect(Doorkeeper::AccessToken.count).to be_zero
|
|
126
|
+
|
|
127
|
+
should_have_json "error", "invalid_request"
|
|
128
|
+
should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code)
|
|
129
|
+
end
|
|
130
|
+
|
|
74
131
|
scenario "resource owner requests an access token with authorization code" do
|
|
75
132
|
visit authorization_endpoint_url(client: @client)
|
|
76
133
|
click_on "Authorize"
|
|
@@ -92,12 +149,15 @@ feature "Authorization Code Flow" do
|
|
|
92
149
|
click_on "Authorize"
|
|
93
150
|
|
|
94
151
|
authorization_code = Doorkeeper::AccessGrant.first.token
|
|
95
|
-
page.driver.post token_endpoint_url(
|
|
96
|
-
|
|
152
|
+
page.driver.post token_endpoint_url(
|
|
153
|
+
code: authorization_code, client_id: @client.uid,
|
|
154
|
+
redirect_uri: @client.redirect_uri,
|
|
155
|
+
)
|
|
97
156
|
|
|
98
157
|
expect(Doorkeeper::AccessToken.count).to be_zero
|
|
99
158
|
|
|
100
159
|
should_have_json "error", "invalid_client"
|
|
160
|
+
should_have_json "error_description", translated_error_message(:invalid_client)
|
|
101
161
|
end
|
|
102
162
|
|
|
103
163
|
scenario "resource owner requests an access token with authorization code but without client id" do
|
|
@@ -105,12 +165,15 @@ feature "Authorization Code Flow" do
|
|
|
105
165
|
click_on "Authorize"
|
|
106
166
|
|
|
107
167
|
authorization_code = Doorkeeper::AccessGrant.first.token
|
|
108
|
-
page.driver.post token_endpoint_url(
|
|
109
|
-
|
|
168
|
+
page.driver.post token_endpoint_url(
|
|
169
|
+
code: authorization_code, client_secret: @client.secret,
|
|
170
|
+
redirect_uri: @client.redirect_uri,
|
|
171
|
+
)
|
|
110
172
|
|
|
111
173
|
expect(Doorkeeper::AccessToken.count).to be_zero
|
|
112
174
|
|
|
113
175
|
should_have_json "error", "invalid_client"
|
|
176
|
+
should_have_json "error_description", translated_error_message(:invalid_client)
|
|
114
177
|
end
|
|
115
178
|
|
|
116
179
|
scenario "silently authorizes if matching token exists" do
|
|
@@ -136,7 +199,7 @@ feature "Authorization Code Flow" do
|
|
|
136
199
|
visit authorization_endpoint_url(
|
|
137
200
|
client: @client,
|
|
138
201
|
code_challenge: code_challenge,
|
|
139
|
-
code_challenge_method: "plain"
|
|
202
|
+
code_challenge_method: "plain",
|
|
140
203
|
)
|
|
141
204
|
click_on "Authorize"
|
|
142
205
|
|
|
@@ -153,13 +216,14 @@ feature "Authorization Code Flow" do
|
|
|
153
216
|
create_access_token authorization_code, @client, code_verifier
|
|
154
217
|
|
|
155
218
|
should_have_json "error", "invalid_grant"
|
|
219
|
+
should_have_json "error_description", translated_error_message(:invalid_grant)
|
|
156
220
|
end
|
|
157
221
|
|
|
158
222
|
scenario "mobile app requests an access token with authorization code and plain code challenge method" do
|
|
159
223
|
visit authorization_endpoint_url(
|
|
160
224
|
client: @client,
|
|
161
225
|
code_challenge: code_challenge,
|
|
162
|
-
code_challenge_method: "plain"
|
|
226
|
+
code_challenge_method: "plain",
|
|
163
227
|
)
|
|
164
228
|
click_on "Authorize"
|
|
165
229
|
|
|
@@ -175,17 +239,36 @@ feature "Authorization Code Flow" do
|
|
|
175
239
|
should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
|
|
176
240
|
end
|
|
177
241
|
|
|
178
|
-
scenario "mobile app requests an access token with authorization code
|
|
179
|
-
visit authorization_endpoint_url(
|
|
180
|
-
|
|
181
|
-
|
|
242
|
+
scenario "mobile app requests an access token with authorization code but without code_verifier" do
|
|
243
|
+
visit authorization_endpoint_url(
|
|
244
|
+
client: @client,
|
|
245
|
+
code_challenge: code_challenge,
|
|
246
|
+
code_challenge_method: "plain",
|
|
247
|
+
)
|
|
248
|
+
click_on "Authorize"
|
|
249
|
+
|
|
250
|
+
authorization_code = current_params["code"]
|
|
251
|
+
create_access_token authorization_code, @client, nil
|
|
252
|
+
|
|
253
|
+
should_not_have_json "access_token"
|
|
254
|
+
should_have_json "error", "invalid_request"
|
|
255
|
+
should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code_verifier)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
scenario "mobile app requests an access token with authorization code with wrong code_verifier" do
|
|
259
|
+
visit authorization_endpoint_url(
|
|
260
|
+
client: @client,
|
|
261
|
+
code_challenge: code_challenge,
|
|
262
|
+
code_challenge_method: "plain",
|
|
263
|
+
)
|
|
182
264
|
click_on "Authorize"
|
|
183
265
|
|
|
184
266
|
authorization_code = current_params["code"]
|
|
185
|
-
create_access_token authorization_code, @client,
|
|
267
|
+
create_access_token authorization_code, @client, "wrong_code_verifier"
|
|
186
268
|
|
|
187
269
|
should_not_have_json "access_token"
|
|
188
270
|
should_have_json "error", "invalid_grant"
|
|
271
|
+
should_have_json "error_description", translated_error_message(:invalid_grant)
|
|
189
272
|
end
|
|
190
273
|
end
|
|
191
274
|
|
|
@@ -197,7 +280,7 @@ feature "Authorization Code Flow" do
|
|
|
197
280
|
visit authorization_endpoint_url(
|
|
198
281
|
client: @client,
|
|
199
282
|
code_challenge: code_challenge,
|
|
200
|
-
code_challenge_method: "S256"
|
|
283
|
+
code_challenge_method: "S256",
|
|
201
284
|
)
|
|
202
285
|
click_on "Authorize"
|
|
203
286
|
|
|
@@ -210,7 +293,7 @@ feature "Authorization Code Flow" do
|
|
|
210
293
|
visit authorization_endpoint_url(
|
|
211
294
|
client: @client,
|
|
212
295
|
code_challenge: code_challenge,
|
|
213
|
-
code_challenge_method: "S256"
|
|
296
|
+
code_challenge_method: "S256",
|
|
214
297
|
)
|
|
215
298
|
click_on "Authorize"
|
|
216
299
|
|
|
@@ -226,32 +309,24 @@ feature "Authorization Code Flow" do
|
|
|
226
309
|
should_have_json_within "expires_in", Doorkeeper::AccessToken.first.expires_in, 1
|
|
227
310
|
end
|
|
228
311
|
|
|
229
|
-
scenario "mobile app requests an access token with authorization code and without code_verifier" do
|
|
230
|
-
visit authorization_endpoint_url(
|
|
231
|
-
client: @client,
|
|
232
|
-
code_challenge: code_challenge,
|
|
233
|
-
code_challenge_method: "S256"
|
|
234
|
-
)
|
|
235
|
-
click_on "Authorize"
|
|
236
|
-
authorization_code = current_params["code"]
|
|
237
|
-
create_access_token authorization_code, @client
|
|
238
|
-
should_have_json "error", "invalid_request"
|
|
239
|
-
should_not_have_json "access_token"
|
|
240
|
-
end
|
|
241
|
-
|
|
242
312
|
scenario "mobile app requests an access token with authorization code and without secret" do
|
|
243
313
|
visit authorization_endpoint_url(
|
|
244
314
|
client: @client,
|
|
245
315
|
code_challenge: code_challenge,
|
|
246
|
-
code_challenge_method: "S256"
|
|
316
|
+
code_challenge_method: "S256",
|
|
247
317
|
)
|
|
248
318
|
click_on "Authorize"
|
|
249
319
|
|
|
250
320
|
authorization_code = current_params["code"]
|
|
251
|
-
page.driver.post token_endpoint_url(
|
|
252
|
-
|
|
253
|
-
|
|
321
|
+
page.driver.post token_endpoint_url(
|
|
322
|
+
code: authorization_code,
|
|
323
|
+
client_id: @client.uid,
|
|
324
|
+
redirect_uri: @client.redirect_uri,
|
|
325
|
+
code_verifier: code_verifier,
|
|
326
|
+
)
|
|
254
327
|
should_not_have_json "access_token"
|
|
328
|
+
should_have_json "error", "invalid_client"
|
|
329
|
+
should_have_json "error_description", translated_error_message(:invalid_client)
|
|
255
330
|
end
|
|
256
331
|
|
|
257
332
|
scenario "mobile app requests an access token with authorization code and without secret but is marked as not confidential" do
|
|
@@ -264,7 +339,7 @@ feature "Authorization Code Flow" do
|
|
|
264
339
|
code: authorization_code,
|
|
265
340
|
client_id: @client.uid,
|
|
266
341
|
redirect_uri: @client.redirect_uri,
|
|
267
|
-
code_verifier: code_verifier
|
|
342
|
+
code_verifier: code_verifier,
|
|
268
343
|
)
|
|
269
344
|
should_not_have_json "error"
|
|
270
345
|
|
|
@@ -277,7 +352,7 @@ feature "Authorization Code Flow" do
|
|
|
277
352
|
visit authorization_endpoint_url(
|
|
278
353
|
client: @client,
|
|
279
354
|
code_challenge: code_challenge,
|
|
280
|
-
code_challenge_method: "S256"
|
|
355
|
+
code_challenge_method: "S256",
|
|
281
356
|
)
|
|
282
357
|
click_on "Authorize"
|
|
283
358
|
|
|
@@ -286,13 +361,14 @@ feature "Authorization Code Flow" do
|
|
|
286
361
|
|
|
287
362
|
should_not_have_json "access_token"
|
|
288
363
|
should_have_json "error", "invalid_request"
|
|
364
|
+
should_have_json "error_description", translated_invalid_request_error_message(:missing_param, :code_verifier)
|
|
289
365
|
end
|
|
290
366
|
|
|
291
367
|
scenario "mobile app requests an access token with authorization code with wrong verifier" do
|
|
292
368
|
visit authorization_endpoint_url(
|
|
293
369
|
client: @client,
|
|
294
370
|
code_challenge: code_challenge,
|
|
295
|
-
code_challenge_method: "S256"
|
|
371
|
+
code_challenge_method: "S256",
|
|
296
372
|
)
|
|
297
373
|
click_on "Authorize"
|
|
298
374
|
|
|
@@ -301,13 +377,14 @@ feature "Authorization Code Flow" do
|
|
|
301
377
|
|
|
302
378
|
should_not_have_json "access_token"
|
|
303
379
|
should_have_json "error", "invalid_grant"
|
|
380
|
+
should_have_json "error_description", translated_error_message(:invalid_grant)
|
|
304
381
|
end
|
|
305
382
|
|
|
306
383
|
scenario "code_challenge_mehthod in token request is totally ignored" do
|
|
307
384
|
visit authorization_endpoint_url(
|
|
308
385
|
client: @client,
|
|
309
386
|
code_challenge: code_challenge,
|
|
310
|
-
code_challenge_method: "S256"
|
|
387
|
+
code_challenge_method: "S256",
|
|
311
388
|
)
|
|
312
389
|
click_on "Authorize"
|
|
313
390
|
|
|
@@ -316,11 +393,12 @@ feature "Authorization Code Flow" do
|
|
|
316
393
|
code: authorization_code,
|
|
317
394
|
client: @client,
|
|
318
395
|
code_verifier: code_challenge,
|
|
319
|
-
code_challenge_method: "plain"
|
|
396
|
+
code_challenge_method: "plain",
|
|
320
397
|
)
|
|
321
398
|
|
|
322
399
|
should_not_have_json "access_token"
|
|
323
400
|
should_have_json "error", "invalid_grant"
|
|
401
|
+
should_have_json "error_description", translated_error_message(:invalid_grant)
|
|
324
402
|
end
|
|
325
403
|
|
|
326
404
|
scenario "expects to set code_challenge_method explicitely without fallback" do
|
|
@@ -332,16 +410,15 @@ feature "Authorization Code Flow" do
|
|
|
332
410
|
|
|
333
411
|
context "when application scopes are present and no scope is passed" do
|
|
334
412
|
background do
|
|
335
|
-
@client.update(scopes: "public write read")
|
|
413
|
+
@client.update(scopes: "public write read default")
|
|
336
414
|
end
|
|
337
415
|
|
|
338
|
-
scenario "
|
|
416
|
+
scenario "scope is invalid because default scope is different from application scope" do
|
|
339
417
|
default_scopes_exist :admin
|
|
340
418
|
visit authorization_endpoint_url(client: @client)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
expect(grant.scopes).to be_empty
|
|
419
|
+
response_status_should_be 200
|
|
420
|
+
i_should_not_see "Authorize"
|
|
421
|
+
i_should_see_translated_error_message :invalid_scope
|
|
345
422
|
end
|
|
346
423
|
|
|
347
424
|
scenario "access grant have scopes which are common in application scopees and default scopes" do
|
|
@@ -442,6 +519,7 @@ describe "Authorization Code Flow" do
|
|
|
442
519
|
|
|
443
520
|
should_not_have_json "access_token"
|
|
444
521
|
should_have_json "error", "invalid_grant"
|
|
522
|
+
should_have_json "error_description", translated_error_message(:invalid_grant)
|
|
445
523
|
end
|
|
446
524
|
end
|
|
447
525
|
end
|
|
@@ -66,6 +66,44 @@ describe "Client Credentials Request" do
|
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
+
context "when configured to check application supported grant flow" do
|
|
70
|
+
before do
|
|
71
|
+
Doorkeeper.configuration.instance_variable_set(
|
|
72
|
+
:@allow_grant_flow_for_client,
|
|
73
|
+
->(_grant_flow, client) { client.name == "admin" },
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
scenario "forbids the request when doesn't satisfy condition" do
|
|
78
|
+
client.update(name: "sample app")
|
|
79
|
+
|
|
80
|
+
headers = authorization client.uid, client.secret
|
|
81
|
+
params = { grant_type: "client_credentials" }
|
|
82
|
+
|
|
83
|
+
post "/oauth/token", params: params, headers: headers
|
|
84
|
+
|
|
85
|
+
should_have_json "error", "unauthorized_client"
|
|
86
|
+
should_have_json "error_description", translated_error_message(:unauthorized_client)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
scenario "allows the request when satisfies condition" do
|
|
90
|
+
client.update(name: "admin")
|
|
91
|
+
|
|
92
|
+
headers = authorization client.uid, client.secret
|
|
93
|
+
params = { grant_type: "client_credentials" }
|
|
94
|
+
|
|
95
|
+
post "/oauth/token", params: params, headers: headers
|
|
96
|
+
|
|
97
|
+
should_have_json "access_token", Doorkeeper::AccessToken.first.token
|
|
98
|
+
should_have_json_within "expires_in", Doorkeeper.configuration.access_token_expires_in, 1
|
|
99
|
+
should_not_have_json "scope"
|
|
100
|
+
should_not_have_json "refresh_token"
|
|
101
|
+
|
|
102
|
+
should_not_have_json "error"
|
|
103
|
+
should_not_have_json "error_description"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
69
107
|
context "when application scopes contain some of the default scopes and no scope is passed" do
|
|
70
108
|
before do
|
|
71
109
|
client.update(scopes: "read write public")
|
|
@@ -4,6 +4,7 @@ require "spec_helper"
|
|
|
4
4
|
|
|
5
5
|
feature "Implicit Grant Flow Errors" do
|
|
6
6
|
background do
|
|
7
|
+
default_scopes_exist :default
|
|
7
8
|
config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
|
|
8
9
|
config_is_set(:grant_flows, ["implicit"])
|
|
9
10
|
client_exists
|
|
@@ -15,20 +16,31 @@ feature "Implicit Grant Flow Errors" do
|
|
|
15
16
|
access_token_should_not_exist
|
|
16
17
|
end
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
].each do |error|
|
|
22
|
-
scenario "displays #{error.last} error for invalid #{error.first}" do
|
|
23
|
-
visit authorization_endpoint_url(client: @client, error.first => "invalid", response_type: "token")
|
|
19
|
+
context "when validate client_id param" do
|
|
20
|
+
scenario "displays invalid_client error for invalid client_id" do
|
|
21
|
+
visit authorization_endpoint_url(client_id: "invalid", response_type: "token")
|
|
24
22
|
i_should_not_see "Authorize"
|
|
25
|
-
i_should_see_translated_error_message
|
|
23
|
+
i_should_see_translated_error_message :invalid_client
|
|
26
24
|
end
|
|
27
25
|
|
|
28
|
-
scenario "displays
|
|
29
|
-
visit authorization_endpoint_url(
|
|
26
|
+
scenario "displays invalid_request error when client_id is missing" do
|
|
27
|
+
visit authorization_endpoint_url(client_id: "", response_type: "token")
|
|
30
28
|
i_should_not_see "Authorize"
|
|
31
|
-
|
|
29
|
+
i_should_see_translated_invalid_request_error_message :missing_param, :client_id
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
context "when validate redirect_uri param" do
|
|
34
|
+
scenario "displays invalid_redirect_uri error for invalid redirect_uri" do
|
|
35
|
+
visit authorization_endpoint_url(client: @client, redirect_uri: "invalid", response_type: "token")
|
|
36
|
+
i_should_not_see "Authorize"
|
|
37
|
+
i_should_see_translated_error_message :invalid_redirect_uri
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
scenario "displays invalid_redirect_uri error when redirect_uri is missing" do
|
|
41
|
+
visit authorization_endpoint_url(client: @client, redirect_uri: "", response_type: "token")
|
|
42
|
+
i_should_not_see "Authorize"
|
|
43
|
+
i_should_see_translated_error_message :invalid_redirect_uri
|
|
32
44
|
end
|
|
33
45
|
end
|
|
34
46
|
end
|
|
@@ -4,6 +4,7 @@ require "spec_helper"
|
|
|
4
4
|
|
|
5
5
|
feature "Implicit Grant Flow (feature spec)" do
|
|
6
6
|
background do
|
|
7
|
+
default_scopes_exist :default
|
|
7
8
|
config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
|
|
8
9
|
config_is_set(:grant_flows, ["implicit"])
|
|
9
10
|
client_exists
|
|
@@ -25,16 +26,15 @@ feature "Implicit Grant Flow (feature spec)" do
|
|
|
25
26
|
@client.update(scopes: "public write read")
|
|
26
27
|
end
|
|
27
28
|
|
|
28
|
-
scenario "
|
|
29
|
+
scenario "scope is invalid because default scope is different from application scope" do
|
|
29
30
|
default_scopes_exist :admin
|
|
30
31
|
visit authorization_endpoint_url(client: @client, response_type: "token")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
expect(token.scopes).to be_empty
|
|
32
|
+
response_status_should_be 200
|
|
33
|
+
i_should_not_see "Authorize"
|
|
34
|
+
i_should_see_translated_error_message :invalid_scope
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
scenario "access token has scopes which are common in application
|
|
37
|
+
scenario "access token has scopes which are common in application scopes and default scopes" do
|
|
38
38
|
default_scopes_exist :public, :write
|
|
39
39
|
visit authorization_endpoint_url(client: @client, response_type: "token")
|
|
40
40
|
click_on "Authorize"
|
|
@@ -46,6 +46,7 @@ end
|
|
|
46
46
|
|
|
47
47
|
describe "Implicit Grant Flow (request spec)" do
|
|
48
48
|
before do
|
|
49
|
+
default_scopes_exist :default
|
|
49
50
|
config_is_set(:authenticate_resource_owner) { User.first || redirect_to("/sign_in") }
|
|
50
51
|
config_is_set(:grant_flows, ["implicit"])
|
|
51
52
|
client_exists
|
|
@@ -56,7 +57,7 @@ describe "Implicit Grant Flow (request spec)" do
|
|
|
56
57
|
it "should return a new token each request" do
|
|
57
58
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(false)
|
|
58
59
|
|
|
59
|
-
token = client_is_authorized(@client, @resource_owner)
|
|
60
|
+
token = client_is_authorized(@client, @resource_owner, scopes: "default")
|
|
60
61
|
|
|
61
62
|
post "/oauth/authorize",
|
|
62
63
|
params: {
|
|
@@ -73,7 +74,7 @@ describe "Implicit Grant Flow (request spec)" do
|
|
|
73
74
|
it "should return the same token if it is still accessible" do
|
|
74
75
|
allow(Doorkeeper.configuration).to receive(:reuse_access_token).and_return(true)
|
|
75
76
|
|
|
76
|
-
token = client_is_authorized(@client, @resource_owner)
|
|
77
|
+
token = client_is_authorized(@client, @resource_owner, scopes: "default")
|
|
77
78
|
|
|
78
79
|
post "/oauth/authorize",
|
|
79
80
|
params: {
|
|
@@ -31,6 +31,43 @@ describe "Resource Owner Password Credentials Flow" do
|
|
|
31
31
|
context "with non-confidential/public client" do
|
|
32
32
|
let(:client_attributes) { { confidential: false } }
|
|
33
33
|
|
|
34
|
+
context "when configured to check application supported grant flow" do
|
|
35
|
+
before do
|
|
36
|
+
Doorkeeper.configuration.instance_variable_set(
|
|
37
|
+
:@allow_grant_flow_for_client,
|
|
38
|
+
->(_grant_flow, client) { client.name == "admin" },
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
scenario "forbids the request when doesn't satisfy condition" do
|
|
43
|
+
@client.update(name: "sample app")
|
|
44
|
+
|
|
45
|
+
expect do
|
|
46
|
+
post password_token_endpoint_url(
|
|
47
|
+
client_id: @client.uid,
|
|
48
|
+
client_secret: "foobar",
|
|
49
|
+
resource_owner: @resource_owner,
|
|
50
|
+
)
|
|
51
|
+
end.not_to(change { Doorkeeper::AccessToken.count })
|
|
52
|
+
|
|
53
|
+
expect(response.status).to eq(401)
|
|
54
|
+
should_have_json "error", "invalid_client"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
scenario "allows the request when satisfies condition" do
|
|
58
|
+
@client.update(name: "admin")
|
|
59
|
+
|
|
60
|
+
expect do
|
|
61
|
+
post password_token_endpoint_url(client_id: @client.uid, resource_owner: @resource_owner)
|
|
62
|
+
end.to change { Doorkeeper::AccessToken.count }.by(1)
|
|
63
|
+
|
|
64
|
+
token = Doorkeeper::AccessToken.first
|
|
65
|
+
|
|
66
|
+
expect(token.application_id).to eq @client.id
|
|
67
|
+
should_have_json "access_token", token.token
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
34
71
|
context "when client_secret absent" do
|
|
35
72
|
it "should issue new token" do
|
|
36
73
|
expect do
|
|
@@ -62,7 +99,7 @@ describe "Resource Owner Password Credentials Flow" do
|
|
|
62
99
|
post password_token_endpoint_url(
|
|
63
100
|
client_id: @client.uid,
|
|
64
101
|
client_secret: "foobar",
|
|
65
|
-
resource_owner: @resource_owner
|
|
102
|
+
resource_owner: @resource_owner,
|
|
66
103
|
)
|
|
67
104
|
end.not_to(change { Doorkeeper::AccessToken.count })
|
|
68
105
|
|
|
@@ -204,9 +241,11 @@ describe "Resource Owner Password Credentials Flow" do
|
|
|
204
241
|
|
|
205
242
|
context "with invalid scopes" do
|
|
206
243
|
subject do
|
|
207
|
-
post password_token_endpoint_url(
|
|
208
|
-
|
|
209
|
-
|
|
244
|
+
post password_token_endpoint_url(
|
|
245
|
+
client: @client,
|
|
246
|
+
resource_owner: @resource_owner,
|
|
247
|
+
scope: "random",
|
|
248
|
+
)
|
|
210
249
|
end
|
|
211
250
|
|
|
212
251
|
it "should not issue new token" do
|
|
@@ -226,9 +265,11 @@ describe "Resource Owner Password Credentials Flow" do
|
|
|
226
265
|
context "with invalid user credentials" do
|
|
227
266
|
it "should not issue new token with bad password" do
|
|
228
267
|
expect do
|
|
229
|
-
post password_token_endpoint_url(
|
|
230
|
-
|
|
231
|
-
|
|
268
|
+
post password_token_endpoint_url(
|
|
269
|
+
client: @client,
|
|
270
|
+
resource_owner_username: @resource_owner.name,
|
|
271
|
+
resource_owner_password: "wrongpassword",
|
|
272
|
+
)
|
|
232
273
|
end.to_not(change { Doorkeeper::AccessToken.count })
|
|
233
274
|
end
|
|
234
275
|
|
|
@@ -237,14 +278,30 @@ describe "Resource Owner Password Credentials Flow" do
|
|
|
237
278
|
post password_token_endpoint_url(client: @client)
|
|
238
279
|
end.to_not(change { Doorkeeper::AccessToken.count })
|
|
239
280
|
end
|
|
281
|
+
|
|
282
|
+
it "should not issue new token if resource_owner_from_credentials returned false or nil" do
|
|
283
|
+
config_is_set(:resource_owner_from_credentials) { false }
|
|
284
|
+
|
|
285
|
+
expect do
|
|
286
|
+
post password_token_endpoint_url(client: @client)
|
|
287
|
+
end.to_not(change { Doorkeeper::AccessToken.count })
|
|
288
|
+
|
|
289
|
+
config_is_set(:resource_owner_from_credentials) { nil }
|
|
290
|
+
|
|
291
|
+
expect do
|
|
292
|
+
post password_token_endpoint_url(client: @client)
|
|
293
|
+
end.to_not(change { Doorkeeper::AccessToken.count })
|
|
294
|
+
end
|
|
240
295
|
end
|
|
241
296
|
|
|
242
297
|
context "with invalid confidential client credentials" do
|
|
243
298
|
it "should not issue new token with bad client credentials" do
|
|
244
299
|
expect do
|
|
245
|
-
post password_token_endpoint_url(
|
|
246
|
-
|
|
247
|
-
|
|
300
|
+
post password_token_endpoint_url(
|
|
301
|
+
client_id: @client.uid,
|
|
302
|
+
client_secret: "bad_secret",
|
|
303
|
+
resource_owner: @resource_owner,
|
|
304
|
+
)
|
|
248
305
|
end.to_not(change { Doorkeeper::AccessToken.count })
|
|
249
306
|
end
|
|
250
307
|
end
|