doorkeeper 5.0.3 → 5.1.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 (215) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +2 -1
  3. data/.rubocop.yml +37 -4
  4. data/.travis.yml +11 -21
  5. data/Appraisals +29 -7
  6. data/Dangerfile +5 -2
  7. data/Gemfile +19 -5
  8. data/NEWS.md +81 -21
  9. data/README.md +75 -485
  10. data/Rakefile +10 -8
  11. data/app/controllers/doorkeeper/application_controller.rb +1 -2
  12. data/app/controllers/doorkeeper/application_metal_controller.rb +2 -13
  13. data/app/controllers/doorkeeper/applications_controller.rb +20 -8
  14. data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
  15. data/app/controllers/doorkeeper/token_info_controller.rb +2 -2
  16. data/app/controllers/doorkeeper/tokens_controller.rb +11 -11
  17. data/app/helpers/doorkeeper/dashboard_helper.rb +1 -1
  18. data/app/validators/redirect_uri_validator.rb +5 -2
  19. data/app/views/doorkeeper/applications/_form.html.erb +6 -0
  20. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  21. data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
  22. data/bin/console +16 -0
  23. data/config/locales/en.yml +1 -0
  24. data/doorkeeper.gemspec +24 -21
  25. data/gemfiles/rails_5_0.gemfile +9 -4
  26. data/gemfiles/rails_5_1.gemfile +9 -4
  27. data/gemfiles/rails_5_2.gemfile +10 -5
  28. data/gemfiles/rails_6_0.gemfile +17 -0
  29. data/gemfiles/rails_master.gemfile +9 -9
  30. data/lib/doorkeeper/config/option.rb +64 -0
  31. data/lib/doorkeeper/config.rb +191 -75
  32. data/lib/doorkeeper/engine.rb +1 -1
  33. data/lib/doorkeeper/grape/authorization_decorator.rb +4 -4
  34. data/lib/doorkeeper/grape/helpers.rb +3 -3
  35. data/lib/doorkeeper/helpers/controller.rb +3 -2
  36. data/lib/doorkeeper/models/access_grant_mixin.rb +20 -3
  37. data/lib/doorkeeper/models/access_token_mixin.rb +63 -19
  38. data/lib/doorkeeper/models/application_mixin.rb +43 -1
  39. data/lib/doorkeeper/models/concerns/expirable.rb +4 -2
  40. data/lib/doorkeeper/models/concerns/ownership.rb +1 -6
  41. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  42. data/lib/doorkeeper/models/concerns/revocable.rb +2 -1
  43. data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
  44. data/lib/doorkeeper/models/concerns/secret_storable.rb +108 -0
  45. data/lib/doorkeeper/oauth/authorization/code.rb +2 -2
  46. data/lib/doorkeeper/oauth/authorization/token.rb +12 -7
  47. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +1 -1
  48. data/lib/doorkeeper/oauth/authorization_code_request.rb +6 -4
  49. data/lib/doorkeeper/oauth/client.rb +1 -1
  50. data/lib/doorkeeper/oauth/client_credentials/validation.rb +5 -4
  51. data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
  52. data/lib/doorkeeper/oauth/code_response.rb +2 -2
  53. data/lib/doorkeeper/oauth/error_response.rb +10 -6
  54. data/lib/doorkeeper/oauth/forbidden_token_response.rb +1 -1
  55. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -8
  56. data/lib/doorkeeper/oauth/helpers/unique_token.rb +13 -1
  57. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +37 -1
  58. data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -1
  59. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
  60. data/lib/doorkeeper/oauth/pre_authorization.rb +11 -5
  61. data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -2
  62. data/lib/doorkeeper/oauth/scopes.rb +5 -3
  63. data/lib/doorkeeper/oauth/token.rb +2 -2
  64. data/lib/doorkeeper/oauth/token_introspection.rb +74 -8
  65. data/lib/doorkeeper/oauth/token_response.rb +9 -9
  66. data/lib/doorkeeper/oauth.rb +5 -5
  67. data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -13
  68. data/lib/doorkeeper/orm/active_record/access_token.rb +6 -13
  69. data/lib/doorkeeper/orm/active_record/application.rb +21 -57
  70. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
  71. data/lib/doorkeeper/orm/active_record.rb +6 -6
  72. data/lib/doorkeeper/rails/helpers.rb +1 -1
  73. data/lib/doorkeeper/rails/routes/mapping.rb +7 -7
  74. data/lib/doorkeeper/rails/routes.rb +11 -11
  75. data/lib/doorkeeper/rake/db.rake +13 -13
  76. data/lib/doorkeeper/rake.rb +1 -1
  77. data/lib/doorkeeper/request.rb +1 -1
  78. data/lib/doorkeeper/secret_storing/base.rb +64 -0
  79. data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  80. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  81. data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  82. data/lib/doorkeeper/stale_records_cleaner.rb +1 -1
  83. data/lib/doorkeeper/version.rb +3 -3
  84. data/lib/doorkeeper.rb +69 -60
  85. data/lib/generators/doorkeeper/application_owner_generator.rb +10 -9
  86. data/lib/generators/doorkeeper/confidential_applications_generator.rb +10 -9
  87. data/lib/generators/doorkeeper/install_generator.rb +11 -9
  88. data/lib/generators/doorkeeper/migration_generator.rb +9 -9
  89. data/lib/generators/doorkeeper/pkce_generator.rb +10 -9
  90. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +10 -9
  91. data/lib/generators/doorkeeper/templates/initializer.rb +120 -13
  92. data/lib/generators/doorkeeper/templates/migration.rb.erb +15 -7
  93. data/lib/generators/doorkeeper/views_generator.rb +6 -4
  94. data/spec/controllers/application_metal_controller_spec.rb +24 -10
  95. data/spec/controllers/applications_controller_spec.rb +54 -52
  96. data/spec/controllers/authorizations_controller_spec.rb +139 -145
  97. data/spec/controllers/protected_resources_controller_spec.rb +78 -76
  98. data/spec/controllers/token_info_controller_spec.rb +14 -12
  99. data/spec/controllers/tokens_controller_spec.rb +167 -108
  100. data/spec/dummy/Rakefile +3 -1
  101. data/spec/dummy/app/controllers/application_controller.rb +3 -1
  102. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +2 -0
  103. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -2
  104. data/spec/dummy/app/controllers/home_controller.rb +5 -3
  105. data/spec/dummy/app/controllers/metal_controller.rb +2 -0
  106. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +4 -2
  107. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  108. data/spec/dummy/app/models/user.rb +2 -0
  109. data/spec/dummy/config/application.rb +25 -1
  110. data/spec/dummy/config/environments/development.rb +2 -0
  111. data/spec/dummy/config/environments/production.rb +2 -0
  112. data/spec/dummy/config/environments/test.rb +3 -1
  113. data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
  114. data/spec/dummy/config/initializers/doorkeeper.rb +5 -2
  115. data/spec/dummy/config/initializers/secret_token.rb +3 -1
  116. data/spec/dummy/config/initializers/session_store.rb +3 -1
  117. data/spec/dummy/config/initializers/wrap_parameters.rb +2 -0
  118. data/spec/dummy/config.ru +3 -1
  119. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +17 -10
  120. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +2 -0
  121. data/spec/dummy/db/schema.rb +1 -1
  122. data/spec/dummy/script/rails +5 -3
  123. data/spec/factories.rb +8 -6
  124. data/spec/generators/application_owner_generator_spec.rb +13 -26
  125. data/spec/generators/confidential_applications_generator_spec.rb +12 -28
  126. data/spec/generators/install_generator_spec.rb +17 -15
  127. data/spec/generators/migration_generator_spec.rb +13 -26
  128. data/spec/generators/pkce_generator_spec.rb +11 -26
  129. data/spec/generators/previous_refresh_token_generator_spec.rb +16 -29
  130. data/spec/generators/templates/routes.rb +2 -0
  131. data/spec/generators/views_generator_spec.rb +14 -12
  132. data/spec/grape/grape_integration_spec.rb +34 -32
  133. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +9 -7
  134. data/spec/lib/config_spec.rb +268 -99
  135. data/spec/lib/doorkeeper_spec.rb +3 -1
  136. data/spec/lib/models/expirable_spec.rb +22 -8
  137. data/spec/lib/models/reusable_spec.rb +40 -0
  138. data/spec/lib/models/revocable_spec.rb +8 -6
  139. data/spec/lib/models/scopes_spec.rb +27 -13
  140. data/spec/lib/models/secret_storable_spec.rb +135 -0
  141. data/spec/lib/oauth/authorization/uri_builder_spec.rb +17 -15
  142. data/spec/lib/oauth/authorization_code_request_spec.rb +34 -11
  143. data/spec/lib/oauth/base_request_spec.rb +20 -8
  144. data/spec/lib/oauth/base_response_spec.rb +3 -1
  145. data/spec/lib/oauth/client/credentials_spec.rb +24 -22
  146. data/spec/lib/oauth/client_credentials/creator_spec.rb +56 -10
  147. data/spec/lib/oauth/client_credentials/issuer_spec.rb +27 -18
  148. data/spec/lib/oauth/client_credentials/validation_spec.rb +20 -15
  149. data/spec/lib/oauth/client_credentials_integration_spec.rb +7 -5
  150. data/spec/lib/oauth/client_credentials_request_spec.rb +27 -21
  151. data/spec/lib/oauth/client_spec.rb +15 -13
  152. data/spec/lib/oauth/code_request_spec.rb +8 -6
  153. data/spec/lib/oauth/code_response_spec.rb +9 -7
  154. data/spec/lib/oauth/error_response_spec.rb +19 -11
  155. data/spec/lib/oauth/error_spec.rb +4 -2
  156. data/spec/lib/oauth/forbidden_token_response_spec.rb +7 -5
  157. data/spec/lib/oauth/helpers/scope_checker_spec.rb +68 -31
  158. data/spec/lib/oauth/helpers/unique_token_spec.rb +8 -6
  159. data/spec/lib/oauth/helpers/uri_checker_spec.rb +115 -95
  160. data/spec/lib/oauth/invalid_token_response_spec.rb +3 -1
  161. data/spec/lib/oauth/password_access_token_request_spec.rb +79 -30
  162. data/spec/lib/oauth/pre_authorization_spec.rb +80 -54
  163. data/spec/lib/oauth/refresh_token_request_spec.rb +36 -33
  164. data/spec/lib/oauth/scopes_spec.rb +63 -61
  165. data/spec/lib/oauth/token_request_spec.rb +79 -24
  166. data/spec/lib/oauth/token_response_spec.rb +40 -38
  167. data/spec/lib/oauth/token_spec.rb +60 -44
  168. data/spec/lib/request/strategy_spec.rb +3 -1
  169. data/spec/lib/secret_storing/base_spec.rb +60 -0
  170. data/spec/lib/secret_storing/bcrypt_spec.rb +49 -0
  171. data/spec/lib/secret_storing/plain_spec.rb +44 -0
  172. data/spec/lib/secret_storing/sha256_hash_spec.rb +48 -0
  173. data/spec/lib/server_spec.rb +16 -14
  174. data/spec/lib/stale_records_cleaner_spec.rb +17 -17
  175. data/spec/models/doorkeeper/access_grant_spec.rb +80 -15
  176. data/spec/models/doorkeeper/access_token_spec.rb +198 -73
  177. data/spec/models/doorkeeper/application_spec.rb +280 -288
  178. data/spec/requests/applications/applications_request_spec.rb +98 -66
  179. data/spec/requests/applications/authorized_applications_spec.rb +20 -18
  180. data/spec/requests/endpoints/authorization_spec.rb +25 -23
  181. data/spec/requests/endpoints/token_spec.rb +38 -36
  182. data/spec/requests/flows/authorization_code_errors_spec.rb +26 -24
  183. data/spec/requests/flows/authorization_code_spec.rb +193 -151
  184. data/spec/requests/flows/client_credentials_spec.rb +54 -52
  185. data/spec/requests/flows/implicit_grant_errors_spec.rb +10 -8
  186. data/spec/requests/flows/implicit_grant_spec.rb +27 -25
  187. data/spec/requests/flows/password_spec.rb +59 -55
  188. data/spec/requests/flows/refresh_token_spec.rb +45 -43
  189. data/spec/requests/flows/revoke_token_spec.rb +40 -54
  190. data/spec/requests/flows/skip_authorization_spec.rb +23 -21
  191. data/spec/requests/protected_resources/metal_spec.rb +7 -5
  192. data/spec/requests/protected_resources/private_api_spec.rb +35 -33
  193. data/spec/routing/custom_controller_routes_spec.rb +71 -65
  194. data/spec/routing/default_routes_spec.rb +22 -20
  195. data/spec/routing/scoped_routes_spec.rb +20 -18
  196. data/spec/spec_helper.rb +15 -13
  197. data/spec/spec_helper_integration.rb +3 -1
  198. data/spec/support/dependencies/factory_bot.rb +3 -1
  199. data/spec/support/doorkeeper_rspec.rb +3 -1
  200. data/spec/support/helpers/access_token_request_helper.rb +3 -1
  201. data/spec/support/helpers/authorization_request_helper.rb +4 -2
  202. data/spec/support/helpers/config_helper.rb +2 -0
  203. data/spec/support/helpers/model_helper.rb +3 -1
  204. data/spec/support/helpers/request_spec_helper.rb +5 -3
  205. data/spec/support/helpers/url_helper.rb +9 -7
  206. data/spec/support/http_method_shim.rb +4 -9
  207. data/spec/support/orm/active_record.rb +3 -1
  208. data/spec/support/shared/controllers_shared_context.rb +18 -16
  209. data/spec/support/shared/hashing_shared_context.rb +36 -0
  210. data/spec/support/shared/models_shared_examples.rb +12 -10
  211. data/spec/validators/redirect_uri_validator_spec.rb +74 -45
  212. data/spec/version/version_spec.rb +7 -5
  213. metadata +60 -26
  214. data/gemfiles/rails_4_2.gemfile +0 -13
  215. data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -8
