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.
Files changed (163) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +3 -3
  3. data/CHANGELOG.md +881 -0
  4. data/CONTRIBUTING.md +11 -9
  5. data/Dangerfile +2 -2
  6. data/Dockerfile +29 -0
  7. data/Gemfile +4 -3
  8. data/NEWS.md +1 -814
  9. data/README.md +11 -3
  10. data/RELEASING.md +6 -5
  11. data/app/controllers/doorkeeper/application_controller.rb +2 -2
  12. data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
  13. data/app/controllers/doorkeeper/applications_controller.rb +1 -0
  14. data/app/controllers/doorkeeper/authorizations_controller.rb +16 -9
  15. data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -2
  16. data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
  17. data/app/views/doorkeeper/applications/_form.html.erb +0 -6
  18. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  19. data/config/locales/en.yml +8 -2
  20. data/doorkeeper.gemspec +9 -1
  21. data/gemfiles/rails_5_0.gemfile +3 -2
  22. data/gemfiles/rails_5_1.gemfile +3 -2
  23. data/gemfiles/rails_5_2.gemfile +3 -2
  24. data/gemfiles/rails_6_0.gemfile +4 -3
  25. data/gemfiles/rails_master.gemfile +3 -2
  26. data/lib/doorkeeper/config/option.rb +13 -7
  27. data/lib/doorkeeper/config.rb +157 -42
  28. data/lib/doorkeeper/errors.rb +13 -18
  29. data/lib/doorkeeper/grape/helpers.rb +6 -2
  30. data/lib/doorkeeper/helpers/controller.rb +28 -7
  31. data/lib/doorkeeper/models/access_grant_mixin.rb +7 -6
  32. data/lib/doorkeeper/models/access_token_mixin.rb +99 -20
  33. data/lib/doorkeeper/models/application_mixin.rb +3 -3
  34. data/lib/doorkeeper/models/concerns/ownership.rb +1 -1
  35. data/lib/doorkeeper/models/concerns/reusable.rb +1 -1
  36. data/lib/doorkeeper/models/concerns/revocable.rb +0 -27
  37. data/lib/doorkeeper/oauth/authorization/code.rb +12 -14
  38. data/lib/doorkeeper/oauth/authorization/token.rb +10 -7
  39. data/lib/doorkeeper/oauth/authorization_code_request.rb +31 -15
  40. data/lib/doorkeeper/oauth/base_request.rb +10 -4
  41. data/lib/doorkeeper/oauth/client.rb +7 -8
  42. data/lib/doorkeeper/oauth/client_credentials/creator.rb +23 -2
  43. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -7
  44. data/lib/doorkeeper/oauth/client_credentials/{validation.rb → validator.rb} +10 -2
  45. data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
  46. data/lib/doorkeeper/oauth/code_request.rb +5 -11
  47. data/lib/doorkeeper/oauth/code_response.rb +4 -4
  48. data/lib/doorkeeper/oauth/error.rb +1 -1
  49. data/lib/doorkeeper/oauth/error_response.rb +6 -6
  50. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +7 -5
  51. data/lib/doorkeeper/oauth/helpers/unique_token.rb +8 -5
  52. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +19 -5
  53. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  54. data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -2
  55. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  56. data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
  57. data/lib/doorkeeper/oauth/pre_authorization.rb +76 -38
  58. data/lib/doorkeeper/oauth/refresh_token_request.rb +17 -14
  59. data/lib/doorkeeper/oauth/token.rb +2 -2
  60. data/lib/doorkeeper/oauth/token_introspection.rb +26 -16
  61. data/lib/doorkeeper/oauth/token_request.rb +4 -18
  62. data/lib/doorkeeper/orm/active_record/access_grant.rb +4 -43
  63. data/lib/doorkeeper/orm/active_record/access_token.rb +4 -35
  64. data/lib/doorkeeper/orm/active_record/application.rb +5 -83
  65. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +53 -0
  66. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +47 -0
  67. data/lib/doorkeeper/orm/active_record/mixins/application.rb +128 -0
  68. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
  69. data/lib/doorkeeper/orm/active_record.rb +21 -6
  70. data/lib/doorkeeper/rails/helpers.rb +4 -4
  71. data/lib/doorkeeper/rails/routes.rb +5 -7
  72. data/lib/doorkeeper/rake/db.rake +3 -3
  73. data/lib/doorkeeper/request/authorization_code.rb +5 -3
  74. data/lib/doorkeeper/request/client_credentials.rb +2 -2
  75. data/lib/doorkeeper/request/password.rb +2 -2
  76. data/lib/doorkeeper/request/refresh_token.rb +3 -3
  77. data/lib/doorkeeper/request.rb +7 -12
  78. data/lib/doorkeeper/server.rb +3 -7
  79. data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
  80. data/lib/doorkeeper/version.rb +1 -1
  81. data/lib/doorkeeper.rb +6 -3
  82. data/lib/generators/doorkeeper/application_owner_generator.rb +1 -1
  83. data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
  84. data/lib/generators/doorkeeper/migration_generator.rb +1 -1
  85. data/lib/generators/doorkeeper/pkce_generator.rb +1 -1
  86. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -7
  87. data/lib/generators/doorkeeper/templates/initializer.rb +149 -33
  88. data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
  89. data/spec/controllers/application_metal_controller_spec.rb +1 -1
  90. data/spec/controllers/applications_controller_spec.rb +95 -1
  91. data/spec/controllers/authorizations_controller_spec.rb +156 -75
  92. data/spec/controllers/protected_resources_controller_spec.rb +28 -20
  93. data/spec/controllers/token_info_controller_spec.rb +1 -1
  94. data/spec/controllers/tokens_controller_spec.rb +206 -38
  95. data/spec/dummy/config/application.rb +3 -1
  96. data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
  97. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -3
  98. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +1 -1
  99. data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +1 -1
  100. data/spec/generators/install_generator_spec.rb +1 -1
  101. data/spec/generators/previous_refresh_token_generator_spec.rb +2 -2
  102. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  103. data/spec/lib/config_spec.rb +104 -7
  104. data/spec/lib/doorkeeper_spec.rb +1 -1
  105. data/spec/lib/models/revocable_spec.rb +3 -3
  106. data/spec/lib/oauth/authorization_code_request_spec.rb +129 -115
  107. data/spec/lib/oauth/base_request_spec.rb +154 -135
  108. data/spec/lib/oauth/base_response_spec.rb +27 -29
  109. data/spec/lib/oauth/client/credentials_spec.rb +1 -1
  110. data/spec/lib/oauth/client_credentials/creator_spec.rb +43 -3
  111. data/spec/lib/oauth/client_credentials/issuer_spec.rb +12 -12
  112. data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
  113. data/spec/lib/oauth/client_credentials_integration_spec.rb +16 -18
  114. data/spec/lib/oauth/client_credentials_request_spec.rb +78 -80
  115. data/spec/lib/oauth/client_spec.rb +26 -26
  116. data/spec/lib/oauth/code_request_spec.rb +34 -35
  117. data/spec/lib/oauth/code_response_spec.rb +21 -25
  118. data/spec/lib/oauth/error_response_spec.rb +42 -44
  119. data/spec/lib/oauth/error_spec.rb +12 -14
  120. data/spec/lib/oauth/forbidden_token_response_spec.rb +11 -13
  121. data/spec/lib/oauth/helpers/scope_checker_spec.rb +30 -18
  122. data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
  123. data/spec/lib/oauth/invalid_request_response_spec.rb +73 -0
  124. data/spec/lib/oauth/invalid_token_response_spec.rb +32 -34
  125. data/spec/lib/oauth/password_access_token_request_spec.rb +145 -147
  126. data/spec/lib/oauth/pre_authorization_spec.rb +161 -153
  127. data/spec/lib/oauth/refresh_token_request_spec.rb +138 -138
  128. data/spec/lib/oauth/scopes_spec.rb +104 -106
  129. data/spec/lib/oauth/token_request_spec.rb +117 -110
  130. data/spec/lib/oauth/token_response_spec.rb +71 -73
  131. data/spec/lib/oauth/token_spec.rb +121 -123
  132. data/spec/lib/server_spec.rb +0 -12
  133. data/spec/models/doorkeeper/access_grant_spec.rb +21 -4
  134. data/spec/models/doorkeeper/access_token_spec.rb +42 -11
  135. data/spec/models/doorkeeper/application_spec.rb +114 -77
  136. data/spec/requests/applications/applications_request_spec.rb +1 -1
  137. data/spec/requests/endpoints/authorization_spec.rb +26 -8
  138. data/spec/requests/endpoints/token_spec.rb +1 -1
  139. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
  140. data/spec/requests/flows/authorization_code_spec.rb +123 -45
  141. data/spec/requests/flows/client_credentials_spec.rb +38 -0
  142. data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
  143. data/spec/requests/flows/implicit_grant_spec.rb +9 -8
  144. data/spec/requests/flows/password_spec.rb +67 -10
  145. data/spec/requests/flows/refresh_token_spec.rb +20 -20
  146. data/spec/requests/flows/revoke_token_spec.rb +37 -23
  147. data/spec/spec_helper.rb +1 -4
  148. data/spec/support/doorkeeper_rspec.rb +1 -1
  149. data/spec/support/helpers/request_spec_helper.rb +14 -2
  150. data/spec/support/shared/controllers_shared_context.rb +33 -23
  151. data/spec/validators/redirect_uri_validator_spec.rb +41 -16
  152. metadata +19 -16
  153. data/.coveralls.yml +0 -1
  154. data/.github/ISSUE_TEMPLATE.md +0 -25
  155. data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  156. data/.gitignore +0 -20
  157. data/.gitlab-ci.yml +0 -16
  158. data/.hound.yml +0 -3
  159. data/.rspec +0 -1
  160. data/.rubocop.yml +0 -50
  161. data/.travis.yml +0 -35
  162. data/app/validators/redirect_uri_validator.rb +0 -50
  163. data/spec/support/http_method_shim.rb +0 -29
