doorkeeper 5.2.0.rc1 → 5.2.0

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.

Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +1 -1
  3. data/CHANGELOG.md +33 -2
  4. data/CONTRIBUTING.md +7 -0
  5. data/Dangerfile +1 -1
  6. data/Dockerfile +29 -0
  7. data/Gemfile +1 -1
  8. data/README.md +9 -1
  9. data/app/controllers/doorkeeper/application_controller.rb +1 -1
  10. data/app/controllers/doorkeeper/application_metal_controller.rb +2 -1
  11. data/app/controllers/doorkeeper/authorizations_controller.rb +14 -7
  12. data/app/controllers/doorkeeper/tokens_controller.rb +14 -1
  13. data/config/locales/en.yml +5 -1
  14. data/doorkeeper.gemspec +8 -0
  15. data/gemfiles/rails_6_0.gemfile +1 -1
  16. data/lib/doorkeeper/config.rb +64 -9
  17. data/lib/doorkeeper/errors.rb +13 -18
  18. data/lib/doorkeeper/helpers/controller.rb +6 -2
  19. data/lib/doorkeeper/models/access_token_mixin.rb +43 -2
  20. data/lib/doorkeeper/oauth/authorization/code.rb +1 -5
  21. data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -9
  22. data/lib/doorkeeper/oauth/base_request.rb +2 -0
  23. data/lib/doorkeeper/oauth/client_credentials/creator.rb +14 -0
  24. data/lib/doorkeeper/oauth/client_credentials/validation.rb +8 -0
  25. data/lib/doorkeeper/oauth/code_request.rb +5 -11
  26. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  27. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
  28. data/lib/doorkeeper/oauth/pre_authorization.rb +70 -37
  29. data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -2
  30. data/lib/doorkeeper/oauth/token_introspection.rb +16 -7
  31. data/lib/doorkeeper/oauth/token_request.rb +4 -18
  32. data/lib/doorkeeper/orm/active_record/application.rb +1 -1
  33. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
  34. data/lib/doorkeeper/orm/active_record.rb +2 -2
  35. data/lib/doorkeeper/request/authorization_code.rb +2 -0
  36. data/lib/doorkeeper/request.rb +6 -11
  37. data/lib/doorkeeper/server.rb +2 -6
  38. data/lib/doorkeeper/version.rb +1 -1
  39. data/lib/doorkeeper.rb +1 -0
  40. data/lib/generators/doorkeeper/templates/initializer.rb +88 -43
  41. data/lib/generators/doorkeeper/templates/migration.rb.erb +1 -1
  42. data/spec/controllers/authorizations_controller_spec.rb +140 -61
  43. data/spec/controllers/protected_resources_controller_spec.rb +3 -3
  44. data/spec/controllers/tokens_controller_spec.rb +140 -40
  45. data/spec/dummy/config/initializers/doorkeeper.rb +47 -20
  46. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +1 -1
  47. data/spec/lib/config_spec.rb +32 -1
  48. data/spec/lib/oauth/authorization_code_request_spec.rb +11 -1
  49. data/spec/lib/oauth/base_request_spec.rb +33 -16
  50. data/spec/lib/oauth/client_credentials/creator_spec.rb +3 -0
  51. data/spec/lib/oauth/code_request_spec.rb +27 -28
  52. data/spec/lib/oauth/invalid_request_response_spec.rb +75 -0
  53. data/spec/lib/oauth/pre_authorization_spec.rb +80 -55
  54. data/spec/lib/oauth/refresh_token_request_spec.rb +1 -0
  55. data/spec/lib/oauth/token_request_spec.rb +20 -17
  56. data/spec/lib/server_spec.rb +0 -12
  57. data/spec/requests/endpoints/authorization_spec.rb +21 -5
  58. data/spec/requests/endpoints/token_spec.rb +1 -1
  59. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
  60. data/spec/requests/flows/authorization_code_spec.rb +77 -23
  61. data/spec/requests/flows/client_credentials_spec.rb +38 -0
  62. data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
  63. data/spec/requests/flows/implicit_grant_spec.rb +9 -8
  64. data/spec/requests/flows/password_spec.rb +37 -0
  65. data/spec/requests/flows/refresh_token_spec.rb +1 -1
  66. data/spec/support/helpers/request_spec_helper.rb +14 -2
  67. data/spec/validators/redirect_uri_validator_spec.rb +1 -1
  68. metadata +15 -6
  69. data/app/validators/redirect_uri_validator.rb +0 -60
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Doorkeeper.configure do
4
- # Change the ORM that doorkeeper will use (needs plugins)
4
+ # Change the ORM that doorkeeper will use (requires ORM extensions installed).
5
+ # Check the list of supported ORMs here: https://github.com/doorkeeper-gem/doorkeeper#orms
5
6
  orm :active_record