@@ -6,23 +6,25 @@ module Doorkeeper
6
6
 
7
7
  include OAuth::Helpers
8
8
  include Models::Expirable
9
+ include Models::Reusable
9
10
  include Models::Revocable
10
11
  include Models::Accessible
11
12
  include Models::Orderable
13
+ include Models::SecretStorable
12
14
  include Models::Scopes
13
15
 
14
16
  module ClassMethods
15
17
  # Returns an instance of the Doorkeeper::AccessToken with
16
- # specific token value.
18
+ # specific plain text token value.
17
19
  #
18
20
  # @param token [#to_s]
19
- # token value (any object that responds to `#to_s`)
21
+ # Plain text token value (any object that responds to `#to_s`)
20
22
  #
21
23
  # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
22
24
  # if there is no record with such token
23
25
  #
24
26
  def by_token(token)
25
- find_by(token: token.to_s)
27
+ find_by_plaintext_token(:token, token)
26
28
  end
27
29
 
28
30
  # Returns an instance of the Doorkeeper::AccessToken
@@ -35,7 +37,7 @@ module Doorkeeper
35
37
  # if there is no record with such refresh token
36
38
  #
37
39
  def by_refresh_token(refresh_token)
38
- find_by(refresh_token: refresh_token.to_s)
40
+ find_by_plaintext_token(:refresh_token, refresh_token)
39
41
  end
