doorkeeper 5.2.0.rc2 → 5.2.0.rc3

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +1 -1
  3. data/CHANGELOG.md +15 -2
  4. data/Gemfile +1 -1
  5. data/README.md +9 -1
  6. data/app/controllers/doorkeeper/application_metal_controller.rb +1 -1
  7. data/app/controllers/doorkeeper/authorizations_controller.rb +11 -9
  8. data/config/locales/en.yml +5 -1
  9. data/doorkeeper.gemspec +8 -0
  10. data/gemfiles/rails_6_0.gemfile +1 -1
  11. data/lib/doorkeeper.rb +1 -0
  12. data/lib/doorkeeper/config.rb +41 -2
  13. data/lib/doorkeeper/errors.rb +13 -18
  14. data/lib/doorkeeper/helpers/controller.rb +6 -2
  15. data/lib/doorkeeper/oauth/authorization/code.rb +1 -5
  16. data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -9
  17. data/lib/doorkeeper/oauth/base_request.rb +2 -0
  18. data/lib/doorkeeper/oauth/client_credentials/validation.rb +8 -0
  19. data/lib/doorkeeper/oauth/code_request.rb +5 -11
  20. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  21. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
  22. data/lib/doorkeeper/oauth/pre_authorization.rb +70 -37
  23. data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -2
  24. data/lib/doorkeeper/oauth/token_introspection.rb +4 -1
  25. data/lib/doorkeeper/oauth/token_request.rb +4 -18
  26. data/lib/doorkeeper/orm/active_record.rb +2 -2
  27. data/lib/doorkeeper/orm/active_record/application.rb +1 -1
  28. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
  29. data/lib/doorkeeper/request.rb +6 -11
  30. data/lib/doorkeeper/request/authorization_code.rb +2 -0
  31. data/lib/doorkeeper/server.rb +2 -6
  32. data/lib/doorkeeper/version.rb +1 -1
  33. data/lib/generators/doorkeeper/templates/initializer.rb +33 -2
  34. data/lib/generators/doorkeeper/templates/migration.rb.erb +1 -1
  35. data/spec/controllers/authorizations_controller_spec.rb +127 -61
  36. data/spec/controllers/protected_resources_controller_spec.rb +3 -3
  37. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +1 -1
  38. data/spec/lib/config_spec.rb +17 -0
  39. data/spec/lib/oauth/authorization_code_request_spec.rb +11 -1
  40. data/spec/lib/oauth/base_request_spec.rb +33 -16
  41. data/spec/lib/oauth/code_request_spec.rb +27 -28
  42. data/spec/lib/oauth/invalid_request_response_spec.rb +75 -0
  43. data/spec/lib/oauth/pre_authorization_spec.rb +80 -55
  44. data/spec/lib/oauth/refresh_token_request_spec.rb +1 -0
  45. data/spec/lib/oauth/token_request_spec.rb +20 -17
  46. data/spec/lib/server_spec.rb +0 -12
  47. data/spec/requests/endpoints/authorization_spec.rb +21 -5
  48. data/spec/requests/endpoints/token_spec.rb +1 -1
  49. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
  50. data/spec/requests/flows/authorization_code_spec.rb +77 -23
  51. data/spec/requests/flows/client_credentials_spec.rb +38 -0
  52. data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
  53. data/spec/requests/flows/implicit_grant_spec.rb +9 -8
  54. data/spec/requests/flows/password_spec.rb +37 -0
  55. data/spec/requests/flows/refresh_token_spec.rb +1 -1
  56. data/spec/support/helpers/request_spec_helper.rb +14 -2
  57. data/spec/validators/redirect_uri_validator_spec.rb +1 -1
  58. metadata +12 -4
  59. data/app/validators/redirect_uri_validator.rb +0 -60
@@ -17,6 +17,8 @@ module Doorkeeper
17
17
  private
18
18
 
19
19
  def grant
20
+ raise Errors::MissingRequiredParameter, :code if parameters[:code].blank?
21
+
20
22
  AccessGrant.by_token(parameters[:code])
21
23
  end
22
24
  end
@@ -10,12 +10,12 @@ module Doorkeeper
10
10
 
11
11
  def authorization_request(strategy)
12
12
  klass = Request.authorization_strategy strategy
13
- klass.new self
13
+ klass.new(self)
14
14
  end
15
15
 
16
16
  def token_request(strategy)
17
17
  klass = Request.token_strategy strategy