6
7
 
7
8
  # This block will be called to check whether the resource owner is authenticated or not.
@@ -9,7 +10,7 @@ Doorkeeper.configure do
9
10
  raise "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}"
10
11
  # Put your resource owner authentication logic here.
11
12
  # Example implementation:
12
- # User.find_by_id(session[:user_id]) || redirect_to(new_user_session_url)
13
+ # User.find_by(id: session[:user_id]) || redirect_to(new_user_session_url)
13
14
  end
14
15
 
15
16
  # If you didn't skip applications controller from Doorkeeper routes in your application routes.rb
@@ -39,18 +40,18 @@ Doorkeeper.configure do
39
40
  #
40
41
  # enforce_content_type
41
42
 
42
- # Authorization Code expiration time (default 10 minutes).
43
+ # Authorization Code expiration time (default: 10 minutes).
43
44
  #
44
45
  # authorization_code_expires_in 10.minutes
45
46
 
46
- # Access token expiration time (default 2 hours).
47
- # If you want to disable expiration, set this to nil.
47
+ # Access token expiration time (default: 2 hours).
48
+ # If you want to disable expiration, set this to `nil`.
48
49
  #
49
50
  # access_token_expires_in 2.hours
50
51
 
51
52
  # Assign custom TTL for access tokens. Will be used instead of access_token_expires_in
52
53
  # option if defined. In case the block returns `nil` value Doorkeeper fallbacks to
53
- # `access_token_expires_in` configuration option value. If you really need to issue a
54
+ # +access_token_expires_in+ configuration option value. If you really need to issue a
54
55
  # non-expiring access token (which is not recommended) then you need to return
55
56
  # Float::INFINITY from this block.
56
57
  #
@@ -65,12 +66,13 @@ Doorkeeper.configure do
65
66
  # end
66
67
 
67
68
  # Use a custom class for generating the access token.
68
- # See https://github.com/doorkeeper-gem/doorkeeper#custom-access-token-generator
69
+ # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-access-token-generator
69
70
  #
70
71
  # access_token_generator '::Doorkeeper::JWT'
71
72
 
72
- # The controller Doorkeeper::ApplicationController inherits from.
73
- # Defaults to ActionController::Base.
73
+ # The controller +Doorkeeper::ApplicationController+ inherits from.
74
+ # Defaults to +ActionController::Base+ unless +api_only+ is set, which changes the default to
75
+ # +ActionController::API+. The return value of this option must be a stringified class name.
74
76
  # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-base-controller
75
77
  #
76
78
  # base_controller 'ApplicationController'
@@ -128,11 +130,10 @@ Doorkeeper.configure do
128
130
  #
129
131
  # hash_application_secrets using: '::Doorkeeper::SecretStoring::BCrypt'
130
132
 
131
- # When the above option is enabled,
132
- # and a hashed token or secret is not found,
133
- # you can allow to fall back to another strategy.
134
- # For users upgrading doorkeeper and wishing to enable hashing,
135
- # you will probably want to enable the fallback to plain tokens.
133
+ # When the above option is enabled, and a hashed token or secret is not found,
134
+ # you can allow to fall back to another strategy. For users upgrading
135
+ # doorkeeper and wishing to enable hashing, you will probably want to enable
136
+ # the fallback to plain tokens.
136
137
  #
137
138
  # This will ensure that old access tokens and secrets
138
139
  # will remain valid even if the hashing above is enabled.
@@ -141,8 +142,8 @@ Doorkeeper.configure do
141
142
 
