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
@@ -40,6 +40,21 @@ module Doorkeeper
40
40
  find_by_plaintext_token(:refresh_token, refresh_token)
41
41
  end
42
42
 
43
+ # Returns an instance of the Doorkeeper::AccessToken
44
+ # found by previous refresh token. Keep in mind that value
45
+ # of the previous_refresh_token isn't encrypted using
46
+ # secrets strategy.
47
+ #
48
+ # @param previous_refresh_token [#to_s]
49
+ # previous refresh token value (any object that responds to `#to_s`)
50
+ #
51
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
52
+ # if there is no record with such refresh token
53
+ #
54
+ def by_previous_refresh_token(previous_refresh_token)
55
+ find_by(refresh_token: previous_refresh_token)
56
+ end
57
+
43
58
  # Revokes AccessToken records that have not been revoked and associated
44
59
  # with the specific Application and Resource Owner.
45
60
  #
@@ -49,10 +64,11 @@ module Doorkeeper
49
64
  # instance of the Resource Owner model
50
65
  #
51
66
  def revoke_all_for(application_id, resource_owner, clock = Time)
52
- where(application_id: application_id,
53
- resource_owner_id: resource_owner.id,
54
- revoked_at: nil)
55
- .update_all(revoked_at: clock.now.utc)
67
+ where(
68
+ application_id: application_id,
69
+ resource_owner_id: resource_owner.id,
70
+ revoked_at: nil,
71
+ ).update_all(revoked_at: clock.now.utc)
56
72
  end
57
73
 
58
74
  # Looking for not revoked Access Token with a matching set of scopes
@@ -76,9 +92,50 @@ module Doorkeeper
76
92
  end
77
93
 
78
94
  tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
79
- tokens.detect do |token|
80
- scopes_match?(token.scopes, scopes, application.try(:scopes))
95
+ find_matching_token(tokens, application, scopes)
96
+ end
97
+
98
+ # Interface to enumerate access token records in batches in order not
99
+ # to bloat the memory. Could be overloaded in any ORM extension.
100
+ #
101
+ def find_access_token_in_batches(relation, *args, &block)
102
+ relation.find_in_batches(*args, &block)
103
+ end
104
+
105
+ # Enumerates AccessToken records in batches to find a matching token.
106
+ # Batching is required in order not to pollute the memory if Application
107
+ # has huge amount of associated records.
108
+ #
109
+ # ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
110
+ # database sort by created_at, so we need to load all the matching records,
111
+ # sort them and find latest one. Probably it would be better to rewrite this
112
+ # query using Time math if possible, but we n eed to consider ORM and
113
+ # different databases support.
114
+ #
115
+ # @param relation [ActiveRecord::Relation]
116
+ # Access tokens relation
117
+ # @param application [Doorkeeper::Application]
118
+ # Application instance
119
+ # @param scopes [String, Doorkeeper::OAuth::Scopes]
120
+ # set of scopes
121
+ #
122
+ # @return [Doorkeeper::AccessToken, nil] Access Token instance or
123
+ # nil if matching record was not found
124
+ #
125
+ def find_matching_token(relation, application, scopes)
126
+ return nil unless relation
127
+
128
+ matching_tokens = []
129
+
130
+ find_access_token_in_batches(relation) do |batch|
131
+ tokens = batch.select do |token|
132
+ scopes_match?(token.scopes, scopes, application.try(:scopes))
133
+ end
134
+
135
+ matching_tokens.concat(tokens)
81
136
  end
137
+
138
+ matching_tokens.max_by(&:created_at)
82
139
  end
83
140
 
84
141
  # Checks whether the token scopes match the scopes from the parameters
@@ -101,8 +158,8 @@ module Doorkeeper
101
158
  (token_scopes.sort == param_scopes.sort) &&
102
159
  Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
103
160
  scope_str: param_scopes.to_s,
104
- server_scopes: Doorkeeper.configuration.scopes,
105
- app_scopes: app_scopes
161
+ server_scopes: Doorkeeper.config.scopes,
162
+ app_scopes: app_scopes,
106
163
  )
107
164
  end
108
165
 
@@ -124,7 +181,7 @@ module Doorkeeper
124
181
  # @return [Doorkeeper::AccessToken] existing record or a new one
125
182
  #
126
183
  def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