40
42
 
41
43
  # Revokes AccessToken records that have not been revoked and associated
@@ -98,9 +100,9 @@ module Doorkeeper
98
100
 
99
101
  (token_scopes.sort == param_scopes.sort) &&
100
102
  Doorkeeper::OAuth::Helpers::ScopeChecker.valid?(
101
- param_scopes.to_s,
102
- Doorkeeper.configuration.scopes,
103
- app_scopes
103
+ scope_str: param_scopes.to_s,
104
+ server_scopes: Doorkeeper.configuration.scopes,
105
+ app_scopes: app_scopes
104
106
  )
105
107
  end
106
108
 
@@ -125,14 +127,14 @@ module Doorkeeper
125
127
  if Doorkeeper.configuration.reuse_access_token
126
128
  access_token = matching_token_for(application, resource_owner_id, scopes)
127
129
 
128
- return access_token if access_token && !access_token.expired?
130
+ return access_token if access_token&.reusable?
129
131
  end
130
132
 
131
133
  create!(
132
- application_id: application.try(:id),
134
+ application_id: application.try(:id),
133
135
  resource_owner_id: resource_owner_id,
134
- scopes: scopes.to_s,
135
- expires_in: expires_in,
136
+ scopes: scopes.to_s,
137
+ expires_in: expires_in,
136
138
  use_refresh_token: use_refresh_token
137
139
  )