@@ -3,38 +3,146 @@
3
3
  require "spec_helper"
4
4
 
5
5
  describe Doorkeeper::TokensController do
6
- describe "when authorization has succeeded" do
7
- let(:token) { double(:token, authorize: true) }
6
+ let(:client) { FactoryBot.create :application }
7
+ let!(:user) { User.create!(name: "Joe", password: "sekret") }
8
8
 
9
- it "returns the authorization" do
10
- skip "verify need of these specs"
9
+ before do
10
+ Doorkeeper.configure do
11
+ resource_owner_from_credentials do
12
+ User.first
13
+ end
14
+ end
11
15
 
12
- expect(token).to receive(:authorization)
16
+ allow(Doorkeeper.configuration).to receive(:grant_flows).and_return(["password"])
17
+ end
13
18
 
14
- post :create
19
+ subject { JSON.parse(response.body) }
20
+
21
+ describe "POST #create" do
22
+ before do
23
+ post :create, params: {
24
+ client_id: client.uid,
25
+ client_secret: client.secret,
26
+ grant_type: "password",
27
+ }
28
+ end
29
+
30
+ it "responds after authorization" do
31
+ expect(response).to be_successful
32
+ end
33
+
34
+ it "includes access token in response" do
35
+ expect(subject["access_token"]).to eq(Doorkeeper::AccessToken.first.token)
36
+ end
37
+
38
+ it "includes token type in response" do
39
+ expect(subject["token_type"]).to eq("Bearer")
40
+ end
41
+
42
+ it "includes token expiration in response" do
43
+ expect(subject["expires_in"].to_i).to eq(Doorkeeper.configuration.access_token_expires_in)
44
+ end
45
+
46
+ it "issues the token for the current client" do
47
+ expect(Doorkeeper::AccessToken.first.application_id).to eq(client.id)
48
+ end
49
+
50
+ it "issues the token for the current resource owner" do
51
+ expect(Doorkeeper::AccessToken.first.resource_owner_id).to eq(user.id)
15
52
  end
