doorkeeper 5.3.3 → 5.4.0.rc1

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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +0 -14
  3. data/CHANGELOG.md +35 -10
  4. data/Dangerfile +7 -7
  5. data/Dockerfile +2 -2
  6. data/Gemfile +9 -9
  7. data/README.md +6 -4
  8. data/app/controllers/doorkeeper/applications_controller.rb +7 -7
  9. data/app/controllers/doorkeeper/authorizations_controller.rb +31 -12
  10. data/app/controllers/doorkeeper/authorized_applications_controller.rb +3 -3
  11. data/app/controllers/doorkeeper/tokens_controller.rb +57 -20
  12. data/app/views/doorkeeper/applications/show.html.erb +19 -2
  13. data/bin/console +14 -0
  14. data/config/locales/en.yml +3 -1
  15. data/doorkeeper.gemspec +1 -1
  16. data/gemfiles/rails_5_0.gemfile +8 -7
  17. data/gemfiles/rails_5_1.gemfile +8 -7
  18. data/gemfiles/rails_5_2.gemfile +8 -7
  19. data/gemfiles/rails_6_0.gemfile +8 -7
  20. data/gemfiles/rails_master.gemfile +8 -7
  21. data/lib/doorkeeper.rb +106 -79
  22. data/lib/doorkeeper/config.rb +40 -17
  23. data/lib/doorkeeper/config/abstract_builder.rb +28 -0
  24. data/lib/doorkeeper/config/option.rb +28 -14
  25. data/lib/doorkeeper/grape/helpers.rb +1 -1
  26. data/lib/doorkeeper/models/access_grant_mixin.rb +9 -11
  27. data/lib/doorkeeper/models/access_token_mixin.rb +100 -41
  28. data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
  29. data/lib/doorkeeper/models/concerns/revocable.rb +1 -1
  30. data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
  31. data/lib/doorkeeper/models/concerns/secret_storable.rb +1 -3
  32. data/lib/doorkeeper/oauth/authorization/code.rb +14 -5
  33. data/lib/doorkeeper/oauth/authorization/context.rb +2 -2
  34. data/lib/doorkeeper/oauth/authorization/token.rb +7 -11
  35. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +4 -4
  36. data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -8
  37. data/lib/doorkeeper/oauth/base_request.rb +11 -19
  38. data/lib/doorkeeper/oauth/client.rb +1 -1
  39. data/lib/doorkeeper/oauth/client/credentials.rb +2 -4
  40. data/lib/doorkeeper/oauth/client_credentials/creator.rb +25 -7
  41. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +3 -2
  42. data/lib/doorkeeper/oauth/client_credentials/validator.rb +1 -1
  43. data/lib/doorkeeper/oauth/client_credentials_request.rb +8 -7
  44. data/lib/doorkeeper/oauth/code_request.rb +1 -1
  45. data/lib/doorkeeper/oauth/code_response.rb +6 -2
  46. data/lib/doorkeeper/oauth/error_response.rb +2 -4
  47. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -5
  48. data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
  49. data/lib/doorkeeper/oauth/invalid_token_response.rb +2 -2
  50. data/lib/doorkeeper/oauth/password_access_token_request.rb +3 -5
  51. data/lib/doorkeeper/oauth/pre_authorization.rb +32 -27
  52. data/lib/doorkeeper/oauth/refresh_token_request.rb +18 -22
  53. data/lib/doorkeeper/oauth/token.rb +1 -1
  54. data/lib/doorkeeper/oauth/token_introspection.rb +3 -3
  55. data/lib/doorkeeper/oauth/token_request.rb +2 -2
  56. data/lib/doorkeeper/oauth/token_response.rb +1 -1
  57. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +7 -2
  58. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +6 -2
  59. data/lib/doorkeeper/orm/active_record/mixins/application.rb +9 -64
  60. data/lib/doorkeeper/rails/routes.rb +13 -17
  61. data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
  62. data/lib/doorkeeper/rails/routes/mapper.rb +2 -2
  63. data/lib/doorkeeper/rails/routes/registry.rb +45 -0
  64. data/lib/doorkeeper/request/strategy.rb +2 -2
  65. data/lib/doorkeeper/server.rb +3 -3
  66. data/lib/doorkeeper/version.rb +3 -3
  67. data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
  68. data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
  69. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +2 -0
  70. data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +2 -0
  71. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +2 -0
  72. data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
  73. data/lib/generators/doorkeeper/templates/initializer.rb +39 -3
  74. data/lib/generators/doorkeeper/templates/migration.rb.erb +2 -0
  75. data/spec/controllers/applications_controller_spec.rb +2 -2
  76. data/spec/controllers/authorizations_controller_spec.rb +165 -30
  77. data/spec/controllers/tokens_controller_spec.rb +6 -5
  78. data/spec/dummy/app/helpers/application_helper.rb +1 -1
  79. data/spec/dummy/app/models/user.rb +5 -1
  80. data/spec/dummy/config/application.rb +6 -4
  81. data/spec/dummy/config/boot.rb +4 -4
  82. data/spec/dummy/config/environment.rb +1 -1
  83. data/spec/dummy/config/routes.rb +4 -4
  84. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +2 -2
  85. data/spec/dummy/db/schema.rb +3 -1
  86. data/spec/factories.rb +1 -1
  87. data/spec/generators/enable_polymorphic_resource_owner_generator_spec.rb +47 -0
  88. data/spec/lib/config_spec.rb +15 -11
  89. data/spec/lib/models/revocable_spec.rb +2 -3
  90. data/spec/lib/models/scopes_spec.rb +8 -0
  91. data/spec/lib/oauth/authorization_code_request_spec.rb +25 -15
  92. data/spec/lib/oauth/base_request_spec.rb +6 -20
  93. data/spec/lib/oauth/client_credentials/creator_spec.rb +90 -89
  94. data/spec/lib/oauth/client_credentials/issuer_spec.rb +84 -86
  95. data/spec/lib/oauth/client_credentials/validation_spec.rb +38 -40
  96. data/spec/lib/oauth/client_credentials_request_spec.rb +5 -4
  97. data/spec/lib/oauth/code_request_spec.rb +1 -1
  98. data/spec/lib/oauth/code_response_spec.rb +5 -1
  99. data/spec/lib/oauth/error_response_spec.rb +1 -1
  100. data/spec/lib/oauth/password_access_token_request_spec.rb +24 -13
  101. data/spec/lib/oauth/pre_authorization_spec.rb +13 -18
  102. data/spec/lib/oauth/refresh_token_request_spec.rb +19 -30
  103. data/spec/lib/oauth/token_request_spec.rb +14 -7
  104. data/spec/lib/option_spec.rb +51 -0
  105. data/spec/lib/stale_records_cleaner_spec.rb +18 -5
  106. data/spec/models/doorkeeper/access_grant_spec.rb +18 -4
  107. data/spec/models/doorkeeper/access_token_spec.rb +507 -479
  108. data/spec/models/doorkeeper/application_spec.rb +22 -62
  109. data/spec/requests/endpoints/token_spec.rb +5 -1
  110. data/spec/requests/flows/authorization_code_errors_spec.rb +4 -1
  111. data/spec/requests/flows/authorization_code_spec.rb +6 -1
  112. data/spec/requests/flows/client_credentials_spec.rb +41 -0
  113. data/spec/requests/flows/refresh_token_spec.rb +16 -8
  114. data/spec/requests/flows/revoke_token_spec.rb +143 -104
  115. data/spec/support/helpers/access_token_request_helper.rb +1 -0
  116. data/spec/support/helpers/authorization_request_helper.rb +4 -4
  117. data/spec/support/helpers/config_helper.rb +1 -1
  118. data/spec/support/shared/controllers_shared_context.rb +2 -2
  119. data/spec/support/shared/models_shared_examples.rb +6 -4
  120. metadata +16 -5