138
140
  end
@@ -168,6 +170,20 @@ module Doorkeeper
168
170
  def last_authorized_token_for(application_id, resource_owner_id)
169
171
  authorized_tokens_for(application_id, resource_owner_id).first
170
172
  end
173
+
174
+ ##
175
+ # Determines the secret storing transformer
176
+ # Unless configured otherwise, uses the plain secret strategy
177
+ def secret_strategy
178
+ ::Doorkeeper.configuration.token_secret_strategy
179
+ end
180
+
181
+ ##
182
+ # Determine the fallback storing strategy
183
+ # Unless configured, there will be no fallback
184
+ def fallback_secret_strategy
185
+ ::Doorkeeper.configuration.token_secret_fallback_strategy
186
+ end
171
187
  end
172
188
 
173
189
  # Access Token type: Bearer.
@@ -175,7 +191,7 @@ module Doorkeeper
175
191
  # The OAuth 2.0 Authorization Framework: Bearer Token Usage
176
192
  #
177
193
  def token_type
178
- 'Bearer'
194
+ "Bearer"
179
195
  end
180
196
 
181
197
  def use_refresh_token?
@@ -188,11 +204,11 @@ module Doorkeeper
188
204
  # @return [Hash] hash with token data
189
205
  def as_json(_options = {})
190
206
  {
191
- resource_owner_id: resource_owner_id,
192
- scope: scopes,
193
- expires_in: expires_in_seconds,
194
- application: { uid: application.try(:uid) },
195
- created_at: created_at.to_i
207
+ resource_owner_id: resource_owner_id,
208
+ scope: scopes,
209
+ expires_in: expires_in_seconds,
210
+ application: { uid: application.try(:uid) },
211
+ created_at: created_at.to_i,
196
212
  }