16
53
  end
17
54
 
18
- describe "when authorization has failed" do
19
- it "returns the error response" do
20
- token = double(:token, authorize: false)
21
- allow(controller).to receive(:token) { token }
55
+ describe "POST #create with errors" do
56
+ before do
57
+ post :create, params: {
58
+ client_id: client.uid,
59
+ client_secret: "invalid",
60
+ grant_type: "password",
61
+ }
62
+ end
22
63
 
23
- post :create
64
+ it "responds after authorization" do
65
+ expect(response).to be_unauthorized
66
+ end
24
67
 
25
- expect(response.status).to eq 400
26
- expect(response.headers["WWW-Authenticate"]).to match(/Bearer/)
68
+ it "include error in response" do
69
+ expect(subject["error"]).to eq("invalid_client")
70
+ end
71
+
72
+ it "include error_description in response" do
73
+ expect(subject["error_description"]).to be
74
+ end
75
+
76
+ it "does not include access token in response" do
77
+ expect(subject["access_token"]).to be_nil
78
+ end
79
+
80
+ it "does not include token type in response" do
81
+ expect(subject["token_type"]).to be_nil
82
+ end
83
+
84
+ it "does not include token expiration in response" do
85
+ expect(subject["expires_in"]).to be_nil
86
+ end
87
+
88
+ it "does not issue any access token" do
89
+ expect(Doorkeeper::AccessToken.all).to be_empty
90
+ end
91
+ end
92
+
93
+ describe "POST #create with callbacks" do
94
+ after do
95
+ client.update_attribute :redirect_uri, "urn:ietf:wg:oauth:2.0:oob"
96
+ end
97
+
98
+ describe "when successful" do
99
+ after do
100
+ post :create, params: {
101
+ client_id: client.uid,
102
+ client_secret: client.secret,
103
+ grant_type: "password",
104
+ }
105
+ end
106
+
107
+ it "should call :before_successful_authorization callback" do
108
+ expect(Doorkeeper.configuration)
109
+ .to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class))
110
+ end
111
+
112
+ it "should call :after_successful_authorization callback" do
113
+ expect(Doorkeeper.configuration)
114
+ .to receive_message_chain(:after_successful_authorization, :call).with(instance_of(described_class))
115
+ end
116
+ end
117
+
118
+ describe "with errors" do
119
+ after do
120
+ post :create, params: {
121
+ client_id: client.uid,
122
+ client_secret: "invalid",
123
+ grant_type: "password",
124
+ }
125
+ end
126
+
127
+ it "should call :before_successful_authorization callback" do
128
+ expect(Doorkeeper.configuration)
129
+ .to receive_message_chain(:before_successful_authorization, :call).with(instance_of(described_class))
130
+ end
131
+
132
+ it "should not call :after_successful_authorization callback" do
133
+ expect(Doorkeeper.configuration).not_to receive(:after_successful_authorization)
134
+ end
27
135
  end