127
- if Doorkeeper.configuration.reuse_access_token
184
+ if Doorkeeper.config.reuse_access_token
128
185
  access_token = matching_token_for(application, resource_owner_id, scopes)
129
186
 
130
187
  return access_token if access_token&.reusable?
@@ -135,7 +192,7 @@ module Doorkeeper
135
192
  resource_owner_id: resource_owner_id,
136
193
  scopes: scopes.to_s,
137
194
  expires_in: expires_in,
138
- use_refresh_token: use_refresh_token
195
+ use_refresh_token: use_refresh_token,
139
196
  )
140
197
  end
141
198
 
@@ -150,10 +207,11 @@ module Doorkeeper
150
207
  # @return [Doorkeeper::AccessToken] array of matching AccessToken objects
151
208
  #
152
209
  def authorized_tokens_for(application_id, resource_owner_id)
153
- ordered_by(:created_at, :desc)
154
- .where(application_id: application_id,
155
- resource_owner_id: resource_owner_id,
156
- revoked_at: nil)
210
+ where(
211
+ application_id: application_id,
212
+ resource_owner_id: resource_owner_id,
213
+ revoked_at: nil,
214
+ )
157
215
  end
158
216
 
159
217
  # Convenience method for backwards-compatibility, return the last
@@ -168,21 +226,22 @@ module Doorkeeper
168
226
  # nil if nothing was found
169
227
  #
170
228
  def last_authorized_token_for(application_id, resource_owner_id)
171
- authorized_tokens_for(application_id, resource_owner_id).first
229
+ authorized_tokens_for(application_id, resource_owner_id)
230
+ .ordered_by(:created_at, :desc).first
172
231
  end
173
232
 
174
233
  ##
175
234
  # Determines the secret storing transformer
176
235
  # Unless configured otherwise, uses the plain secret strategy
177
236
  def secret_strategy
178
- ::Doorkeeper.configuration.token_secret_strategy
237
+ ::Doorkeeper.config.token_secret_strategy
179
238
  end
180
239
 
181
240
  ##
182
241
  # Determine the fallback storing strategy
183
242
  # Unless configured, there will be no fallback
184
243
  def fallback_secret_strategy
185
- ::Doorkeeper.configuration.token_secret_fallback_strategy
244
+ ::Doorkeeper.config.token_secret_fallback_strategy
186
245
  end
187
246
  end
188
247
 
@@ -259,8 +318,28 @@ module Doorkeeper
259
318
  end
260
319
  end
261
320
 
321
+ # Revokes token with `:refresh_token` equal to `:previous_refresh_token`
322
+ # and clears `:previous_refresh_token` attribute.
323
+ #
324
+ def revoke_previous_refresh_token!
325
+ return unless self.class.refresh_token_revoked_on_use?
326
+
327
+ old_refresh_token&.revoke
328
+ update_attribute :previous_refresh_token, ""
329
+ end
330
+
262
331
  private
263
332
 
333
+ # Searches for Access Token record with `:refresh_token` equal to
334
+ # `:previous_refresh_token` value.
335
+ #
336
+ # @return [Doorkeeper::AccessToken, nil]
337
+ # Access Token record or nil if nothing found
338
+ #
339
+ def old_refresh_token
340
+ @old_refresh_token ||= self.class.by_previous_refresh_token(previous_refresh_token)
341
+ end
342
+
264
343
  # Generates refresh token with UniqueToken generator.
265
344
  #
266
345
  # @return [String] refresh token value
@@ -271,7 +350,7 @@ module Doorkeeper
271
350
  end
272
351
 
273
352
  # Generates and sets the token value with the
274
- # configured Generator class (see Doorkeeper.configuration).
353
+ # configured Generator class (see Doorkeeper.config).
275
354
  #
276
355
  # @return [String] generated token value
277
356
  #
@@ -288,7 +367,7 @@ module Doorkeeper
288
367
  scopes: scopes,
289
368
  application: application,
290
369
  expires_in: expires_in,
291
- created_at: created_at
370
+ created_at: created_at,
292
371
  )
293
372
 
294
373
  secret_strategy.store_secret(self, :token, @raw_token)
@@ -296,7 +375,7 @@ module Doorkeeper
296
375
  end
297
376
 
298
377
  def token_generator
299
- generator_name = Doorkeeper.configuration.access_token_generator
378
+ generator_name = Doorkeeper.config.access_token_generator
300
379
  generator = generator_name.constantize