197
213
  end
198
214
 
@@ -219,6 +235,30 @@ module Doorkeeper
219
235
  accessible? && includes_scope?(*scopes)
220
236
  end
221
237
 
238
+ # We keep a volatile copy of the raw refresh token for initial communication
239
+ # The stored refresh_token may be mapped and not available in cleartext.
240
+ def plaintext_refresh_token
241
+ if secret_strategy.allows_restoring_secrets?
242
+ secret_strategy.restore_secret(self, :refresh_token)
243
+ else
244
+ @raw_refresh_token
245
+ end
246
+ end
247
+
248
+ # We keep a volatile copy of the raw token for initial communication
249
+ # The stored refresh_token may be mapped and not available in cleartext.
250
+ #
251
+ # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
252
+ # while hashing strategies do not, so you cannot rely on this value
253
+ # returning a present value for persisted tokens.
254
+ def plaintext_token
255
+ if secret_strategy.allows_restoring_secrets?
256
+ secret_strategy.restore_secret(self, :token)
257
+ else
258
+ @raw_token
259
+ end
260
+ end
261
+
222
262
  private
223
263
 
224
264
  # Generates refresh token with UniqueToken generator.
@@ -226,7 +266,8 @@ module Doorkeeper
226
266
  # @return [String] refresh token value
227
267
  #
228
268
  def generate_refresh_token
229
- self.refresh_token = UniqueToken.generate
269
+ @raw_refresh_token = UniqueToken.generate
270
+ secret_strategy.store_secret(self, :refresh_token, @raw_refresh_token)
230
271
  end
231
272
 
232
273
  # Generates and sets the token value with the
@@ -242,13 +283,16 @@ module Doorkeeper
242
283
  def generate_token
243
284
  self.created_at ||= Time.now.utc
244
285
 