28
136
  end
29
137
 
30
- describe "when there is a failure due to a custom error" do
138
+ describe "POST #create with custom error" do
31
139
  it "returns the error response with a custom message" do
32
140
  # I18n looks for `doorkeeper.errors.messages.custom_message` in locale files
33
141
  custom_message = "my_message"
34
142
  allow(I18n).to receive(:translate)
35
143
  .with(
36
144
  custom_message,
37
- hash_including(scope: %i[doorkeeper errors messages])
145
+ hash_including(scope: %i[doorkeeper errors messages]),
38
146
  )
39
147
  .and_return("Authorization custom message")
40
148
 
@@ -58,7 +166,7 @@ describe Doorkeeper::TokensController do
58
166
  end
59
167
 
60
168
  # http://tools.ietf.org/html/rfc7009#section-2.2
61
- describe "revoking tokens" do
169
+ describe "POST #revoke" do
62
170
  let(:client) { FactoryBot.create(:application) }
63
171
  let(:access_token) { FactoryBot.create(:access_token, application: client) }
64
172
 
@@ -102,10 +210,10 @@ describe Doorkeeper::TokensController do
102
210
  let(:some_other_client) { FactoryBot.create(:application, confidential: true) }
103
211
  let(:oauth_client) { Doorkeeper::OAuth::Client.new(some_other_client) }
104
212
 
105
- it "returns 200" do
213
+ it "returns 403" do
106
214
  post :revoke, params: { token: access_token.token }
107
215
 
108
- expect(response.status).to eq 200
216
+ expect(response.status).to eq 403
109
217
  end
110
218
 
111
219
  it "does not revoke the access token" do
@@ -117,21 +225,7 @@ describe Doorkeeper::TokensController do
117
225
  end
118
226
  end
119
227
 
120
- describe "authorize response memoization" do
121
- it "memoizes the result of the authorization" do
122
- strategy = double(:strategy, authorize: true)
123
- expect(strategy).to receive(:authorize).once
124
- allow(controller).to receive(:strategy) { strategy }
125
- allow(controller).to receive(:create) do
126
- 2.times { controller.send :authorize_response }
127
- controller.render json: {}, status: :ok
128
- end
129
-
130
- post :create
131
- end
132
- end
133
-
134
- describe "when requested token introspection" do
228
+ describe "POST #introspect" do
135
229
  let(:client) { FactoryBot.create(:application) }
136
230
  let(:access_token) { FactoryBot.create(:access_token, application: client) }
137
231
  let(:token_for_introspection) { FactoryBot.create(:access_token, application: client) }
@@ -147,7 +241,7 @@ describe Doorkeeper::TokensController do
147
241
  end
148
242
  end
149
243
 
150
- context "authorized using valid Client Authentication" do
244
+ context "authorized using Client Credentials of the client that token is issued to" do
151
245
  it "responds with full token introspection" do
152
246
  request.headers["Authorization"] = basic_auth_header_for_client(client)
153
247
 
@@ -159,6 +253,26 @@ describe Doorkeeper::TokensController do
159
253
  end
160
254
  end
161
255
 