@@ -102,7 +102,7 @@ describe Doorkeeper::Application do
102
102
  end
103
103
 
104
104
  context "application owner is required" do
105
- before do
105
+ before(:each) do
106
106
  require_owner
107
107
  @owner = FactoryBot.build_stubbed(:doorkeeper_testing_user)
108
108
  end
@@ -222,13 +222,14 @@ describe Doorkeeper::Application do
222
222
  new_application.save
223
223
  end
224
224
 
225
- let(:resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
225
+ let(:resource_owner) { FactoryBot.create(:resource_owner) }
226
226
 
227
227
  it "should destroy its access grants" do
228
228
  FactoryBot.create(
229
229
  :access_grant,
230
230
  application: new_application,
231
231
  resource_owner_id: resource_owner.id,
232
+ resource_owner_type: resource_owner.class.name,
232
233
  )
233
234
 
234
235
  expect { new_application.destroy }.to change { Doorkeeper::AccessGrant.count }.by(-1)
@@ -287,8 +288,8 @@ describe Doorkeeper::Application do
287
288
  end
288
289
 
289
290
  describe "#authorized_for" do
290
- let(:resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
291
- let(:other_resource_owner) { FactoryBot.create(:doorkeeper_testing_user) }
291
+ let(:resource_owner) { FactoryBot.create(:resource_owner) }
292
+ let(:other_resource_owner) { FactoryBot.create(:resource_owner) }
292
293
 
293
294
  it "is empty if the application is not authorized for anyone" do
294
295
  expect(described_class.authorized_for(resource_owner)).to be_empty
@@ -298,10 +299,12 @@ describe Doorkeeper::Application do
298
299
  FactoryBot.create(
299
300
  :access_token,
300
301
  resource_owner_id: other_resource_owner.id,
302
+ resource_owner_type: other_resource_owner.class.name,
301
303
  )
302
304
  token = FactoryBot.create(
303
305
  :access_token,
304
306
  resource_owner_id: resource_owner.id,
307
+ resource_owner_type: resource_owner.class.name,
305
308
  )
306
309
  expect(described_class.authorized_for(resource_owner)).to eq([token.application])
307
310
  end
@@ -310,6 +313,7 @@ describe Doorkeeper::Application do
310
313
  FactoryBot.create(
311
314
  :access_token,
312
315
  resource_owner_id: resource_owner.id,
316
+ resource_owner_type: resource_owner.class.name,
313
317
  revoked_at: 2.days.ago,
314
318
  )
315
319
  expect(described_class.authorized_for(resource_owner)).to be_empty
@@ -319,10 +323,12 @@ describe Doorkeeper::Application do
319
323
  token1 = FactoryBot.create(
320
324
  :access_token,
321
325
  resource_owner_id: resource_owner.id,
326
+ resource_owner_type: resource_owner.class.name,
322
327
  )
323
328
  token2 = FactoryBot.create(
324
329
  :access_token,
325
330
  resource_owner_id: resource_owner.id,
331
+ resource_owner_type: resource_owner.class.name,
326
332
  )
327
333
  expect(described_class.authorized_for(resource_owner))
328
334
  .to eq([token1.application, token2.application])
@@ -333,11 +339,13 @@ describe Doorkeeper::Application do
333
339
  FactoryBot.create(
334
340
  :access_token,
335
341
  resource_owner_id: resource_owner.id,
342
+ resource_owner_type: resource_owner.class.name,
336
343
  application: application,
337
344
  )
338
345
  FactoryBot.create(
339
346
  :access_token,
340
347
  resource_owner_id: resource_owner.id,
348
+ resource_owner_type: resource_owner.class.name,
341
349
  application: application,
342
350
  )
343
351
  expect(described_class.authorized_for(resource_owner)).to eq([application])
@@ -413,7 +421,16 @@ describe Doorkeeper::Application do
413
421
  .to receive(:application_secret_strategy).and_return(Doorkeeper::SecretStoring::Plain)
414
422
  end
415
423
 
416
- # AR specific feature
424
+ it "includes plaintext secret" do
425
+ expect(app.as_json).to include("secret" => "123123123")
426
+ end
427
+
428
+ it "respects custom options" do
429
+ expect(app.as_json(except: :secret)).not_to include("secret")
430
+ expect(app.as_json(only: :id)).to match("id" => app.id)
431
+ end
432
+
433
+ # AR specific
417
434
  if DOORKEEPER_ORM == :active_record
418
435
  it "correctly works with #to_json" do
419
436
  ActiveRecord::Base.include_root_in_json = true
@@ -421,62 +438,5 @@ describe Doorkeeper::Application do
421
438
  ActiveRecord::Base.include_root_in_json = false
422
439
  end
423
440
  end
424
-
425
- context "when called without authorized resource owner" do
426
- it "includes minimal set of attributes" do
427
- expect(app.as_json).to match(
428
- "id" => app.id,
429
- "name" => app.name,
430
- "created_at" => an_instance_of(String),
431
- )
432
- end
433
-
434
- it "includes application UID if it's public" do
435
- app = FactoryBot.create :application, secret: "123123123", confidential: false
436
-
437
- expect(app.as_json).to match(
438
- "id" => app.id,
439
- "name" => app.name,
440
- "created_at" => an_instance_of(String),
441
- "uid" => app.uid,
442
- )
443
- end
444
-
445
- it "respects custom options" do
446
- expect(app.as_json(except: :id)).not_to include("id")
447
- expect(app.as_json(only: %i[name created_at secret]))
448
- .to match(
449
- "name" => app.name,
450
- "created_at" => an_instance_of(String),
451
- )
452
- end
453
- end
454
-
455
- context "when called with authorized resource owner" do
456
- let(:owner) { FactoryBot.create(:doorkeeper_testing_user) }
457
- let(:other_owner) { FactoryBot.create(:doorkeeper_testing_user) }
458
- let(:app) { FactoryBot.create(:application, secret: "123123123", owner: owner) }
459
-
460
- before do
461
- Doorkeeper.configure do
462
- orm DOORKEEPER_ORM
463
- enable_application_owner confirmation: false
464
- end
465
- end
466
-
467
- it "includes all the attributes" do
468
- expect(app.as_json(current_resource_owner: owner))
469
- .to include(
470
- "secret" => "123123123",
471
- "redirect_uri" => app.redirect_uri,
472
- "uid" => app.uid,
473
- )
474
- end
475
-
476
- it "doesn't include unsafe attributes if current owner isn't the same as owner" do
477
- expect(app.as_json(current_resource_owner: other_owner))
478
- .not_to include("redirect_uri")
479
- end
480
- end
481
441
  end
482
442
  end
@@ -5,7 +5,11 @@ require "spec_helper"
5
5
  describe "Token endpoint" do
6
6
  before do
7
7
  client_exists
8
- authorization_code_exists application: @client, scopes: "public"
8
+ create_resource_owner
9
+ authorization_code_exists application: @client,
10
+ scopes: "public",
11
+ resource_owner_id: @resource_owner.id,
12
+ resource_owner_type: @resource_owner.class.name
9
13
  end
10
14
 
11
15
  it "respond with correct headers" do
@@ -50,7 +50,10 @@ end
50
50
  describe "Authorization Code Flow Errors", "after authorization" do
51
51
  before do
52
52
  client_exists
53
- authorization_code_exists application: @client
53
+ create_resource_owner
54
+ authorization_code_exists application: @client,
55
+ resource_owner_id: @resource_owner.id,
56
+ resource_owner_type: @resource_owner.class.name
54
57
  end
55
58
 
56
59
  it "returns :invalid_grant error when posting an already revoked grant code" do
@@ -182,6 +182,7 @@ feature "Authorization Code Flow" do
182
182
  access_token_exists application: @client,
183
183
  expires_in: -100, # even expired token
184
184
  resource_owner_id: @resource_owner.id,
185
+ resource_owner_type: @resource_owner.class.name,
185
186
  scopes: "public write"
186
187
 
187
188
  visit authorization_endpoint_url(client: @client, scope: "public write")
@@ -506,8 +507,12 @@ describe "Authorization Code Flow" do
506
507
  end
507
508
 
508
509
  context "issuing a refresh token" do
510
+ let(:resource_owner) { FactoryBot.create(:resource_owner) }
511
+
509
512
  before do
510
- authorization_code_exists application: @client
513
+ authorization_code_exists application: @client,
514
+ resource_owner_id: resource_owner.id,
515
+ resource_owner_type: resource_owner.class.name
511
516
  end
512
517
 
513
518
  it "second of simultaneous client requests get an error for revoked acccess token" do
@@ -159,6 +159,47 @@ describe "Client Credentials Request" do
159
159
  end
160
160
  end
161
161
 
162
+ context "when revoke_previous_client_credentials_token is true" do
163
+ before do
164
+ allow(Doorkeeper.config).to receive(:reuse_access_token) { false }
165
+ allow(Doorkeeper.config).to receive(:revoke_previous_client_credentials_token) { true }
166
+ end
167
+
168
+ it "revokes the previous token" do
169
+ headers = authorization client.uid, client.secret
170
+ params = { grant_type: "client_credentials" }
171
+
172
+ post "/oauth/token", params: params, headers: headers
173
+ should_have_json "access_token", Doorkeeper::AccessToken.first.token
174
+
175
+ token = Doorkeeper::AccessToken.first
176
+
177
+ post "/oauth/token", params: params, headers: headers
178
+ should_have_json "access_token", Doorkeeper::AccessToken.last.token
179
+
180
+ expect(token.reload.revoked?).to be_truthy
181
+ expect(Doorkeeper::AccessToken.last.revoked?).to be_falsey
182
+ end
183
+
184
+ context "with a simultaneous request" do
185
+ let!(:access_token) { FactoryBot.create :access_token, resource_owner_id: nil }
186
+
187
+ before do
188
+ allow(Doorkeeper.config.access_token_model).to receive(:matching_token_for) { access_token }
189
+ allow(access_token).to receive(:revoked?).and_return(true)
190
+ end
191
+
192
+ it "returns an error" do
193
+ headers = authorization client.uid, client.secret
194
+ params = { grant_type: "client_credentials" }
195
+
196
+ post "/oauth/token", params: params, headers: headers
197
+ should_not_have_json "access_token"
198
+ should_have_json "error", "invalid_token_reuse"
199
+ end
200
+ end
201
+ end
202
+
162
203
  def authorization(username, password)
163
204
  credentials = ActionController::HttpAuthentication::Basic.encode_credentials username, password
164
205
  { "HTTP_AUTHORIZATION" => credentials }
@@ -12,9 +12,13 @@ describe "Refresh Token Flow" do
12
12
  client_exists
13
13
  end
14
14
 
15
+ let(:resource_owner) { FactoryBot.create(:resource_owner) }
16
+
15
17
  context "issuing a refresh token" do
16
18
  before do
17
- authorization_code_exists application: @client
19
+ authorization_code_exists application: @client,
20
+ resource_owner_id: resource_owner.id,
21
+ resource_owner_type: resource_owner.class.name
18
22
  end
19
23
 
20
24
  it "client gets the refresh token and refreshes it" do
@@ -43,7 +47,8 @@ describe "Refresh Token Flow" do
43
47
  @token = FactoryBot.create(
44
48
  :access_token,
45
49
  application: @client,
46
- resource_owner_id: 1,
50
+ resource_owner_id: resource_owner.id,
51
+ resource_owner_type: resource_owner.class.name,
47
52
  use_refresh_token: true,
48
53
  )
49
54
  end
@@ -110,7 +115,8 @@ describe "Refresh Token Flow" do
110
115
  FactoryBot.create(
111
116
  :access_token,
112
117
  application: @client,
113
- resource_owner_id: 1,
118
+ resource_owner_id: resource_owner.id,
119
+ resource_owner_type: resource_owner.class.name,
114
120
  use_refresh_token: true,
115
121
  )
116
122
  end
@@ -119,7 +125,8 @@ describe "Refresh Token Flow" do
119
125
  FactoryBot.create(
120
126
  :access_token,
121
127
  application: public_client,
122
- resource_owner_id: 1,
128
+ resource_owner_id: resource_owner.id,
129
+ resource_owner_type: resource_owner.class.name,
123
130
  use_refresh_token: true,
124
131
  )
125
132
  end
@@ -185,14 +192,15 @@ describe "Refresh Token Flow" do
185
192
  end
186
193
  create_resource_owner
187
194
  _another_token = post password_token_endpoint_url(
188
- client: @client, resource_owner: @resource_owner,
195
+ client: @client, resource_owner: resource_owner,
189
196
  )
190
- last_token.update_attribute :created_at, 5.seconds.ago
197
+ last_token.update(created_at: 5.seconds.ago)
191
198
 
192
199
  @token = FactoryBot.create(
193
200
  :access_token,
194
201
  application: @client,
195
- resource_owner_id: @resource_owner.id,
202
+ resource_owner_id: resource_owner.id,
203
+ resource_owner_type: resource_owner.class.name,
196
204
  use_refresh_token: true,
197
205
  )
198
206
  @token.update_attribute :expires_in, -100
@@ -226,7 +234,7 @@ describe "Refresh Token Flow" do
226
234
 
227
235
  def last_token
228
236
  Doorkeeper::AccessToken.last_authorized_token_for(
229
- @client.id, @resource_owner.id,
237
+ @client.id, resource_owner,
230
238
  )
231
239
  end
232
240
  end
@@ -7,151 +7,190 @@ describe "Revoke Token Flow" do
7
7
  Doorkeeper.configure { orm DOORKEEPER_ORM }
8
8
  end
9
9
 
10
- context "with default parameters" do
11
- let(:client_application) { FactoryBot.create :application }
12
- let(:resource_owner) { User.create!(name: "John", password: "sekret") }
10
+ let(:private_client_application) { FactoryBot.create :application }
11
+ let(:public_client_application) { FactoryBot.create :application, confidential: false }
12
+ let(:resource_owner) { User.create!(name: "John", password: "sekret") }
13
+
14
+ context "with authenticated, confidential OAuth 2.0 client/application" do
13
15
  let(:access_token) do
14
16
  FactoryBot.create(
15
17
  :access_token,
16
- application: client_application,
18
+ application: private_client_application,
17
19
  resource_owner_id: resource_owner.id,
20
+ resource_owner_type: resource_owner.class.name,
18
21
  use_refresh_token: true,
19
22
  )
20
23
  end
21
24
 
22
- context "with authenticated, confidential OAuth 2.0 client/application" do
25
+ let(:headers) do
26
+ client_id = private_client_application.uid
27
+ client_secret = private_client_application.secret
28
+ credentials = Base64.encode64("#{client_id}:#{client_secret}")
29
+ { "HTTP_AUTHORIZATION" => "Basic #{credentials}" }
30
+ end
31
+
32
+ it "should revoke the access token provided" do
33
+ post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers
34
+
35
+ expect(response).to be_successful
36
+ expect(access_token.reload.revoked?).to be_truthy
37
+ end
38
+
39
+ it "should revoke the refresh token provided" do
40
+ post revocation_token_endpoint_url, params: { token: access_token.refresh_token }, headers: headers
41
+
42
+ expect(response).to be_successful
43
+ expect(access_token.reload.revoked?).to be_truthy
44
+ end
45
+
46
+ context "with invalid token to revoke" do
47
+ it "should not revoke any tokens and must respond with success" do
48
+ expect do
49
+ post revocation_token_endpoint_url,
50
+ params: { token: "I_AM_AN_INVALID_TOKEN" },
51
+ headers: headers
52
+ end.not_to(change { Doorkeeper::AccessToken.where(revoked_at: nil).count })
53
+
54
+ expect(response).to be_successful
55
+ end
56
+ end
57
+
58
+ context "with bad credentials and a valid token" do
23
59
  let(:headers) do
24
- client_id = client_application.uid
25
- client_secret = client_application.secret
26
- credentials = Base64.encode64("#{client_id}:#{client_secret}")
60
+ client_id = private_client_application.uid
61
+ credentials = Base64.encode64("#{client_id}:poop")
27
62
  { "HTTP_AUTHORIZATION" => "Basic #{credentials}" }
28
63
  end
29
64
 
30
- it "should revoke the access token provided" do
65
+ it "should not revoke any tokens and respond with forbidden" do
31
66
  post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers
32
67
 
33
- expect(response).to be_successful
34
- expect(access_token.reload.revoked?).to be_truthy
68
+ expect(response).to be_forbidden
69
+ expect(response.body).to include("unauthorized_client")
70
+ expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
71
+ expect(access_token.reload.revoked?).to be_falsey
35
72
  end
73
+ end
36
74
 
37
- it "should revoke the refresh token provided" do
38
- post revocation_token_endpoint_url, params: { token: access_token.refresh_token }, headers: headers
75
+ context "with no credentials and a valid token" do
76
+ it "should not revoke any tokens and respond with forbidden" do
77
+ post revocation_token_endpoint_url, params: { token: access_token.token }
39
78
 
40
- expect(response).to be_successful
41
- expect(access_token.reload.revoked?).to be_truthy
79
+ expect(response).to be_forbidden
80
+ expect(response.body).to include("unauthorized_client")
81
+ expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
82
+ expect(access_token.reload.revoked?).to be_falsey
42
83
  end
84
+ end
43
85
 
44
- context "with invalid token to revoke" do
45
- it "should not revoke any tokens and respond with forbidden" do
46
- expect do
47
- post revocation_token_endpoint_url,
48
- params: { token: "I_AM_AN_INVALID_TOKEN" },
49
- headers: headers
50
- end.not_to(change { Doorkeeper::AccessToken.where(revoked_at: nil).count })
51
-
52
- expect(response).to be_forbidden
53
- end
86
+ context "with valid token for another client application" do
87
+ let(:other_client_application) { FactoryBot.create :application }
88
+ let(:headers) do
89
+ client_id = other_client_application.uid
90
+ client_secret = other_client_application.secret
91
+ credentials = Base64.encode64("#{client_id}:#{client_secret}")
92
+ { "HTTP_AUTHORIZATION" => "Basic #{credentials}" }
54
93
  end
55
94
 
56
- context "with bad credentials and a valid token" do
57
- let(:headers) do
58
- client_id = client_application.uid
59
- credentials = Base64.encode64("#{client_id}:poop")
60
- { "HTTP_AUTHORIZATION" => "Basic #{credentials}" }
61
- end
62
- it "should not revoke any tokens and respond with forbidden" do
63
- post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers
64
-
65
- expect(response).to be_forbidden
66
- expect(response.body).to include("unauthorized_client")
67
- expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
68
- expect(access_token.reload.revoked?).to be_falsey
69
- end
95
+ it "should not revoke the token as it's unauthorized" do
96
+ post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers
97
+
98
+ expect(response).to be_forbidden
99
+ expect(response.body).to include("unauthorized_client")
100
+ expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
101
+ expect(access_token.reload.revoked?).to be_falsey
70
102
  end
103
+ end
104
+ end
105
+
106
+ context "with authenticated public OAuth 2.0 client/application" do
107
+ let(:access_token) do
108
+ FactoryBot.create(
109
+ :access_token,
110
+ application: nil,
111
+ resource_owner_id: resource_owner.id,
112
+ resource_owner_type: resource_owner.class.name,
113
+ use_refresh_token: true,
114
+ )
115
+ end
71
116
 
72
- context "with no credentials and a valid token" do
73
- it "should not revoke any tokens and respond with forbidden" do
74
- post revocation_token_endpoint_url, params: { token: access_token.token }
117
+ it "should revoke the access token provided" do
118
+ post revocation_token_endpoint_url,
119
+ params: { client_id: public_client_application.uid, token: access_token.token },
120
+ headers: headers
75
121
 
76
- expect(response).to be_forbidden
77
- expect(response.body).to include("unauthorized_client")
78
- expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
79
- expect(access_token.reload.revoked?).to be_falsey
80
- end
81
- end
122
+ expect(response).to be_successful
123
+ expect(access_token.reload.revoked?).to be_truthy
124
+ end
82
125
 
83
- context "with valid token for another client application" do
84
- let(:other_client_application) { FactoryBot.create :application }
85
- let(:headers) do
86
- client_id = other_client_application.uid
87
- client_secret = other_client_application.secret
88
- credentials = Base64.encode64("#{client_id}:#{client_secret}")
89
- { "HTTP_AUTHORIZATION" => "Basic #{credentials}" }
90
- end
91
-
92
- it "should not revoke the token as its unauthorized" do
93
- post revocation_token_endpoint_url, params: { token: access_token.token }, headers: headers
94
-
95
- expect(response).to be_forbidden
96
- expect(response.body).to include("unauthorized_client")
97
- expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
98
- expect(access_token.reload.revoked?).to be_falsey
99
- end
100
- end
126
+ it "should revoke the refresh token provided" do
127
+ post revocation_token_endpoint_url,
128
+ params: { client_id: public_client_application.uid, token: access_token.refresh_token },
129
+ headers: headers
130
+
131
+ expect(response).to be_successful
132
+ expect(access_token.reload.revoked?).to be_truthy
101
133
  end
102
134
 
103
- context "with public OAuth 2.0 client/application" do
135
+ it "should response with success even for invalid token" do
136
+ post revocation_token_endpoint_url,
137
+ params: { client_id: public_client_application.uid, token: "dont_exist" },
138
+ headers: headers
139
+
140
+ expect(response).to be_successful
141
+ end
142
+
143
+ context "with a valid token issued for a confidential client" do
104
144
  let(:access_token) do
105
145
  FactoryBot.create(
106
146
  :access_token,
107
- application: nil,
147
+ application: private_client_application,
108
148
  resource_owner_id: resource_owner.id,
149
+ resource_owner_type: resource_owner.class.name,
109
150
  use_refresh_token: true,
110
151
  )
111
152
  end
112
153
 
113
- it "should revoke the access token provided" do
114
- post revocation_token_endpoint_url, params: { token: access_token.token }
154
+ it "should not revoke the access token provided" do
155
+ post revocation_token_endpoint_url,
156
+ params: { client_id: public_client_application.uid, token: access_token.token }
115
157
 
116
- expect(response).to be_successful
117
- expect(access_token.reload.revoked?).to be_truthy
158
+ expect(response).to be_forbidden
159
+ expect(response.body).to include("unauthorized_client")
160
+ expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
161
+ expect(access_token.reload.revoked?).to be_falsey
118
162
  end
119
163
 
120
- it "should revoke the refresh token provided" do
121
- post revocation_token_endpoint_url, params: { token: access_token.refresh_token }
164
+ it "should not revoke the refresh token provided" do
165
+ post revocation_token_endpoint_url,
166
+ params: { client_id: public_client_application.uid, token: access_token.token }
122
167
 
123
- expect(response).to be_successful
124
- expect(access_token.reload.revoked?).to be_truthy
168
+ expect(response).to be_forbidden
169
+ expect(response.body).to include("unauthorized_client")
170
+ expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
171
+ expect(access_token.reload.revoked?).to be_falsey
125
172
  end
173
+ end
174
+ end
126
175
 
127
- context "with a valid token issued for a confidential client" do
128
- let(:access_token) do
129
- FactoryBot.create(
130
- :access_token,
131
- application: client_application,
132
- resource_owner_id: resource_owner.id,
133
- use_refresh_token: true,
134
- )
135
- end
136
-
137
- it "should not revoke the access token provided" do
138
- post revocation_token_endpoint_url, params: { token: access_token.token }
139
-
140
- expect(response).to be_forbidden
141
- expect(response.body).to include("unauthorized_client")
142
- expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
143
- expect(access_token.reload.revoked?).to be_falsey
144
- end
145
-
146
- it "should not revoke the refresh token provided" do
147
- post revocation_token_endpoint_url, params: { token: access_token.token }
148
-
149
- expect(response).to be_forbidden
150
- expect(response.body).to include("unauthorized_client")
151
- expect(response.body).to include(I18n.t("doorkeeper.errors.messages.revoke.unauthorized"))
152
- expect(access_token.reload.revoked?).to be_falsey
153
- end
154
- end
176
+ context "without client authentication" do
177
+ let(:access_token) do
178
+ FactoryBot.create(
179
+ :access_token,
180
+ application: nil,
181
+ resource_owner_id: resource_owner.id,
182
+ resource_owner_type: resource_owner.class.name,
183
+ use_refresh_token: true,
184
+ )
185
+ end
186
+
187
+ it "shouldn't remove the token and must response with an error" do
188
+ post revocation_token_endpoint_url,
189
+ params: { token: access_token.token },
190
+ headers: headers
191
+
192
+ expect(response).not_to be_successful
193
+ expect(access_token.reload.revoked?).to be_falsey
155
194
  end
156
195
  end
157
196
  end