301
380
 
302
381
  return generator if generator.respond_to?(:generate)
@@ -47,14 +47,14 @@ module Doorkeeper
47
47
  # Determines the secret storing transformer
48
48
  # Unless configured otherwise, uses the plain secret strategy
49
49
  def secret_strategy
50
- ::Doorkeeper.configuration.application_secret_strategy
50
+ ::Doorkeeper.config.application_secret_strategy
51
51
  end
52
52
 
53
53
  ##
54
54
  # Determine the fallback storing strategy
55
55
  # Unless configured, there will be no fallback
56
56
  def fallback_secret_strategy
57
- ::Doorkeeper.configuration.application_secret_fallback_strategy
57
+ ::Doorkeeper.config.application_secret_fallback_strategy
58
58
  end
59
59
  end
60
60
 
@@ -72,7 +72,7 @@ module Doorkeeper
72
72
  # @param input [#to_s] Plain secret provided by user
73
73
  # (any object that responds to `#to_s`)
74
74
  #
75
- # @return [true] Whether the given secret matches the stored secret
75
+ # @return [Boolean] Whether the given secret matches the stored secret
76
76
  # of this application.
77
77
  #
78
78
  def secret_matches?(input)
@@ -11,7 +11,7 @@ module Doorkeeper
11
11
  end
12
12
 
13
13
  def validate_owner?
14
- Doorkeeper.configuration.confirm_application_owner?
14
+ Doorkeeper.config.confirm_application_owner?
15
15
  end
16
16
  end
17
17
  end
@@ -11,7 +11,7 @@ module Doorkeeper
11
11
  return false if expired?
12
12
  return true unless expires_in
13
13
 
14
- threshold_limit = 100 - Doorkeeper.configuration.token_reuse_limit
14
+ threshold_limit = 100 - Doorkeeper.config.token_reuse_limit
15
15
  expires_in_seconds >= threshold_limit * expires_in / 100
16
16
  end
17
17
  end
@@ -19,33 +19,6 @@ module Doorkeeper
19
19
  def revoked?
20
20
  !!(revoked_at && revoked_at <= Time.now.utc)
21
21
  end
22
-
23
- # Revokes token with `:refresh_token` equal to `:previous_refresh_token`
24
- # and clears `:previous_refresh_token` attribute.
25
- #
26
- def revoke_previous_refresh_token!
27
- return unless refresh_token_revoked_on_use?
28
-
29
- old_refresh_token&.revoke
30
- update_attribute :previous_refresh_token, ""
31
- end
32
-
33
- private
34
-
35
- # Searches for Access Token record with `:refresh_token` equal to
36
- # `:previous_refresh_token` value.
37
- #
38
- # @return [Doorkeeper::AccessToken, nil]
39
- # Access Token record or nil if nothing found
40
- #
41
- def old_refresh_token
42
- @old_refresh_token ||=
43
- AccessToken.by_refresh_token(previous_refresh_token)
44
- end
45
-
46
- def refresh_token_revoked_on_use?
47
- AccessToken.refresh_token_revoked_on_use?
48
- end
49
22
  end
50
23
  end
51
24
  end
@@ -12,29 +12,27 @@ module Doorkeeper
12
12
  end
13
13
 
14
14
  def issue_token
15
- @token ||= AccessGrant.create! access_grant_attributes
15
+ @token ||= Doorkeeper.config.access_grant_model.create!(access_grant_attributes)
16
16
  end
17
17
 
18
- def native_redirect
18
+ def oob_redirect
19
19
  { action: :show, code: token.plaintext_token }
20
20
  end
21
21
 
22
- def configuration
23
- Doorkeeper.configuration
24
- end
25
-
26
22
  private
27
23
 
28
24
  def authorization_code_expires_in
29
- configuration.authorization_code_expires_in
25
+ Doorkeeper.config.authorization_code_expires_in
30
26
  end
31
27
 
32
28
  def access_grant_attributes
33
- pkce_attributes.merge application_id: pre_auth.client.id,
34
- resource_owner_id: resource_owner.id,
35
- expires_in: authorization_code_expires_in,
36
- redirect_uri: pre_auth.redirect_uri,
37
- scopes: pre_auth.scopes.to_s
29
+ pkce_attributes.merge(
30
+ application_id: pre_auth.client.id,
31
+ resource_owner_id: resource_owner.id,
32
+ expires_in: authorization_code_expires_in,
33
+ redirect_uri: pre_auth.redirect_uri,
34
+ scopes: pre_auth.scopes.to_s,
35
+ )
38
36
  end