142
143
  # Issue access tokens with refresh token (disabled by default), you may also
143
144
  # pass a block which accepts `context` to customize when to give a refresh
144
- # token or not. Similar to `custom_access_token_expires_in`, `context` has
145
- # the properties:
145
+ # token or not. Similar to +custom_access_token_expires_in+, `context` has
146
+ # the following properties:
146
147
  #
147
148
  # `client` - the OAuth client application (see Doorkeeper::OAuth::Client)
148
149
  # `grant_type` - the grant type of the request (see Doorkeeper::OAuth)
@@ -151,7 +152,7 @@ Doorkeeper.configure do
151
152
  # use_refresh_token
152
153
 
153
154
  # Provide support for an owner to be assigned to each registered application (disabled by default)
154
- # Optional parameter confirmation: true (default false) if you want to enforce ownership of
155
+ # Optional parameter confirmation: true (default: false) if you want to enforce ownership of
155
156
  # a registered application
156
157
  # NOTE: you must also run the rails g doorkeeper:application_owner generator
157
158
  # to provide the necessary support
@@ -160,22 +161,22 @@ Doorkeeper.configure do
160
161
 
161
162
  # Define access token scopes for your provider
162
163
  # For more information go to
163
- # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
164
+ # https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes
164
165
  #
165
166
  # default_scopes :public
166
167
  # optional_scopes :write, :update
167
168
 
168
- # Define scopes_by_grant_type to restrict only certain scopes for grant_type
169
+ # Allows to restrict only certain scopes for grant_type.
169
170
  # By default, all the scopes will be available for all the grant types.
170
171
  #
171
172
  # Keys to this hash should be the name of grant_type and
172
173
  # values should be the array of scopes for that grant type.
173
- # Note: scopes should be from configured_scopes(i.e. deafult or optional)
174
+ # Note: scopes should be from configured_scopes (i.e. default or optional)
174
175
  #
175
176
  # scopes_by_grant_type password: [:write], client_credentials: [:update]
176
177
 
177
178
  # Forbids creating/updating applications with arbitrary scopes that are
178
- # not in configuration, i.e. `default_scopes` or `optional_scopes`.
179
+ # not in configuration, i.e. +default_scopes+ or +optional_scopes+.
179
180
  # (disabled by default)
180
181
  #
181
182
  # enforce_configured_scopes
@@ -237,7 +238,7 @@ Doorkeeper.configure do
237
238
  # is invalid, expired, revoked or has invalid scopes.
238
239
  #
239
240
  # If you want to render error response yourself (i.e. rescue exceptions),
240
- # set handle_auth_errors to `:raise` and rescue Doorkeeper::Errors::InvalidToken
241
+ # set +handle_auth_errors+ to `:raise` and rescue Doorkeeper::Errors::InvalidToken
241
242
  # or following specific errors:
242
243
  #
243
244
  # Doorkeeper::Errors::TokenForbidden, Doorkeeper::Errors::TokenExpired,
@@ -281,6 +282,37 @@ Doorkeeper.configure do
281
282
  #
282
283
  # grant_flows %w[authorization_code client_credentials]
283
284
 
285
+ # Allows to customize OAuth grant flows that +each+ application support.
286
+ # You can configure a custom block (or use a class respond to `#call`) that must
287
+ # return `true` in case Application instance supports requested OAuth grant flow
288
+ # during the authorization request to the server. This configuration +doesn't+
289
+ # set flows per application, it only allows to check if application supports
290
+ # specific grant flow.
291
+ #
292
+ # For example you can add an additional database column to `oauth_applications` table,
293
+ # say `t.array :grant_flows, default: []`, and store allowed grant flows that can
294
+ # be used with this application there. Then when authorization requested Doorkeeper
295
+ # will call this block to check if specific Application (passed with client_id and/or
296
+ # client_secret) is allowed to perform the request for the specific grant type
297
+ # (authorization, password, client_credentials, etc).
298
+ #
299
+ # Example of the block:
300
+ #
301
+ # ->(flow, client) { client.grant_flows.include?(flow) }
302
+ #
303
+ # In case this option invocation result is `false`, Doorkeeper server returns
304
+ # :unauthorized_client error and stops the request.
305
+ #
306
+ # @param allow_grant_flow_for_client [Proc] Block or any object respond to #call
307
+ # @return [Boolean] `true` if allow or `false` if forbid the request
308
+ #
309
+ # allow_grant_flow_for_client do |grant_flow, client|
310
+ # # `grant_flows` is an Array column with grant
311
+ # # flows that application supports
312
+ #
313
+ # client.grant_flows.include?(grant_flow)
314
+ # end
315
+
284
316
  # Hook into the strategies' request & response life-cycle in case your