256
+ context "configured token introspection disabled" do
257
+ before do
258
+ Doorkeeper.configure do
259
+ orm DOORKEEPER_ORM
260
+ allow_token_introspection false
261
+ end
262
+ end
263
+
264
+ it "responds with invalid_token error" do
265
+ request.headers["Authorization"] = "Bearer #{access_token.token}"
266
+
267
+ post :introspect, params: { token: token_for_introspection.token }
268
+
269
+ response_status_should_be 401
270
+
271
+ should_not_have_json "active"
272
+ should_have_json "error", "invalid_token"
273
+ end
274
+ end
275
+
162
276
  context "using custom introspection response" do
163
277
  before do
164
278
  Doorkeeper.configure do
@@ -212,12 +326,64 @@ describe Doorkeeper::TokensController do
212
326
  end
213
327
  end
214
328
 
329
+ context "introspection request authorized by a client and allow_token_introspection is true" do
330
+ let(:different_client) { FactoryBot.create(:application) }
331
+
332
+ before do
333
+ allow(Doorkeeper.configuration).to receive(:allow_token_introspection).and_return(proc do
334
+ true
335
+ end)
336
+ end
337
+
338
+ it "responds with full token introspection" do
339
+ request.headers["Authorization"] = basic_auth_header_for_client(different_client)
340
+
341
+ post :introspect, params: { token: token_for_introspection.token }
342
+
343
+ should_have_json "active", true
344
+ expect(json_response).to include("client_id", "token_type", "exp", "iat")
345
+ should_have_json "client_id", client.uid
346
+ end
347
+ end
348
+
349
+ context "allow_token_introspection requires authorized token with special scope" do
350
+ let(:access_token) { FactoryBot.create(:access_token, scopes: "introspection") }
351
+
352
+ before do
353
+ allow(Doorkeeper.configuration).to receive(:allow_token_introspection).and_return(proc do |_token, _client, authorized_token|
354
+ authorized_token.scopes.include?("introspection")
355
+ end)
356
+ end
357
+
358
+ it "responds with full token introspection if authorized token has introspection scope" do
359
+ request.headers["Authorization"] = "Bearer #{access_token.token}"
360
+
361
+ post :introspect, params: { token: token_for_introspection.token }
362
+
363
+ should_have_json "active", true
364
+ expect(json_response).to include("client_id", "token_type", "exp", "iat")
365
+ end
366
+
367
+ it "responds with invalid_token error if authorized token doesn't have introspection scope" do
368
+ access_token.update(scopes: "read write")
369
+
370
+ request.headers["Authorization"] = "Bearer #{access_token.token}"
371
+
372
+ post :introspect, params: { token: token_for_introspection.token }
373
+
374
+ response_status_should_be 401
375
+
376
+ should_not_have_json "active"
377
+ should_have_json "error", "invalid_token"
378
+ end
379
+ end
380
+
215
381
  context "authorized using invalid Bearer token" do
216
382
  let(:access_token) do
217
383
  FactoryBot.create(:access_token, application: client, revoked_at: 1.day.ago)
218
384
  end
219
385
 
220
- it "responds with invalid token error" do
386
+ it "responds with invalid_token error" do
221
387
  request.headers["Authorization"] = "Bearer #{access_token.token}"
222
388
 
223
389
  post :introspect, params: { token: token_for_introspection.token }
@@ -272,13 +438,15 @@ describe Doorkeeper::TokensController do
272
438
  end
273
439
 
274
440
  context "authorized using valid Bearer token" do
275
- it "responds with only active state" do
441
+ it "responds with invalid_token error" do
276
442
  request.headers["Authorization"] = "Bearer #{access_token.token}"
277
443
 
278
444
  post :introspect, params: { token: SecureRandom.hex(16) }
279
445
 
280
- should_have_json "active", false
281
- expect(json_response).not_to include("client_id", "token_type", "exp", "iat")
446
+ response_status_should_be 401
447
+
448
+ should_not_have_json "active"
449
+ should_have_json "error", "invalid_token"
282
450
  end
283
451
  end
284
452
  end
@@ -5,11 +5,13 @@ require "rails"
5
5
  %w[
6
6
  action_controller/railtie
7
7
  action_view/railtie
8
+ action_cable/engine
8
9
  sprockets/railtie
9
10
  ].each do |railtie|
10
11
  begin
11
12
  require railtie
12
- rescue LoadError
13
+ rescue LoadError => e
14
+ puts "Error loading '#{railtie}' (#{e.message})"
13
15
  end
14
16
  end
15
17
 
@@ -65,15 +65,6 @@ Doorkeeper.configure do
65
65
  # Check out the wiki for more information on customization
66
66
  # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
67
67
 