245
- self.token = token_generator.generate(
286
+ @raw_token = token_generator.generate(
246
287
  resource_owner_id: resource_owner_id,
247
288
  scopes: scopes,
248
289
  application: application,
249
290
  expires_in: expires_in,
250
291
  created_at: created_at
251
292
  )
293
+
294
+ secret_strategy.store_secret(self, :token, @raw_token)
295
+ @raw_token
252
296
  end
253
297
 
254
298
  def token_generator
@@ -6,8 +6,10 @@ module Doorkeeper
6
6
 
7
7
  include OAuth::Helpers
8
8
  include Models::Orderable
9
+ include Models::SecretStorable
9
10
  include Models::Scopes
10
11
 
12
+ # :nodoc
11
13
  module ClassMethods
12
14
  # Returns an instance of the Doorkeeper::Application with
13
15
  # specific UID and secret.
@@ -25,7 +27,8 @@ module Doorkeeper
25
27
  app = by_uid(uid)
26
28
  return unless app
27
29
  return app if secret.blank? && !app.confidential?
28
- return unless app.secret == secret
30
+ return unless app.secret_matches?(secret)
31
+
29
32
  app
30
33
  end
31
34
 
@@ -39,6 +42,20 @@ module Doorkeeper
39
42
  def by_uid(uid)
40
43
  find_by(uid: uid.to_s)
41
44
  end
45
+
46
+ ##
47
+ # Determines the secret storing transformer
48
+ # Unless configured otherwise, uses the plain secret strategy
49
+ def secret_strategy
50
+ ::Doorkeeper.configuration.application_secret_strategy
51
+ end
52
+
53
+ ##
54
+ # Determine the fallback storing strategy
55
+ # Unless configured, there will be no fallback
56
+ def fallback_secret_strategy
57
+ ::Doorkeeper.configuration.application_secret_fallback_strategy
58
+ end
42
59
  end
43
60
 
44
61
  # Set an application's valid redirect URIs.
@@ -49,5 +66,30 @@ module Doorkeeper
49
66
  def redirect_uri=(uris)
50
67
  super(uris.is_a?(Array) ? uris.join("\n") : uris)
51
68
  end
69
+
70
+ # Check whether the given plain text secret matches our stored secret
71
+ #
72
+ # @param input [#to_s] Plain secret provided by user
73
+ # (any object that responds to `#to_s`)
74
+ #
75
+ # @return [true] Whether the given secret matches the stored secret
76
+ # of this application.
77
+ #
78
+ def secret_matches?(input)
79
+ # return false if either is nil, since secure_compare depends on strings
80
+ # but Application secrets MAY be nil depending on confidentiality.
81
+ return false if input.nil? || secret.nil?
82
+
83
+ # When matching the secret by comparer function, all is well.
84
+ return true if secret_strategy.secret_matches?(input, secret)
85
+
86
+ # When fallback lookup is enabled, ensure applications
87
+ # with plain secrets can still be found
88
+ if fallback_secret_strategy
89
+ fallback_secret_strategy.secret_matches?(input, secret)
90
+ else
91
+ false
92
+ end
93
+ end
52
94
  end
53
95
  end
@@ -17,6 +17,7 @@ module Doorkeeper
17
17
  # or nil if object never expires.
18
18
  def expires_in_seconds
19
19
  return nil if expires_in.nil?
20
+
20
21
  expires = expires_at - Time.now.utc
21
22
  expires_sec = expires.seconds.round(0)
22
23
  expires_sec > 0 ? expires_sec : 0
@@ -24,10 +25,11 @@ module Doorkeeper
24
25
 
25
26
  # Expiration time (date time of creation + TTL).
26
27
  #
27
- # @return [Time] expiration time in UTC
28
+ # @return [Time, nil] expiration time in UTC
29
+ # or nil if the object never expires.
28
30
  #
29
31
  def expires_at
30
- created_at + expires_in.seconds
32
+ expires_in && created_at + expires_in.seconds
31
33
  end
32
34
  end
33
35
  end
@@ -6,12 +6,7 @@ module Doorkeeper
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- belongs_to_options = { polymorphic: true }
10
- if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
11
- belongs_to_options[:optional] = true
12
- end
13
-
14
- belongs_to :owner, belongs_to_options
9
+ belongs_to :owner, polymorphic: true, optional: true
15
10
  validates :owner, presence: true, if: :validate_owner?
16
11
  end
17
12
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Models
5
+ module Reusable
6
+ # Indicates whether the object is reusable (i.e. It is not expired and
7
+ # has not crossed reuse_limit).
8
+ #
9
+ # @return [Boolean] true if can be reused and false in other case
10
+ def reusable?
11
+ return false if expired?
12
+ return true unless expires_in
13
+
14
+ threshold_limit = 100 - Doorkeeper.configuration.token_reuse_limit
15
+ expires_in_seconds >= threshold_limit * expires_in / 100
16
+ end
17
+ end
18
+ end
19
+ end
@@ -25,7 +25,8 @@ module Doorkeeper
25
25
  #
26
26
  def revoke_previous_refresh_token!
27
27
  return unless refresh_token_revoked_on_use?
28
- old_refresh_token.revoke if old_refresh_token
28
+
29
+ old_refresh_token&.revoke
29
30
  update_attribute :previous_refresh_token, ""
30
31
  end
31
32
 
@@ -4,7 +4,11 @@ module Doorkeeper
4
4
  module Models
5
5
  module Scopes
6
6
  def scopes
7
- OAuth::Scopes.from_string(self[:scopes])
7
+ OAuth::Scopes.from_string(scopes_string)
8
+ end
9
+
10
+ def scopes=(value)
11
+ super Array(value).join(" ")
8
12
  end
9
13
 
10
14
  def scopes_string
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Models
5
+ ##
6
+ # Storable finder to provide lookups for input plaintext values which are
7
+ # mapped to their stored versions (e.g., hashing, encryption) before lookup.
8
+ module SecretStorable
9
+ extend ActiveSupport::Concern
10
+
11
+ delegate :secret_strategy,
12
+ :fallback_secret_strategy,
13
+ to: :class
14
+
15
+ # :nodoc
16
+ module ClassMethods
17
+ # Compare the given plaintext with the secret
18
+ #
19
+ # @param input [String]
20
+ # The plain input to compare.
21
+ #
22
+ # @param secret [String]
23
+ # The secret value to compare with.
24
+ #
25
+ # @return [Boolean]
26
+ # Whether input matches secret as per the secret strategy
27
+ #
28
+ def secret_matches?(input, secret)
29
+ secret_strategy.secret_matches?(input, secret)
30
+ end
31
+
32
+ # Returns an instance of the Doorkeeper::AccessToken with
33
+ # specific token value.
34
+ #
35
+ # @param attr [Symbol]
36
+ # The token attribute we're looking with.
37
+ #
38
+ # @param token [#to_s]
39
+ # token value (any object that responds to `#to_s`)
40
+ #
41
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
42
+ # if there is no record with such token
43
+ #
44
+ def find_by_plaintext_token(attr, token)
45
+ token = token.to_s
46
+
47
+ find_by(attr => secret_strategy.transform_secret(token)) ||
48
+ find_by_fallback_token(attr, token)
49
+ end
50
+
51
+ # Allow looking up previously plain tokens as a fallback
52
+ # IFF a fallback strategy has been defined
53
+ #
54
+ # @param attr [Symbol]
55
+ # The token attribute we're looking with.
56
+ #
57
+ # @param plain_secret [#to_s]
58
+ # plain secret value (any object that responds to `#to_s`)
59
+ #
60
+ # @return [Doorkeeper::AccessToken, nil] AccessToken object or nil
61
+ # if there is no record with such token
62
+ #
63
+ def find_by_fallback_token(attr, plain_secret)
64
+ return nil unless fallback_secret_strategy
65
+
66
+ # Use the previous strategy to look up
67
+ stored_token = fallback_secret_strategy.transform_secret(plain_secret)
68
+ find_by(attr => stored_token).tap do |resource|
69
+ return nil unless resource
70
+
71
+ upgrade_fallback_value resource, attr, plain_secret
72
+ end
73
+ end
74
+
75
+ # Allow implementations in ORMs to replace a plain
76
+ # value falling back to to avoid it remaining as plain text.
77
+ #
78
+ # @param instance
79
+ # An instance of this model with a plain value token.
80
+ #
81
+ # @param attr
82
+ # The secret attribute name to upgrade.
83
+ #
84
+ # @param plain_secret
85
+ # The plain secret to upgrade.
86
+ #
87
+ def upgrade_fallback_value(instance, attr, plain_secret)
88
+ upgraded = secret_strategy.store_secret(instance, attr, plain_secret)
89
+ instance.update(attr => upgraded)
90
+ end
91
+
92
+ ##
93
+ # Determines the secret storing transformer
94
+ # Unless configured otherwise, uses the plain secret strategy
95
+ def secret_strategy
96
+ ::Doorkeeper::SecretStoring::Plain
97
+ end
98
+
99
+ ##
100
+ # Determine the fallback storing strategy
101
+ # Unless configured, there will be no fallback
102
+ def fallback_secret_strategy
103
+ nil
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -16,7 +16,7 @@ module Doorkeeper
16
16
  end
17
17
 
18
18
  def native_redirect
19
- { action: :show, code: token.token }
19
+ { action: :show, code: token.plaintext_token }
20
20
  end
21
21
 
22
22
  def configuration
@@ -42,7 +42,7 @@ module Doorkeeper
42
42
 
43
43
  {
44
44
  code_challenge: pre_auth.code_challenge,
45
- code_challenge_method: pre_auth.code_challenge_method
45
+ code_challenge_method: pre_auth.code_challenge_method,
46
46
  }
47
47
  end
48
48
 
@@ -8,7 +8,9 @@ module Doorkeeper
8
8
 
9
9
  class << self
10
10
  def build_context(pre_auth_or_oauth_client, grant_type, scopes)
11
- oauth_client = if pre_auth_or_oauth_client.respond_to?(:client)
11
+ oauth_client = if pre_auth_or_oauth_client.respond_to?(:application)
12
+ pre_auth_or_oauth_client.application
13
+ elsif pre_auth_or_oauth_client.respond_to?(:client)
12
14
  pre_auth_or_oauth_client.client
13
15
  else
14
16
  pre_auth_or_oauth_client
@@ -21,11 +23,14 @@ module Doorkeeper
21
23
  )