285
317
  # application needs advanced customization or logging:
286
318
  #
@@ -296,7 +328,7 @@ Doorkeeper.configure do
296
328
  # or add any other functionality.
297
329
  #
298
330
  # before_successful_authorization do |controller|
299
- # Rails.logger.info(params.inspect)
331
+ # Rails.logger.info(controller.request.params.inspect)
300
332
  # end
301
333
  #
302
334
  # after_successful_authorization do |controller|
@@ -314,23 +346,13 @@ Doorkeeper.configure do
314
346
  # client.superapp? or resource_owner.admin?
315
347
  # end
316
348
 
317
- # Configure custom constraints for the Token Introspection request. By default
318
- # this configuration option allows to introspect the token that belongs to authorized
319
- # client (from Bearer token or from authenticated client) OR when token doesn't
349
+ # Configure custom constraints for the Token Introspection request.
350
+ # By default this configuration option allows to introspect a token by another
351
+ # token of the same application, OR to introspect the token that belongs to
352
+ # authorized client (from authenticated client) OR when token doesn't
320
353
  # belong to any client (public token). Otherwise requester has no access to the
321
354
  # introspection and it will return response as stated in the RFC.
322
355
  #
323
- # You can define your custom check:
324
- #
325
- # allow_token_introspection do |token, authorized_client, _authorized_token|
326
- # if token.application
327
- # # `protected_resource` is a new database boolean column, for example
328
- # token.application == authorized_client || authorized_client.protected_resource?
329
- # else
330
- # true
331
- # end
332
- # end
333
- #
334
356
  # Block arguments:
335
357
  #
336
358
  # @param token [Doorkeeper::AccessToken]
@@ -343,19 +365,42 @@ Doorkeeper.configure do
343
365
  # @param authorized_token [Doorkeeper::AccessToken]
344
366
  # Bearer token used to authorize the request
345
367
  #
346
- # Keep in mind, that in case the block returns `nil` or `false` introspection response
347
- # doesn't have 401 status code and some descriptive body, you'll get 200 with
348
- # { "active": false } body as stated in the RFC 7662 section 2.2. Introspection Response.
368
+ # In case the block returns `nil` or `false` introspection responses with 401 status code
369
+ # when using authorized token to introspect, or you'll get 200 with { "active": false } body
370
+ # when using authorized client to introspect as stated in the
371
+ # RFC 7662 section 2.2. Introspection Response.
372
+ #
373
+ # Using with caution:
374
+ # Keep in mind that these three parameters pass to block can be nil as following case:
375
+ # `authorized_client` is nil if and only if `authorized_token` is present, and vice versa.
376
+ # `token` will be nil if and only if `authorized_token` is present.
377
+ # So remember to use `&` or check if it is present before calling method on
378
+ # them to make sure you doesn't get NoMethodError exception.
379
+ #
380
+ # You can define your custom check:
381
+ #
382
+ # allow_token_introspection do |token, authorized_client, authorized_token|
383
+ # if authorized_token
384
+ # # customize: require `introspection` scope
385
+ # authorized_token.application == token&.application ||
386
+ # authorized_token.scopes.include?("introspection")
387
+ # elsif token.application
388
+ # # `protected_resource` is a new database boolean column, for example
389
+ # authorized_client == token.application || authorized_client.protected_resource?
390
+ # else
391
+ # # public token (when token.application is nil, token doesn't belong to any application)
392
+ # true
393
+ # end
394
+ # end
349
395
  #
350
- # You can completely disable any token introspection:
396
+ # Or you can completely disable any token introspection:
351
397
  #
352
398
  # allow_token_introspection false