18
- klass.new self
18
+ klass.new(self)
19
19
  end
20
20
 
21
21
  # TODO: context should be the request
@@ -27,10 +27,6 @@ module Doorkeeper
27
27
  @client ||= OAuth::Client.authenticate(credentials)
28
28
  end
29
29
 
30
- def client_via_uid
31
- @client_via_uid ||= OAuth::Client.find(parameters[:client_id])
32
- end
33
-
34
30
  def current_resource_owner
35
31
  context.send :current_resource_owner
36
32
  end
@@ -10,7 +10,7 @@ module Doorkeeper
10
10
  MAJOR = 5
11
11
  MINOR = 2
12
12
  TINY = 0
13
- PRE = "rc2"
13
+ PRE = "rc3"
14
14
 
15
15
  # Full version number
16
16
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -9,7 +9,7 @@ Doorkeeper.configure do
9
9
  raise "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}"
10
10
  # Put your resource owner authentication logic here.
11
11
  # Example implementation:
12
- # User.find_by_id(session[:user_id]) || redirect_to(new_user_session_url)
12
+ # User.find_by(id: session[:user_id]) || redirect_to(new_user_session_url)
13
13
  end
14
14
 
15
15
  # If you didn't skip applications controller from Doorkeeper routes in your application routes.rb
@@ -170,7 +170,7 @@ Doorkeeper.configure do
170
170
  #
171
171
  # Keys to this hash should be the name of grant_type and
172
172
  # values should be the array of scopes for that grant type.
173
- # Note: scopes should be from configured_scopes(i.e. deafult or optional)
173
+ # Note: scopes should be from configured_scopes(i.e. default or optional)
174
174
  #
175
175
  # scopes_by_grant_type password: [:write], client_credentials: [:update]
176
176
 
@@ -281,6 +281,37 @@ Doorkeeper.configure do
281
281
  #
282
282
  # grant_flows %w[authorization_code client_credentials]
283
283
 
284
+ # Allows to customize OAuth grant flows that +each+ application support.
285
+ # You can configure a custom block (or use a class respond to `#call`) that must
286
+ # return `true` in case Application instance supports requested OAuth grant flow
287
+ # during the authorization request to the server. This configuration +doesn't+
288
+ # set flows per application, it only allows to check if application supports
289
+ # specific grant flow.
290
+ #
291
+ # For example you can add an additional database column to `oauth_applications` table,
292
+ # say `t.array :grant_flows, default: []`, and store allowed grant flows that can
293
+ # be used with this application there. Then when authorization requested Doorkeeper
294
+ # will call this block to check if specific Application (passed with client_id and/or
295
+ # client_secret) is allowed to perform the request for the specific grant type
296
+ # (authorization, password, client_credentials, etc).
297
+ #
298
+ # Example of the block:
299
+ #
300
+ # ->(flow, client) { client.grant_flows.include?(flow) }
301
+ #
302
+ # In case this option invocation result is `false`, Doorkeeper server returns
303
+ # :unauthorized_client error and stops the request.
304
+ #
305
+ # @param allow_grant_flow_for_client [Proc] Block or any object respond to #call
306
+ # @return [Boolean] `true` if allow or `false` if forbid the request
307
+ #
308
+ # allow_grant_flow_for_client do |grant_flow, client|
309
+ # # `grant_flows` is an Array column with grant
310
+ # # flows that application supports
311
+ #
312
+ # client.grant_flows.include?(grant_flow)
313
+ # end
314
+
284
315
  # Hook into the strategies' request & response life-cycle in case your
285
316
  # application needs advanced customization or logging:
286
317
  #
@@ -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
139
164
 
140
- it "does not issue any access token" do
141
- expect(Doorkeeper::AccessToken.all).to be_empty
165
+ it "includes error description in fragment" do
166
+ expect(response.query_params["error_description"]).to eq(translated_error_message(:invalid_scope))
167
+ end
168
+
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
212
+
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
180
220
 
181
- it "does not issue any access token" do
182
- expect(Doorkeeper::AccessToken.all).to be_empty
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 }
@@ -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
@@ -25,7 +25,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration[4.2]
25
25
  t.text :redirect_uri, null: false
26
26
  t.datetime :created_at, null: false
27
27
  t.datetime :revoked_at
28
- t.string :scopes
28
+ t.string :scopes, null: false, default: ""
29
29
  end
30
30
 