39
37
 
40
38
  def pkce_attributes
@@ -46,10 +44,10 @@ module Doorkeeper
46
44
  }
47
45
  end
48
46
 
49
- # ensures firstly, if migration with additional pcke columns was
47
+ # Ensures firstly, if migration with additional PKCE columns was
50
48
  # generated and migrated
51
49
  def pkce_supported?
52
- Doorkeeper::AccessGrant.pkce_supported?
50
+ Doorkeeper.config.access_grant_model.pkce_supported?
53
51
  end
54
52
  end
55
53
  end
@@ -19,7 +19,7 @@ module Doorkeeper
19
19
  Doorkeeper::OAuth::Authorization::Context.new(
20
20
  oauth_client,
21
21
  grant_type,
22
- scopes
22
+ scopes,
23
23
  )
24
24
  end
25
25
 
@@ -35,7 +35,7 @@ module Doorkeeper
35
35
  end
36
36
 
37
37
  def refresh_token_enabled?(server, context)
38
- if server.refresh_token_enabled?.respond_to? :call
38
+ if server.refresh_token_enabled?.respond_to?(:call)
39
39
  server.refresh_token_enabled?.call(context)
40
40
  else
41
41
  !!server.refresh_token_enabled?
@@ -49,21 +49,24 @@ module Doorkeeper
49
49
  end
50
50
 
51
51
  def issue_token
52
+ return @token if defined?(@token)
53
+
52
54
  context = self.class.build_context(
53
55
  pre_auth.client,
54
56
  Doorkeeper::OAuth::IMPLICIT,
55
- pre_auth.scopes
57
+ pre_auth.scopes,
56
58
  )