353
399
  #
354
- # In such case every request for token introspection will get { "active": false } response.
355
400
  # If you need to block the request at all, then configure your routes.rb or web-server
356
401
  # like nginx to forbid the request.
357
402
 
358
- # WWW-Authenticate Realm (default "Doorkeeper").
403
+ # WWW-Authenticate Realm (default: "Doorkeeper").
359
404
  #
360
405
  # realm "Doorkeeper"
361
406
  end
@@ -24,7 +24,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration<%= migration_version %>
24
24
  t.text :redirect_uri, null: false
25
25
  t.datetime :created_at, null: false
26
26
  t.datetime :revoked_at
27
- t.string :scopes
27
+ t.string :scopes, null: false, default: ''
28
28
  end
29
29
 
30
30
  add_index :oauth_access_grants, :token, unique: true
@@ -14,16 +14,14 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do
14
14
  end
15
15
  end
16
16
 
17
- def translated_error_message(key)
18
- I18n.translate key, scope: %i[doorkeeper errors messages]
19
- end
20
-
21
17
  let(:client) { FactoryBot.create :application }
22
18
  let(:user) { User.create!(name: "Joe", password: "sekret") }
23
- let(:access_token) { FactoryBot.build :access_token, resource_owner_id: user.id, application_id: client.id }
19
+ let(:access_token) { FactoryBot.build :access_token, resource_owner_id: user.id, application_id: client.id, scopes: "default" }
24
20
 
25
21
  before do
26
22
  Doorkeeper.configure do
23
+ default_scopes :default
24
+
27
25
  custom_access_token_expires_in(lambda do |context|
28
26
  context.grant_type == Doorkeeper::OAuth::IMPLICIT ? 1234 : nil
29
27
  end)
@@ -106,80 +104,146 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do
106
104
  end
107
105
 
108
106
  describe "POST #create with errors" do
109
- before do
110
- default_scopes_exist :public
107
+ context "when missing client_id" do
108
+ before do
109
+ post :create, params: {
110
+ client_id: "",
111
+ response_type: "token",
112
+ redirect_uri: client.redirect_uri,
113
+ }
114
+ end
111
115
 
112
- post :create, params: {
113
- client_id: client.uid,
114
- response_type: "token",
115
- scope: "invalid",
116
- redirect_uri: client.redirect_uri,
117
- }
118
- end
116
+ let(:response_json_body) { JSON.parse(response.body) }
119
117
 
120
- it "redirects after authorization" do
121
- expect(response).to be_redirect
122
- end
118
+ it "renders 400 error" do
119
+ expect(response.status).to eq 400
120
+ end
123
121
 