68
- # Change the native redirect uri for client apps
69
- # When clients register with the following redirect uri, they won't be redirected to any server and
70
- # the authorization code will be displayed within the provider
71
- # The value can be any string. Use nil to disable this feature.
72
- # When disabled, clients must provide a valid URL
73
- # (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
74
- #
75
- # native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
76
-
77
68
  # Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
78
69
  # by default in non-development environments). OAuth2 delegates security in
79
70
  # communication to the HTTPS protocol so it is wise to keep this enabled.
@@ -116,6 +107,60 @@ Doorkeeper.configure do
116
107
  # client.superapp? or resource_owner.admin?
117
108
  # end
118
109
 
110
+ # Configure custom constraints for the Token Introspection request.
111
+ # By default this configuration option allows to introspect a token by another
112
+ # token of the same application, OR to introspect the token that belongs to
113
+ # authorized client (from authenticated client) OR when token doesn't
114
+ # belong to any client (public token). Otherwise requester has no access to the
115
+ # introspection and it will return response as stated in the RFC.
116
+ #
117
+ # Block arguments:
118
+ #
119
+ # @param token [Doorkeeper::AccessToken]
120
+ # token to be introspected
121
+ #
122
+ # @param authorized_client [Doorkeeper::Application]
123
+ # authorized client (if request is authorized using Basic auth with
124
+ # Client Credentials for example)
125
+ #
126
+ # @param authorized_token [Doorkeeper::AccessToken]
127
+ # Bearer token used to authorize the request
128
+ #
129
+ # In case the block returns `nil` or `false` introspection responses with 401 status code
130
+ # when using authorized token to introspect, or you'll get 200 with { "active": false } body
131
+ # when using authorized client to introspect as stated in the
132
+ # RFC 7662 section 2.2. Introspection Response.
133
+ #
134
+ # Using with caution:
135
+ # Keep in mind that these three parameters pass to block can be nil as following case:
136
+ # `authorized_client` is nil if and only if `authorized_token` is present, and vice versa.
137
+ # `token` will be nil if and only if `authorized_token` is present.
138
+ # So remember to use `&` or check if it is present before calling method on
139
+ # them to make sure you doesn't get NoMethodError exception.
140
+ #
141
+ # You can define your custom check:
142
+ #
143
+ # allow_token_introspection do |token, authorized_client, authorized_token|
144
+ # if authorized_token
145
+ # # customize: require `introspection` scope
146
+ # authorized_token.application == token&.application ||
147
+ # authorized_token.scopes.include?("introspection")
148
+ # elsif token.application
149
+ # # `protected_resource` is a new database boolean column, for example
150
+ # authorized_client == token.application || authorized_client.protected_resource?
151
+ # else
152
+ # # public token (when token.application is nil, token doesn't belong to any application)
153
+ # true
154
+ # end
155
+ # end
156
+ #
157
+ # Or you can completely disable any token introspection:
158
+ #
159
+ # allow_token_introspection false
160
+ #
161
+ # If you need to block the request at all, then configure your routes.rb or web-server
162
+ # like nginx to forbid the request.
163
+
119
164
  # WWW-Authenticate Realm (default "Doorkeeper").
120
165
  realm "Doorkeeper"
121
166
  end
@@ -25,14 +25,14 @@ 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
32
32
  add_foreign_key(
33
33
  :oauth_access_grants,
34
34
  :oauth_applications,
35
- column: :application_id
35
+ column: :application_id,
36
36
  )
37
37
 
38
38
  create_table :oauth_access_tokens do |t|
@@ -59,7 +59,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration[4.2]
59
59
  add_foreign_key(
60
60
  :oauth_access_tokens,
61
61
  :oauth_applications,
62
- column: :application_id
62
+ column: :application_id,
63
63
  )
64
64
 
65
65
  # Uncomment below to ensure a valid reference to the resource owner's table
@@ -7,7 +7,7 @@ class AddPreviousRefreshTokenToAccessTokens < ActiveRecord::Migration[4.2]
7
7
  :previous_refresh_token,
8
8
  :string,
9
9
  default: "",
10
- null: false
10
+ null: false,
11
11
  )
12
12
  end
13
13
  end
@@ -7,7 +7,7 @@ class AddConfidentialToApplications < ActiveRecord::Migration[5.1]
7
7
  :confidential,
8
8
  :boolean,
9
9
  null: false,
10
- default: true # maintaining backwards compatibility: require secrets
10
+ default: true, # maintaining backwards compatibility: require secrets
11
11
  )
12
12
  end
13
13
  end