31
31
  add_index :oauth_access_grants, :token, unique: true
@@ -517,6 +517,23 @@ describe Doorkeeper, "configuration" do
517
517
  end
518
518
  end
519
519
 
520
+ describe "base_metal_controller" do
521
+ context "default" do
522
+ it { expect(Doorkeeper.configuration.base_metal_controller).to eq("ActionController::API") }
523
+ end
524
+
525
+ context "custom" do
526
+ before do
527
+ Doorkeeper.configure do
528
+ orm DOORKEEPER_ORM
529
+ base_metal_controller "ApplicationController"
530
+ end
531
+ end
532
+
533
+ it { expect(Doorkeeper.configuration.base_metal_controller).to eq("ApplicationController") }
534
+ end
535
+ end
536
+
520
537
  if DOORKEEPER_ORM == :active_record
521
538
  describe "active_record_options" do
522
539
  let(:models) { [Doorkeeper::AccessGrant, Doorkeeper::AccessToken, Doorkeeper::Application] }
@@ -23,7 +23,7 @@ module Doorkeeper::OAuth
23
23
  end
24
24
 
25
25
  subject do
26
- AuthorizationCodeRequest.new server, grant, client, params
26
+ AuthorizationCodeRequest.new(server, grant, client, params)
27
27
  end
28
28
 
29
29
  it "issues a new token for the client" do
@@ -65,6 +65,16 @@ module Doorkeeper::OAuth
65
65
  subject.redirect_uri = nil
66
66
  subject.validate
67
67
  expect(subject.error).to eq(:invalid_request)
68
+ expect(subject.missing_param).to eq(:redirect_uri)
69
+ end
70
+
71
+ it "invalid code_verifier param because server does not support pkce" do
72
+ allow_any_instance_of(Doorkeeper::AccessGrant).to receive(:respond_to?).with(:code_challenge).and_return(false)
73
+
74
+ subject.code_verifier = "a45a9fea-0676-477e-95b1-a40f72ac3cfb"
75
+ subject.validate
76
+ expect(subject.error).to eq(:invalid_request)
77
+ expect(subject.invalid_request_reason).to eq(:not_support_pkce)
68
78
  end
69
79
 
70
80
  it "matches the redirect_uri with grant's one" do
@@ -66,27 +66,44 @@ module Doorkeeper::OAuth
66
66
  end
67
67
 
68
68
  context "invalid" do
69
- before do
70
- allow(subject).to receive(:valid?).and_return(false)
71
- allow(subject).to receive(:error).and_return("server_error")
72
- allow(subject).to receive(:state).and_return("hello")
69
+ context "with error other than invalid_request" do
70
+ before do
71
+ allow(subject).to receive(:valid?).and_return(false)
72
+ allow(subject).to receive(:error).and_return(:server_error)
73
+ allow(subject).to receive(:state).and_return("hello")
74
+ end
75
+
76
+ it "returns an ErrorResponse object" do
77
+ result = subject.authorize
78
+
79
+ expect(result).to be_an_instance_of(ErrorResponse)
80
+
81
+ expect(result.body).to eq(
82
+ error: :server_error,
83
+ error_description: translated_error_message(:server_error),
84
+ state: "hello"
85
+ )
86
+ end
73
87
  end
74
88
 
75
- it "returns an ErrorResponse object" do
76
- error_description = I18n.translate(
77
- "server_error",
78
- scope: %i[doorkeeper errors messages]
79
- )
89
+ context "with invalid_request error" do
90
+ before do
91
+ allow(subject).to receive(:valid?).and_return(false)
92
+ allow(subject).to receive(:error).and_return(:invalid_request)
93
+ allow(subject).to receive(:state).and_return("hello")
94
+ end
80
95
 
81
- result = subject.authorize
96
+ it "returns an InvalidRequestResponse object" do
97
+ result = subject.authorize
82
98
 
83
- expect(result).to be_an_instance_of(ErrorResponse)
99
+ expect(result).to be_an_instance_of(InvalidRequestResponse)
84
100
 
85
- expect(result.body).to eq(
86
- error: "server_error",
87
- error_description: error_description,
88
- state: "hello"
89
- )
101
+ expect(result.body).to eq(
102
+ error: :invalid_request,
103
+ error_description: translated_invalid_request_error_message(:unknown, :unknown),
104
+ state: "hello"
105
+ )
106
+ end
90
107
  end
91
108
  end
92
109
  end