124
- it "redirects to client redirect uri" do
125
- expect(response.location).to match(/^#{client.redirect_uri}/)
126
- end
122
+ it "includes error name" do
123
+ expect(response_json_body["error"]).to eq("invalid_request")
124
+ end
127
125
 
128
- it "does not include access token in fragment" do
129
- expect(response.query_params["access_token"]).to be_nil
130
- end
126
+ it "includes error description" do
127
+ expect(response_json_body["error_description"]).to eq(
128
+ translated_invalid_request_error_message(:missing_param, :client_id)
129
+ )
130
+ end
131
131
 
132
- it "includes error in fragment" do
133
- expect(response.query_params["error"]).to eq("invalid_scope")
132
+ it "does not issue any access token" do
133
+ expect(Doorkeeper::AccessToken.all).to be_empty
134
+ end
134
135
  end
135
136
 
136
- it "includes error description in fragment" do
137
- expect(response.query_params["error_description"]).to eq(translated_error_message(:invalid_scope))
138
- end
137
+ context "when other error happens" do
138
+ before do
139
+ default_scopes_exist :public
140
+
141
+ post :create, params: {
142
+ client_id: client.uid,
143
+ response_type: "token",
144
+ scope: "invalid",
145
+ redirect_uri: client.redirect_uri,
146
+ }
147
+ end
148
+
149
+ it "redirects after authorization" do
150
+ expect(response).to be_redirect
151
+ end
152
+
153
+ it "redirects to client redirect uri" do
154
+ expect(response.location).to match(/^#{client.redirect_uri}/)
155
+ end
156
+
157
+ it "does not include access token in fragment" do
158
+ expect(response.query_params["access_token"]).to be_nil
159
+ end
160
+
161
+ it "includes error in fragment" do
162
+ expect(response.query_params["error"]).to eq("invalid_scope")
163
+ end
164
+
165
+ it "includes error description in fragment" do
166
+ expect(response.query_params["error_description"]).to eq(translated_error_message(:invalid_scope))
167
+ end
139
168
 
140
- it "does not issue any access token" do
141
- expect(Doorkeeper::AccessToken.all).to be_empty
169
+ it "does not issue any access token" do
170
+ expect(Doorkeeper::AccessToken.all).to be_empty
171
+ end
142
172
  end
143
173
  end
144
174
 
145
175
  describe "POST #create in API mode with errors" do
146
- before do
147
- allow(Doorkeeper.configuration).to receive(:api_only).and_return(true)
148
- default_scopes_exist :public
176
+ context "when missing client_id" do
177
+ before do
178
+ allow(Doorkeeper.configuration).to receive(:api_only).and_return(true)
149
179
 
150
- post :create, params: {
151
- client_id: client.uid,
152
- response_type: "token",
153
- scope: "invalid",
154
- redirect_uri: client.redirect_uri,
155
- }
156
- end
180
+ post :create, params: {
181
+ client_id: "",
182
+ response_type: "token",
183
+ redirect_uri: client.redirect_uri,
184
+ }
185
+ end
157
186
 
158
- let(:response_json_body) { JSON.parse(response.body) }
159
- let(:redirect_uri) { response_json_body["redirect_uri"] }
187
+ let(:response_json_body) { JSON.parse(response.body) }
160
188
 
161
- it "renders 400 error" do
162
- expect(response.status).to eq 400
163
- end
189
+ it "renders 400 error" do
190
+ expect(response.status).to eq 400
191
+ end
164
192
 
165
- it "includes correct redirect URI" do
166
- expect(redirect_uri).to match(/^#{client.redirect_uri}/)
167
- end
193
+ it "includes error name" do
194
+ expect(response_json_body["error"]).to eq("invalid_request")
195
+ end
168
196
 
169
- it "does not include access token in fragment" do
170
- expect(redirect_uri.match(/access_token=([a-f0-9]+)&?/)).to be_nil
171
- end
197
+ it "includes error description" do
198
+ expect(response_json_body["error_description"]).to eq(
199
+ translated_invalid_request_error_message(:missing_param, :client_id)
200
+ )
201
+ end
172
202
 
173
- it "includes error in redirect uri" do
174
- expect(redirect_uri.match(/error=([a-z_]+)&?/)[1]).to eq "invalid_scope"
203
+ it "does not issue any access token" do
204
+ expect(Doorkeeper::AccessToken.all).to be_empty
205
+ end
175
206
  end
176
207
 
177
- it "includes error description in redirect uri" do
178
- expect(redirect_uri.match(/error_description=(.+)&?/)[1]).to_not be_nil
179
- end
208
+ context "when other error happens" do
209
+ before do
210
+ allow(Doorkeeper.configuration).to receive(:api_only).and_return(true)
211
+ default_scopes_exist :public
180
212
 
181
- it "does not issue any access token" do
182
- expect(Doorkeeper::AccessToken.all).to be_empty
213
+ post :create, params: {
214
+ client_id: client.uid,
215
+ response_type: "token",
216
+ scope: "invalid",
217
+ redirect_uri: client.redirect_uri,
218
+ }
219
+ end
220
+
221
+ let(:response_json_body) { JSON.parse(response.body) }
222
+ let(:redirect_uri) { response_json_body["redirect_uri"] }
223
+
224
+ it "renders 400 error" do
225
+ expect(response.status).to eq 400
226
+ end
227
+
228
+ it "includes correct redirect URI" do
229
+ expect(redirect_uri).to match(/^#{client.redirect_uri}/)
230
+ end
231
+
232
+ it "does not include access token in fragment" do
233
+ expect(redirect_uri.match(/access_token=([a-f0-9]+)&?/)).to be_nil
234
+ end
235
+
236
+ it "includes error in redirect uri" do
237
+ expect(redirect_uri.match(/error=([a-z_]+)&?/)[1]).to eq "invalid_scope"
238
+ end
239
+
240
+ it "includes error description in redirect uri" do
241
+ expect(redirect_uri.match(/error_description=(.+)&?/)[1]).to_not be_nil
242
+ end
243
+
244
+ it "does not issue any access token" do
245
+ expect(Doorkeeper::AccessToken.all).to be_empty
246
+ end
183
247
  end
184
248
  end
185
249
 
@@ -368,7 +432,7 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do
368
432
  expect(json_response["redirect_uri"]).to eq(client.redirect_uri)
369
433
  expect(json_response["state"]).to be_nil
370
434
  expect(json_response["response_type"]).to eq("token")
371
- expect(json_response["scope"]).to eq("")
435
+ expect(json_response["scope"]).to eq("default")
372
436
  end
373
437
  end
374
438
 
@@ -445,12 +509,12 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do
445
509
  end
446
510
 
447
511
  it "includes error in body" do
448
- expect(response_json_body["error"]).to eq("unsupported_response_type")
512
+ expect(response_json_body["error"]).to eq("invalid_request")
449
513
  end
450
514
 
451
515
  it "includes error description in body" do
452
516
  expect(response_json_body["error_description"])
453
- .to eq(translated_error_message(:unsupported_response_type))
517
+ .to eq(translated_invalid_request_error_message(:missing_param, :client_id))
454
518
  end
455
519
 
456
520
  it "does not issue any token" do
@@ -513,6 +577,8 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do
513
577
 
514
578
  describe "authorize response memoization" do
515
579
  it "memoizes the result of the authorization" do
580
+ pre_auth = double(:pre_auth, authorizable?: true)
581
+ allow(controller).to receive(:pre_auth) { pre_auth }
516
582
  strategy = double(:strategy, authorize: true)
517
583
  expect(strategy).to receive(:authorize).once
518
584
  allow(controller).to receive(:strategy) { strategy }
@@ -524,4 +590,17 @@ describe Doorkeeper::AuthorizationsController, "implicit grant flow" do
524
590
  post :create
525
591
  end
526
592
  end
593
+
594
+ describe "strong parameters" do
595
+ it "ignores non-scalar scope parameter" do
596
+ get :new, params: {
597
+ client_id: client.uid,
598
+ response_type: "token",
599
+ redirect_uri: client.redirect_uri,
600
+ scope: { "0" => "profile" },
601
+ }
602
+
603
+ expect(response).to be_successful
604
+ end
605
+ end
527
606
  end
@@ -166,7 +166,7 @@ describe "doorkeeper authorize filter" do
166
166
  it "it renders a custom JSON response", token: :invalid do
167
167
  get :index, params: { access_token: token_string }
168
168
  expect(response.status).to eq 401
169
- expect(response.content_type).to eq("application/json")
169
+ expect(response.content_type).to include("application/json")
170
170
  expect(response.header["WWW-Authenticate"]).to match(/^Bearer/)
171
171
 
172
172
  expect(json_response).not_to be_nil
@@ -196,7 +196,7 @@ describe "doorkeeper authorize filter" do
196
196
  it "it renders a custom text response", token: :invalid do
197
197
  get :index, params: { access_token: token_string }
198
198
  expect(response.status).to eq 401
199
- expect(response.content_type).to eq("text/plain")
199
+ expect(response.content_type).to include("text/plain")
200
200
  expect(response.header["WWW-Authenticate"]).to match(/^Bearer/)
201
201
  expect(response.body).to eq("Unauthorized")
202
202
  end
@@ -246,7 +246,7 @@ describe "doorkeeper authorize filter" do
246
246
  it "renders a custom JSON response" do
247
247
  get :index, params: { access_token: token_string }
248
248
  expect(response.header).to_not include("WWW-Authenticate")
249
- expect(response.content_type).to eq("application/json")
249
+ expect(response.content_type).to include("application/json")
250
250
  expect(response.status).to eq 403
251
251
 
252
252
  expect(json_response).not_to be_nil