22
24
  end
23
25
 
24
- def access_token_expires_in(server, context)
25
- if (expiration = server.custom_access_token_expires_in.call(context))
26
- expiration
26
+ def access_token_expires_in(configuration, context)
27
+ if configuration.option_defined?(:custom_access_token_expires_in)
28
+ expiration = configuration.custom_access_token_expires_in.call(context)
29
+ return nil if expiration == Float::INFINITY
30
+
31
+ expiration || configuration.access_token_expires_in
27
32
  else
28
- server.access_token_expires_in
33
+ configuration.access_token_expires_in
29
34
  end
30
35
  end
31
36
 
@@ -62,7 +67,7 @@ module Doorkeeper
62
67
  {
63
68
  controller: controller,
64
69
  action: :show,
65
- access_token: token.token
70
+ access_token: token.plaintext_token,
66
71
  }
67
72
  end
68
73
 
@@ -75,7 +80,7 @@ module Doorkeeper
75
80
  def controller
76
81
  @controller ||= begin
77
82
  mapping = Doorkeeper::Rails::Routes.mapping[:token_info] || {}
78
- mapping[:controllers] || 'doorkeeper/token_info'
83
+ mapping[:controllers] || "doorkeeper/token_info"
79
84
  end
80
85
  end
81
86
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rack/utils'
3
+ require "rack/utils"
4
4
 
5
5
  module Doorkeeper
6
6
  module OAuth
@@ -43,17 +43,19 @@ module Doorkeeper
43
43
  end
44
44
 
45
45
  def validate_attributes