57
- @token ||= AccessToken.find_or_create_for(
59
+
60
+ @token = configuration.access_token_model.find_or_create_for(
58
61
  pre_auth.client,
59
62
  resource_owner.id,
60
63
  pre_auth.scopes,
61
64
  self.class.access_token_expires_in(configuration, context),
62
- false
65
+ false,
63
66
  )
64
67
  end
65
68
 
66
- def native_redirect
69
+ def oob_redirect
67
70
  {
68
71
  controller: controller,
69
72
  action: :show,
@@ -74,7 +77,7 @@ module Doorkeeper
74
77
  private
75
78
 
76
79
  def configuration
77
- Doorkeeper.configuration
80
+ Doorkeeper.config
78
81
  end
79
82
 
80
83
  def controller
@@ -3,7 +3,8 @@
3
3
  module Doorkeeper
4
4
  module OAuth
5
5
  class AuthorizationCodeRequest < BaseRequest
6
- validate :attributes, error: :invalid_request
6
+ validate :pkce_support, error: :invalid_request
7
+ validate :params, error: :invalid_request
7
8
  validate :client, error: :invalid_client
8
9
  validate :grant, error: :invalid_grant
9
10
  # @see https://tools.ietf.org/html/rfc6749#section-5.2
@@ -12,6 +13,7 @@ module Doorkeeper
12
13
 
13
14
  attr_accessor :server, :grant, :client, :redirect_uri, :access_token,
14
15
  :code_verifier
16
+ attr_reader :invalid_request_reason, :missing_param
15
17
 
16
18
  def initialize(server, grant, client, parameters = {})
17
19
  @server = server
@@ -24,29 +26,39 @@ module Doorkeeper
24
26
 
25
27
  private
26
28
 
27
- def client_by_uid(parameters)
28
- Doorkeeper::Application.by_uid(parameters[:client_id])
29
- end
30
-
31
29
  def before_successful_response
32
30
  grant.transaction do
33
31
  grant.lock!
34
32
  raise Errors::InvalidGrantReuse if grant.revoked?
35
33
 
36
34
  grant.revoke
37
- find_or_create_access_token(grant.application,
38
- grant.resource_owner_id,
39
- grant.scopes,
40
- server)
35
+
36
+ find_or_create_access_token(
37
+ grant.application,
38
+ grant.resource_owner_id,
39
+ grant.scopes,
40
+ server,
41
+ )
41
42
  end
42
43
  super
43
44
  end
44
45
 
45
- def validate_attributes
46
- return false if grant&.uses_pkce? && code_verifier.blank?
47
- return false if grant && !grant.pkce_supported? && !code_verifier.blank?
46
+ def validate_pkce_support
47
+ @invalid_request_reason = :not_support_pkce if grant &&
48
+ !grant.pkce_supported? &&
49
+ code_verifier.present?
50
+
51
+ @invalid_request_reason.nil?
52
+ end
53
+
54
+ def validate_params
55
+ @missing_param = if grant&.uses_pkce? && code_verifier.blank?
56
+ :code_verifier
57
+ elsif redirect_uri.blank?
58
+ :redirect_uri
59
+ end
48
60
 
49
- redirect_uri.present?
61
+ @missing_param.nil?
50
62
  end
51
63
 
52
64
  def validate_client
@@ -62,7 +74,7 @@ module Doorkeeper
62
74
  def validate_redirect_uri
63
75
  Helpers::URIChecker.valid_for_authorization?(
64
76
  redirect_uri,
65
- grant.redirect_uri
77
+ grant.redirect_uri,
66
78
  )
67
79
  end
68
80
 
@@ -73,13 +85,17 @@ module Doorkeeper
73
85
  return false unless grant.pkce_supported?
74
86
 
75
87
  if grant.code_challenge_method == "S256"
76
- grant.code_challenge == AccessGrant.generate_code_challenge(code_verifier)
88
+ grant.code_challenge == generate_code_challenge(code_verifier)
77
89
  elsif grant.code_challenge_method == "plain"
78
90
  grant.code_challenge == code_verifier
79
91
  else
80
92
  false
81
93
  end
82
94
  end
95
+
96
+ def generate_code_challenge(code_verifier)
97
+ server_config.access_grant_model.generate_code_challenge(code_verifier)
98
+ end
83
99
  end
84
100
  end
85
101
  end
@@ -15,6 +15,8 @@ module Doorkeeper
15
15
  @response = TokenResponse.new(access_token)
16
16
  after_successful_response
17
17
  @response
18
+ elsif error == :invalid_request
19
+ @response = InvalidRequestResponse.from_request(self)
18
20
  else
19
21
  @response = ErrorResponse.from_request(self)
20
22
  end
@@ -34,21 +36,25 @@ module Doorkeeper
34
36
 
35
37
  def find_or_create_access_token(client, resource_owner_id, scopes, server)
36
38
  context = Authorization::Token.build_context(client, grant_type, scopes)
37
- @access_token = AccessToken.find_or_create_for(
39
+ @access_token = server_config.access_token_model.find_or_create_for(
38
40
  client,
39
41
  resource_owner_id,
40
42
  scopes,
41
43
  Authorization::Token.access_token_expires_in(server, context),
42
- Authorization::Token.refresh_token_enabled?(server, context)
44
+ Authorization::Token.refresh_token_enabled?(server, context),
43
45
  )
44
46
  end
45
47
 
46
48
  def before_successful_response
47
- Doorkeeper.configuration.before_successful_strategy_response.call(self)
49
+ server_config.before_successful_strategy_response.call(self)
48
50
  end
49
51
 
50
52
  def after_successful_response
51
- Doorkeeper.configuration.after_successful_strategy_response.call(self, @response)
53
+ server_config.after_successful_strategy_response.call(self, @response)
54
+ end
55
+
56
+ def server_config
57
+ Doorkeeper.config
52
58
  end
53
59
 
54
60
  private
@@ -11,18 +11,17 @@ module Doorkeeper
11
11
  @application = application
12
12
  end
13
13
 
14
- def self.find(uid, method = Application.method(:by_uid))
15
- if (application = method.call(uid))
16
- new(application)
17
- end
14
+ def self.find(uid, method = Doorkeeper.config.application_model.method(:by_uid))
15
+ return unless (application = method.call(uid))
16
+
17
+ new(application)
18
18
  end
19
19
 
20
- def self.authenticate(credentials, method = Application.method(:by_uid_and_secret))
20
+ def self.authenticate(credentials, method = Doorkeeper.config.application_model.method(:by_uid_and_secret))
21
21
  return if credentials.blank?
22
+ return unless (application = method.call(credentials.uid, credentials.secret))
22
23
 
23
- if (application = method.call(credentials.uid, credentials.secret))
24
- new(application)
25
- end
24
+ new(application)
26
25
  end
27
26
  end
28
27
  end
@@ -5,11 +5,32 @@ module Doorkeeper
5
5
  class ClientCredentialsRequest < BaseRequest
6
6
  class Creator
7
7
  def call(client, scopes, attributes = {})
8
- AccessToken.find_or_create_for(
8
+ if lookup_existing_token?
9
+ existing_token = find_existing_token_for(client, scopes)
10
+ return existing_token if server_config.reuse_access_token && existing_token&.reusable?
11
+
12
+ existing_token&.revoke if server_config.revoke_previous_client_credentials_token
13
+ end
14
+
15
+ server_config.access_token_model.find_or_create_for(
9
16
  client, nil, scopes, attributes[:expires_in],
10
- attributes[:use_refresh_token]
17
+ attributes[:use_refresh_token],
11
18
  )
12
19
  end
20
+
21
+ private
22
+
23
+ def lookup_existing_token?
24
+ server_config.reuse_access_token || server_config.revoke_previous_client_credentials_token
25
+ end
26
+
27
+ def find_existing_token_for(client, scopes)
28
+ server_config.access_token_model.matching_token_for(client, nil, scopes)
29
+ end
30
+
31
+ def server_config
32
+ Doorkeeper.config
33
+ end
13
34
  end
14
35
  end
15
36
  end
@@ -4,20 +4,20 @@ module Doorkeeper
4
4
  module OAuth
5
5
  class ClientCredentialsRequest < BaseRequest
6
6
  class Issuer
7
- attr_accessor :token, :validation, :error
7
+ attr_accessor :token, :validator, :error
8
8
 
9
- def initialize(server, validation)
9
+ def initialize(server, validator)
10
10
  @server = server
11
- @validation = validation
11
+ @validator = validator
12
12
  end
13
13
 
14
14
  def create(client, scopes, creator = Creator.new)
15
- if validation.valid?
15
+ if validator.valid?
16
16
  @token = create_token(client, scopes, creator)
17
17
  @error = :server_error unless @token
18
18
  else
19
19
  @token = false
20
- @error = validation.error
20
+ @error = validator.error
21
21
  end
22
22
  @token
23
23
  end
@@ -28,7 +28,7 @@ module Doorkeeper
28
28
  context = Authorization::Token.build_context(
29
29
  client,
30
30
  Doorkeeper::OAuth::CLIENT_CREDENTIALS,
31
- scopes
31
+ scopes,
32
32
  )
33
33
  ttl = Authorization::Token.access_token_expires_in(@server, context)
34
34
 
@@ -36,7 +36,7 @@ module Doorkeeper
36
36
  client,
37
37
  scopes,
38
38
  use_refresh_token: false,
39
- expires_in: ttl
39
+ expires_in: ttl,
40
40
  )
41
41
  end
42
42
  end
@@ -3,11 +3,12 @@
3
3
  module Doorkeeper
4
4
  module OAuth
5
5
  class ClientCredentialsRequest < BaseRequest
6
- class Validation
6
+ class Validator
7
7
  include Validations
8
8
  include OAuth::Helpers
9
9
 
10
10
  validate :client, error: :invalid_client
11
+ validate :client_supports_grant_flow, error: :unauthorized_client
11
12
  validate :scopes, error: :invalid_scope
12
13
 
13
14
  def initialize(server, request)
@@ -24,6 +25,13 @@ module Doorkeeper
24
25
  @client.present?
25
26
  end
26
27
 
28
+ def validate_client_supports_grant_flow
29
+ Doorkeeper.config.allow_grant_flow_for_client?(
30
+ Doorkeeper::OAuth::CLIENT_CREDENTIALS,
31
+ @client,
32
+ )
33
+ end
34
+
27
35
  def validate_scopes
28
36
  return true if @request.scopes.blank?
29
37
 
@@ -37,7 +45,7 @@ module Doorkeeper
37
45
  scope_str: @request.scopes.to_s,
38
46
  server_scopes: @server.scopes,
39
47
  app_scopes: application_scopes,
40
- grant_type: Doorkeeper::OAuth::CLIENT_CREDENTIALS
48
+ grant_type: Doorkeeper::OAuth::CLIENT_CREDENTIALS,
41
49
  )
42
50
  end
43
51
  end