@@ -16,7 +16,7 @@ describe "Doorkeeper::InstallGenerator" do
16
16
  FileUtils.mkdir(::File.expand_path("db", Pathname(destination_root)))
17
17
  FileUtils.copy_file(
18
18
  ::File.expand_path("../templates/routes.rb", __FILE__),
19
- ::File.expand_path("config/routes.rb", Pathname.new(destination_root))
19
+ ::File.expand_path("config/routes.rb", Pathname.new(destination_root)),
20
20
  )
21
21
  run_generator
22
22
  end
@@ -14,7 +14,7 @@ describe "Doorkeeper::PreviousRefreshTokenGenerator" do
14
14
  prepare_destination
15
15
 
16
16
  allow_any_instance_of(Doorkeeper::PreviousRefreshTokenGenerator).to(
17
- receive(:no_previous_refresh_token_column?).and_return(true)
17
+ receive(:no_previous_refresh_token_column?).and_return(true),
18
18
  )
19
19
  end
20
20
 
@@ -32,7 +32,7 @@ describe "Doorkeeper::PreviousRefreshTokenGenerator" do
32
32
  context "already exist" do
33
33
  it "does not create a migration" do
34
34
  allow_any_instance_of(Doorkeeper::PreviousRefreshTokenGenerator).to(
35
- receive(:no_previous_refresh_token_column?).and_call_original
35
+ receive(:no_previous_refresh_token_column?).and_call_original,
36
36
  )
37
37
 
38
38
  run_generator
@@ -11,7 +11,7 @@ describe Doorkeeper::DashboardHelper do
11
11
  it "returns error messages" do
12
12
  messages.each do |message|
13
13
  expect(helper.doorkeeper_errors_for(object, :method)).to include(
14
- message.capitalize
14
+ message.capitalize,
15
15
  )
16
16
  end
17
17
  end
@@ -22,7 +22,7 @@ describe Doorkeeper, "configuration" do
22
22
  end
23
23
 
24
24
  expect(Rails.logger).to receive(:warn).with(
25
- I18n.t("doorkeeper.errors.messages.resource_owner_authenticator_not_configured")
25
+ I18n.t("doorkeeper.errors.messages.resource_owner_authenticator_not_configured"),
26
26
  )
27
27
  subject.authenticate_resource_owner.call(nil)
28
28
  end
@@ -45,7 +45,7 @@ describe Doorkeeper, "configuration" do
45
45
  end
46
46
 
47
47
  expect(Rails.logger).to receive(:warn).with(
48
- I18n.t("doorkeeper.errors.messages.credential_flow_not_configured")
48
+ I18n.t("doorkeeper.errors.messages.credential_flow_not_configured"),
49
49
  )
50
50
  subject.resource_owner_from_credentials.call(nil)
51
51
  end
@@ -471,7 +471,7 @@ describe Doorkeeper, "configuration" do
471
471
  describe "access_token_generator" do
472
472
  it "is 'Doorkeeper::OAuth::Helpers::UniqueToken' by default" do
473
473
  expect(Doorkeeper.configuration.access_token_generator).to(
474
- eq("Doorkeeper::OAuth::Helpers::UniqueToken")
474
+ eq("Doorkeeper::OAuth::Helpers::UniqueToken"),
475
475
  )
476
476
  end
477
477
 
@@ -502,7 +502,21 @@ describe Doorkeeper, "configuration" do
502
502
 
503
503
  describe "base_controller" do
504
504
  context "default" do
505
- it { expect(Doorkeeper.configuration.base_controller).to eq("ActionController::Base") }
505
+ it { expect(Doorkeeper.configuration.base_controller).to be_an_instance_of(Proc) }
506
+
507
+ it "resolves to a ApplicationController::Base in default mode" do
508
+ expect(Doorkeeper.configuration.resolve_controller(:base))
509
+ .to eq(ActionController::Base)
510
+ end
511
+
512
+ it "resolves to a ApplicationController::API in api_only mode" do
513
+ Doorkeeper.configure do
514
+ api_only
515
+ end
516
+
517
+ expect(Doorkeeper.configuration.resolve_controller(:base))
518
+ .to eq(ActionController::API)
519
+ end
506
520
  end
507
521
 
508
522
  context "custom" do
@@ -517,7 +531,26 @@ describe Doorkeeper, "configuration" do
517
531
  end
518
532
  end
519
533
 