46
- return false if grant && grant.uses_pkce? && code_verifier.blank?
46
+ return false if grant&.uses_pkce? && code_verifier.blank?
47
47
  return false if grant && !grant.pkce_supported? && !code_verifier.blank?
48
+
48
49
  redirect_uri.present?
49
50
  end
50
51
 
51
52
  def validate_client
52
- !client.nil?
53
+ client.present?
53
54
  end
54
55
 
55
56
  def validate_grant
56
57
  return false unless grant && grant.application_id == client.id
58
+
57
59
  grant.accessible?
58
60
  end
59
61
 
@@ -70,9 +72,9 @@ module Doorkeeper
70
72
  return true unless grant.uses_pkce? || code_verifier
71
73
  return false unless grant.pkce_supported?
72
74
 
73
- if grant.code_challenge_method == 'S256'
75
+ if grant.code_challenge_method == "S256"
74
76
  grant.code_challenge == AccessGrant.generate_code_challenge(code_verifier)
75
- elsif grant.code_challenge_method == 'plain'
77
+ elsif grant.code_challenge_method == "plain"
76
78
  grant.code_challenge == code_verifier
77
79
  else
78
80
  false
@@ -18,7 +18,7 @@ module Doorkeeper
18
18
  end
19
19
 
20
20
  def self.authenticate(credentials, method = Application.method(:by_uid_and_secret))
21
- return false if credentials.blank?
21
+ return if credentials.blank?
22
22
 
23
23
  if (application = method.call(credentials.uid, credentials.secret))
24
24
  new(application)
@@ -30,13 +30,14 @@ module Doorkeeper
30
30
  application_scopes = if @client.present?
31
31
  @client.application.scopes
32
32
  else
33
- ''
33
+ ""
34
34
  end
35
35
 
36
36
  ScopeChecker.valid?(
37
- @request.scopes.to_s,
38
- @server.scopes,
39
- application_scopes
37
+ scope_str: @request.scopes.to_s,
38
+ server_scopes: @server.scopes,
39
+ app_scopes: application_scopes,
40
+ grant_type: Doorkeeper::OAuth::CLIENT_CREDENTIALS
40
41
  )
41
42
  end
42
43
  end
@@ -7,7 +7,7 @@ module Doorkeeper
7
7
  attr_reader :response
8
8
  attr_writer :issuer
9
9
 
10
- alias_method :error_response, :response
10
+ alias error_response response
11
11
 
12
12
  delegate :error, to: :issuer
13
13
 
@@ -23,7 +23,7 @@ module Doorkeeper
23
23
  elsif response_on_fragment
24
24
  Authorization::URIBuilder.uri_with_fragment(
25
25
  pre_auth.redirect_uri,
26
- access_token: auth.token.token,
26
+ access_token: auth.token.plaintext_token,
27
27
  token_type: auth.token.token_type,
28
28
  expires_in: auth.token.expires_in_seconds,
29
29
  state: pre_auth.state
@@ -31,7 +31,7 @@ module Doorkeeper
31
31
  else
32
32
  Authorization::URIBuilder.uri_with_query(
33
33
  pre_auth.redirect_uri,
34
- code: auth.token.token,
34
+ code: auth.token.plaintext_token,
35
35
  state: pre_auth.state
36
36
  )
37
37
  end
@@ -27,12 +27,16 @@ module Doorkeeper
27
27
  {
28
28
  error: name,
29
29
  error_description: description,
30
- state: state
30
+ state: state,
31
31
  }.reject { |_, v| v.blank? }
32
32
  end
33
33
 
34
34
  def status
35
- :unauthorized
35
+ if name == :invalid_client
36
+ :unauthorized
37
+ else
38
+ :bad_request
39
+ end
36
40
  end
37
41
 
38
42
  def redirectable?
@@ -50,10 +54,10 @@ module Doorkeeper
50
54
 
51
55
  def headers
52
56
  {
53
- 'Cache-Control' => 'no-store',
54
- 'Pragma' => 'no-cache',
55
- 'Content-Type' => 'application/json; charset=utf-8',
56
- 'WWW-Authenticate' => authenticate_info
57
+ "Cache-Control" => "no-store",
58
+ "Pragma" => "no-cache",
59
+ "Content-Type" => "application/json; charset=utf-8",
60
+ "WWW-Authenticate" => authenticate_info,
57
61
  }
58
62
  end
59
63
 
@@ -18,7 +18,7 @@ module Doorkeeper
18
18
 
19
19
  def headers
20
20
  headers = super
21
- headers.delete 'WWW-Authenticate'
21
+ headers.delete "WWW-Authenticate"
22
22
  headers
23
23
  end
24
24