534
+ describe "base_metal_controller" do
535
+ context "default" do
536
+ it { expect(Doorkeeper.configuration.base_metal_controller).to eq("ActionController::API") }
537
+ end
538
+
539
+ context "custom" do
540
+ before do
541
+ Doorkeeper.configure do
542
+ orm DOORKEEPER_ORM
543
+ base_metal_controller { "ApplicationController" }
544
+ end
545
+ end
546
+
547
+ it { expect(Doorkeeper.configuration.resolve_controller(:base_metal)).to eq(ApplicationController) }
548
+ end
549
+ end
550
+
520
551
  if DOORKEEPER_ORM == :active_record
552
+ class FakeCustomModel; end
553
+
521
554
  describe "active_record_options" do
522
555
  let(:models) { [Doorkeeper::AccessGrant, Doorkeeper::AccessToken, Doorkeeper::Application] }
523
556
 
@@ -535,11 +568,62 @@ describe Doorkeeper, "configuration" do
535
568
  Doorkeeper.configure do
536
569
  orm DOORKEEPER_ORM
537
570
  active_record_options(
538
- establish_connection: Rails.configuration.database_configuration[Rails.env]
571
+ establish_connection: Rails.configuration.database_configuration[Rails.env],
539
572
  )
540
573
  end
541
574
  end
542
575
  end
576
+
577
+ describe "access_token_class" do
578
+ it "uses default doorkeeper value" do
579
+ expect(subject.access_token_class).to eq("Doorkeeper::AccessToken")
580
+ expect(subject.access_token_model).to be(Doorkeeper::AccessToken)
581
+ end
582
+
583
+ it "can change the value" do
584
+ Doorkeeper.configure do
585
+ orm DOORKEEPER_ORM
586
+ access_token_class "FakeCustomModel"
587
+ end
588
+
589
+ expect(subject.access_token_class).to eq("FakeCustomModel")
590
+ expect(subject.access_token_model).to be(FakeCustomModel)
591
+ end
592
+ end
593
+
594
+ describe "access_grant_class" do
595
+ it "uses default doorkeeper value" do
596
+ expect(subject.access_grant_class).to eq("Doorkeeper::AccessGrant")
597
+ expect(subject.access_grant_model).to be(Doorkeeper::AccessGrant)
598
+ end
599
+
600
+ it "can change the value" do
601
+ Doorkeeper.configure do
602
+ orm DOORKEEPER_ORM
603
+ access_grant_class "FakeCustomModel"
604
+ end
605
+
606
+ expect(subject.access_grant_class).to eq("FakeCustomModel")
607
+ expect(subject.access_grant_model).to be(FakeCustomModel)
608
+ end
609
+ end
610
+
611
+ describe "application_class" do
612
+ it "uses default doorkeeper value" do
613
+ expect(subject.application_class).to eq("Doorkeeper::Application")
614
+ expect(subject.application_model).to be(Doorkeeper::Application)
615
+ end
616
+
617
+ it "can change the value" do
618
+ Doorkeeper.configure do
619
+ orm DOORKEEPER_ORM
620
+ application_class "FakeCustomModel"
621
+ end
622
+
623
+ expect(subject.application_class).to eq("FakeCustomModel")
624
+ expect(subject.application_model).to be(FakeCustomModel)
625
+ end
626
+ end
543
627
  end
544
628
 
545
629
  describe "api_only" do
@@ -620,8 +704,10 @@ describe Doorkeeper, "configuration" do
620
704
  Doorkeeper.configure do
621
705
  hash_token_secrets using: "Doorkeeper::SecretStoring::BCrypt"
622
706
  end
623
- end.to raise_error(ArgumentError,
624
- /can only be used for storing application secrets/)
707
+ end.to raise_error(
708
+ ArgumentError,
709
+ /can only be used for storing application secrets/,
710
+ )
625
711
  end
626
712
  end
627
713
 
@@ -694,4 +780,15 @@ describe Doorkeeper, "configuration" do
694
780
  end
695
781
  end
696
782
  end
783
+
784
+ describe "options deprecation" do
785
+ it "prints a warning message when an option is deprecated" do
786
+ expect(Kernel).to receive(:warn).with(
787
+ "[DOORKEEPER] native_redirect_uri has been deprecated and will soon be removed",
788
+ )
789
+ Doorkeeper.configure do
790
+ native_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
791
+ end
792
+ end
793
+ end
697
794
  end
@@ -7,7 +7,7 @@ describe Doorkeeper do
7
7
  let(:request) { double }
8
8
 
9
9
  it "calls OAuth::Token#authenticate" do
10
- token_strategies = Doorkeeper.configuration.access_token_methods
10
+ token_strategies = Doorkeeper.config.access_token_methods
11
11
 
12
12
  expect(Doorkeeper::OAuth::Token).to receive(:authenticate)
13
13
  .with(request, *token_strategies)