doorkeeper 5.0.2 → 5.1.0.rc2

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 (94) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +17 -4
  3. data/Appraisals +29 -3
  4. data/Dangerfile +5 -2
  5. data/Gemfile +14 -4
  6. data/NEWS.md +64 -19
  7. data/README.md +68 -487
  8. data/app/controllers/doorkeeper/token_info_controller.rb +1 -1
  9. data/app/controllers/doorkeeper/tokens_controller.rb +7 -7
  10. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  11. data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
  12. data/bin/console +15 -0
  13. data/doorkeeper.gemspec +3 -2
  14. data/gemfiles/rails_4_2.gemfile +8 -4
  15. data/gemfiles/rails_5_0.gemfile +9 -5
  16. data/gemfiles/rails_5_1.gemfile +9 -5
  17. data/gemfiles/rails_5_2.gemfile +9 -5
  18. data/gemfiles/rails_6_0.gemfile +16 -0
  19. data/gemfiles/rails_master.gemfile +8 -9
  20. data/lib/doorkeeper/config.rb +164 -11
  21. data/lib/doorkeeper/helpers/controller.rb +3 -2
  22. data/lib/doorkeeper/models/access_grant_mixin.rb +16 -1
  23. data/lib/doorkeeper/models/access_token_mixin.rb +54 -10
  24. data/lib/doorkeeper/models/application_mixin.rb +42 -1
  25. data/lib/doorkeeper/models/concerns/expirable.rb +3 -2
  26. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  27. data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
  28. data/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
  29. data/lib/doorkeeper/oauth/authorization/code.rb +1 -1
  30. data/lib/doorkeeper/oauth/authorization/token.rb +4 -2
  31. data/lib/doorkeeper/oauth/authorization_code_request.rb +1 -1
  32. data/lib/doorkeeper/oauth/client.rb +1 -1
  33. data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -3
  34. data/lib/doorkeeper/oauth/code_response.rb +2 -2
  35. data/lib/doorkeeper/oauth/error_response.rb +5 -1
  36. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -8
  37. data/lib/doorkeeper/oauth/helpers/unique_token.rb +12 -1
  38. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +32 -0
  39. data/lib/doorkeeper/oauth/invalid_token_response.rb +4 -0
  40. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
  41. data/lib/doorkeeper/oauth/pre_authorization.rb +8 -3
  42. data/lib/doorkeeper/oauth/refresh_token_request.rb +4 -1
  43. data/lib/doorkeeper/oauth/token_introspection.rb +72 -6
  44. data/lib/doorkeeper/oauth/token_response.rb +2 -2
  45. data/lib/doorkeeper/orm/active_record/access_grant.rb +23 -2
  46. data/lib/doorkeeper/orm/active_record/application.rb +20 -2
  47. data/lib/doorkeeper/secret_storing/base.rb +63 -0
  48. data/lib/doorkeeper/secret_storing/bcrypt.rb +59 -0
  49. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  50. data/lib/doorkeeper/secret_storing/sha256_hash.rb +25 -0
  51. data/lib/doorkeeper/version.rb +3 -3
  52. data/lib/doorkeeper.rb +7 -0
  53. data/lib/generators/doorkeeper/templates/initializer.rb +90 -8
  54. data/spec/controllers/application_metal_controller_spec.rb +18 -4
  55. data/spec/controllers/authorizations_controller_spec.rb +3 -3
  56. data/spec/controllers/token_info_controller_spec.rb +1 -1
  57. data/spec/controllers/tokens_controller_spec.rb +85 -41
  58. data/spec/dummy/app/controllers/application_controller.rb +1 -1
  59. data/spec/dummy/config/application.rb +12 -1
  60. data/spec/factories.rb +3 -3
  61. data/spec/lib/config_spec.rb +168 -0
  62. data/spec/lib/models/expirable_spec.rb +12 -0
  63. data/spec/lib/models/reusable_spec.rb +40 -0
  64. data/spec/lib/models/scopes_spec.rb +13 -1
  65. data/spec/lib/models/secret_storable_spec.rb +113 -0
  66. data/spec/lib/oauth/authorization_code_request_spec.rb +18 -1
  67. data/spec/lib/oauth/base_request_spec.rb +7 -7
  68. data/spec/lib/oauth/client_credentials/creator_spec.rb +51 -7
  69. data/spec/lib/oauth/client_credentials/validation_spec.rb +3 -0
  70. data/spec/lib/oauth/error_response_spec.rb +7 -1
  71. data/spec/lib/oauth/helpers/scope_checker_spec.rb +52 -17
  72. data/spec/lib/oauth/helpers/uri_checker_spec.rb +20 -2
  73. data/spec/lib/oauth/password_access_token_request_spec.rb +43 -12
  74. data/spec/lib/oauth/pre_authorization_spec.rb +24 -0
  75. data/spec/lib/oauth/token_request_spec.rb +16 -1
  76. data/spec/lib/oauth/token_response_spec.rb +13 -13
  77. data/spec/lib/oauth/token_spec.rb +14 -0
  78. data/spec/lib/secret_storing/base_spec.rb +60 -0
  79. data/spec/lib/secret_storing/bcrypt_spec.rb +49 -0
  80. data/spec/lib/secret_storing/plain_spec.rb +44 -0
  81. data/spec/lib/secret_storing/sha256_hash_spec.rb +48 -0
  82. data/spec/models/doorkeeper/access_grant_spec.rb +61 -0
  83. data/spec/models/doorkeeper/access_token_spec.rb +123 -0
  84. data/spec/models/doorkeeper/application_spec.rb +56 -0
  85. data/spec/requests/flows/authorization_code_spec.rb +41 -1
  86. data/spec/requests/flows/client_credentials_spec.rb +2 -2
  87. data/spec/requests/flows/implicit_grant_spec.rb +1 -1
  88. data/spec/requests/flows/password_spec.rb +7 -5
  89. data/spec/requests/flows/revoke_token_spec.rb +14 -30
  90. data/spec/routing/custom_controller_routes_spec.rb +4 -0
  91. data/spec/spec_helper.rb +2 -1
  92. data/spec/support/ruby_2_6_rails_4_2_patch.rb +14 -0
  93. data/spec/support/shared/hashing_shared_context.rb +36 -0
  94. metadata +59 -22
@@ -17,9 +17,11 @@
17
17
  <li class="nav-item <%= 'active' if request.path == oauth_applications_path %>">
18
18
  <%= link_to t('doorkeeper.layouts.admin.nav.applications'), oauth_applications_path, class: 'nav-link' %>
19
19
  </li>
20
- <li class="nav-item">
21
- <%= link_to t('doorkeeper.layouts.admin.nav.home'), root_path, class: 'nav-link' %>
22
- </li>
20
+ <% if respond_to?(:root_path) %>
21
+ <li class="nav-item">
22
+ <%= link_to t('doorkeeper.layouts.admin.nav.home'), root_path, class: 'nav-link' %>
23
+ </li>
24
+ <% end %>
23
25
  </ul>
24
26
  </div>
25
27
  </nav>
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'rails/all'
5
+ require 'doorkeeper'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/doorkeeper.gemspec CHANGED
@@ -19,13 +19,14 @@ Gem::Specification.new do |gem|
19
19
  gem.add_dependency 'railties', '>= 4.2'
20
20
  gem.required_ruby_version = '>= 2.1'
21
21
 
22
- gem.add_development_dependency 'capybara', '~> 2.18'
22
+ gem.add_development_dependency 'appraisal'
23
+ gem.add_development_dependency 'capybara'
23
24
  gem.add_development_dependency 'coveralls'
24
25
  gem.add_development_dependency 'danger', '~> 5.0'
25
- gem.add_development_dependency 'grape'
26
26
  gem.add_development_dependency 'database_cleaner', '~> 1.6'
27
27
  gem.add_development_dependency 'factory_bot', '~> 4.8'
28
28
  gem.add_development_dependency 'generator_spec', '~> 0.9.3'
29
+ gem.add_development_dependency 'grape'
29
30
  gem.add_development_dependency 'rake', '>= 11.3.0'
30
31
  gem.add_development_dependency 'rspec-rails'
31
32
  end
@@ -3,11 +3,15 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~> 4.2.0"
6
- gem "appraisal"
6
+ gem "rspec-core", git: "https://github.com/rspec/rspec-core.git"
7
+ gem "rspec-expectations", git: "https://github.com/rspec/rspec-expectations.git"
8
+ gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
+ gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
+ gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
+ gem "bcrypt", "~> 3.1", require: false
7
12
  gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
8
- gem "sqlite3", platform: [:ruby, :mswin, :mingw, :x64_mingw]
13
+ gem "sqlite3", "~> 1.3", "< 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
9
14
  gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw]
10
- # Older Grape requires Ruby >= 2.2.2
11
- gem "grape", '~> 0.16', '< 0.19.2'
15
+ gem "grape", "~> 0.16", "< 0.19.2"
12
16
 
13
17
  gemspec path: "../"
@@ -2,11 +2,15 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 5.0.0"
6
- gem "appraisal"
7
- gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
8
- gem "sqlite3", platforms: [:ruby, :mswin, :mingw, :x64_mingw]
5
+ gem "rails", "~> 5.0.0", ">= 5.0.7.2"
6
+ gem "rspec-core", git: "https://github.com/rspec/rspec-core.git"
7
+ gem "rspec-expectations", git: "https://github.com/rspec/rspec-expectations.git"
8
+ gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
+ gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
+ gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
+ gem "bcrypt", "~> 3.1", require: false
12
+ gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
13
+ gem "sqlite3", "~> 1.3", "< 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
9
14
  gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw]
10
- gem "rspec-rails", "~> 3.5"
11
15
 
12
16
  gemspec path: "../"
@@ -2,11 +2,15 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 5.1.0"
6
- gem "appraisal"
7
- gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
8
- gem "sqlite3", platforms: [:ruby, :mswin, :mingw, :x64_mingw]
5
+ gem "rails", "~> 5.1.0", ">= 5.1.6.2"
6
+ gem "rspec-core", git: "https://github.com/rspec/rspec-core.git"
7
+ gem "rspec-expectations", git: "https://github.com/rspec/rspec-expectations.git"
8
+ gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
+ gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
+ gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
+ gem "bcrypt", "~> 3.1", require: false
12
+ gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
13
+ gem "sqlite3", "~> 1.3", "< 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
9
14
  gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw]
10
- gem "rspec-rails", "~> 3.7"
11
15
 
12
16
  gemspec path: "../"
@@ -2,11 +2,15 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "5.2.0"
6
- gem "appraisal"
7
- gem "activerecord-jdbcsqlite3-adapter", platforms: :jruby
8
- gem "sqlite3", platforms: [:ruby, :mswin, :mingw, :x64_mingw]
5
+ gem "rails", "~> 5.2.2", ">= 5.2.2.1"
6
+ gem "rspec-core", git: "https://github.com/rspec/rspec-core.git"
7
+ gem "rspec-expectations", git: "https://github.com/rspec/rspec-expectations.git"
8
+ gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
+ gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
+ gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
+ gem "bcrypt", "~> 3.1", require: false
12
+ gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
13
+ gem "sqlite3", "~> 1.3", "< 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
9
14
  gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw]
10
- gem "rspec-rails", "~> 3.7"
11
15
 
12
16
  gemspec path: "../"
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 6.0.0.beta3"
6
+ gem "rspec-core", git: "https://github.com/rspec/rspec-core.git"
7
+ gem "rspec-expectations", git: "https://github.com/rspec/rspec-expectations.git"
8
+ gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
+ gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
+ gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
+ gem "bcrypt", "~> 3.1", require: false
12
+ gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
13
+ gem "sqlite3", "~> 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
14
+ gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw]
15
+
16
+ gemspec path: "../"
@@ -2,16 +2,15 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", git: 'https://github.com/rails/rails'
6
- gem "arel", git: 'https://github.com/rails/arel'
7
-
8
- gem "appraisal"
5
+ gem "rails", git: "https://github.com/rails/rails"
6
+ gem "rspec-core", git: "https://github.com/rspec/rspec-core.git"
7
+ gem "rspec-expectations", git: "https://github.com/rspec/rspec-expectations.git"
8
+ gem "rspec-mocks", git: "https://github.com/rspec/rspec-mocks.git"
9
+ gem "rspec-rails", branch: "4-0-dev", git: "https://github.com/rspec/rspec-rails.git"
10
+ gem "rspec-support", git: "https://github.com/rspec/rspec-support.git"
11
+ gem "bcrypt", "~> 3.1", require: false
9
12
  gem "activerecord-jdbcsqlite3-adapter", platform: :jruby
10
- gem "sqlite3", platform: [:ruby, :mswin, :mingw, :x64_mingw]
13
+ gem "sqlite3", "~> 1.4", platform: [:ruby, :mswin, :mingw, :x64_mingw]
11
14
  gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw]
12
15
 
13
- %w[rspec-core rspec-expectations rspec-mocks rspec-rails rspec-support].each do |lib|
14
- gem lib, git: "https://github.com/rspec/#{lib}.git", branch: 'master'
15
- end
16
-
17
16
  gemspec path: "../"
@@ -46,6 +46,7 @@ module Doorkeeper
46
46
  end
47
47
 
48
48
  def build
49
+ @config.validate
49
50
  @config
50
51
  end
51
52
 
@@ -83,6 +84,13 @@ module Doorkeeper
83
84
  @config.instance_variable_set(:@optional_scopes, OAuth::Scopes.from_array(scopes))
84
85
  end
85
86
 
87
+ # Define scopes_by_grant_type to limit certain scope to certain grant_type
88
+ # @param { Hash } with grant_types as keys.
89
+ # Default set to {} i.e. no limitation on scopes usage
90
+ def scopes_by_grant_type(hash = {})
91
+ @config.instance_variable_set(:@scopes_by_grant_type, hash)
92
+ end
93
+
86
94
  # Change the way client credentials are retrieved from the request object.
87
95
  # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
88
96
  # falls back to the `:client_id` and `:client_secret` params from the
@@ -118,6 +126,15 @@ module Doorkeeper
118
126
  @config.instance_variable_set(:@reuse_access_token, true)
119
127
  end
120
128
 
129
+ # Sets the token_reuse_limit
130
+ # It will be used only when reuse_access_token option in enabled
131
+ # By default it will be 100
132
+ # It will be used for token reusablity to some threshold percentage
133
+ # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/1189
134
+ def token_reuse_limit(percentage)
135
+ @config.instance_variable_set(:@token_reuse_limit, percentage)
136
+ end
137
+
121
138
  # Use an API mode for applications generated with --api argument
122
139
  # It will skip applications controller, disable forgery protection
123
140
  def api_only
@@ -136,6 +153,57 @@ module Doorkeeper
136
153
  def enforce_content_type
137
154
  @config.instance_variable_set(:@enforce_content_type, true)
138
155
  end
156
+
157
+ # Allow optional hashing of input tokens before persisting them.
158
+ # Will be used for hashing of input token and grants.
159
+ #
160
+ # @param using
161
+ # Provide a different secret storage implementation class for tokens
162
+ # @param fallback
163
+ # Provide a fallback secret storage implementation class for tokens
164
+ # or use :plain to fallback to plain tokens
165
+ def hash_token_secrets(using: nil, fallback: nil)
166
+ default = '::Doorkeeper::SecretStoring::Sha256Hash'
167
+ configure_secrets_for :token,
168
+ using: using || default,
169
+ fallback: fallback
170
+ end
171
+
172
+ # Allow optional hashing of application secrets before persisting them.
173
+ # Will be used for hashing of input token and grants.
174
+ #
175
+ # @param using
176
+ # Provide a different secret storage implementation for applications
177
+ # @param fallback
178
+ # Provide a fallback secret storage implementation for applications
179
+ # or use :plain to fallback to plain application secrets
180
+ def hash_application_secrets(using: nil, fallback: nil)
181
+ default = '::Doorkeeper::SecretStoring::Sha256Hash'
182
+ configure_secrets_for :application,
183
+ using: using || default,
184
+ fallback: fallback
185
+ end
186
+
187
+ private
188
+
189
+ # Configure the secret storing functionality
190
+ def configure_secrets_for(type, using:, fallback:)
191
+ unless %i[application token].include?(type)
192
+ raise ArgumentError, "Invalid type #{type}"
193
+ end
194
+
195
+ @config.instance_variable_set(:"@#{type}_secret_strategy",
196
+ using.constantize)
197
+
198
+ if fallback.nil?
199
+ return
200
+ elsif fallback.to_sym == :plain
201
+ fallback = '::Doorkeeper::SecretStoring::Plain'
202
+ end
203
+
204
+ @config.instance_variable_set(:"@#{type}_secret_fallback_strategy",
205
+ fallback.constantize)
206
+ end
139
207
  end
140
208
 
141
209
  module Option
@@ -226,11 +294,16 @@ module Doorkeeper
226
294
 
227
295
  nil
228
296
  end)
229
- option :before_successful_authorization, default: ->(_context) {}
230
- option :after_successful_authorization, default: ->(_context) {}
231
- option :before_successful_strategy_response, default: ->(_request) {}
232
- option :after_successful_strategy_response,
233
- default: ->(_request, _response) {}
297
+
298
+ # Hooks for authorization
299
+ option :before_successful_authorization, default: ->(_context) {}
300
+ option :after_successful_authorization, default: ->(_context) {}
301
+ # Hooks for strategies responses
302
+ option :before_successful_strategy_response, default: ->(_request) {}
303
+ option :after_successful_strategy_response, default: ->(_request, _response) {}
304
+ # Allows to customize Token Introspection response
305
+ option :custom_introspection_response, default: ->(_token, _context) { {} }
306
+
234
307
  option :skip_authorization, default: ->(_routes) {}
235
308
  option :access_token_expires_in, default: 7200
236
309
  option :custom_access_token_expires_in, default: ->(_context) { nil }
@@ -278,6 +351,15 @@ module Doorkeeper
278
351
  option :access_token_generator,
279
352
  default: 'Doorkeeper::OAuth::Helpers::UniqueToken'
280
353
 
354
+
355
+ # Default access token generator is a SecureRandom class from Ruby stdlib.
356
+ # This option defines which method will be used to generate a unique token value.
357
+ #
358
+ # @param access_token_generator [String]
359
+ # the name of the access token generator class
360
+ #
361
+ option :default_generator_method, default: :urlsafe_base64
362
+
281
363
  # The controller Doorkeeper::ApplicationController inherits from.
282
364
  # Defaults to ActionController::Base.
283
365
  # https://github.com/doorkeeper-gem/doorkeeper#custom-base-controller
@@ -286,9 +368,16 @@ module Doorkeeper
286
368
  option :base_controller,
287
369
  default: 'ActionController::Base'
288
370
 
289
- attr_reader :reuse_access_token
290
- attr_reader :api_only
291
- attr_reader :enforce_content_type
371
+ attr_reader :api_only,
372
+ :enforce_content_type,
373
+ :reuse_access_token
374
+
375
+ # Return the valid subset of this configuration
376
+ def validate
377
+ validate_reuse_access_token_value
378
+ validate_token_reuse_limit
379
+ validate_secret_strategies
380
+ end
292
381
 
293
382
  def api_only
294
383
  @api_only ||= false
@@ -306,22 +395,42 @@ module Doorkeeper
306
395
  end
307
396
  end
308
397
 
398
+ def token_reuse_limit
399
+ @token_reuse_limit ||= 100
400
+ end
401
+
309
402
  def enforce_configured_scopes?
310
- !!(defined?(@enforce_configured_scopes) && @enforce_configured_scopes)
403
+ option_set? :enforce_configured_scopes
311
404
  end
312
405
 
313
406
  def enable_application_owner?
314
- !!(defined?(@enable_application_owner) && @enable_application_owner)
407
+ option_set? :enable_application_owner
315
408
  end
316
409
 
317
410
  def confirm_application_owner?
318
- !!(defined?(@confirm_application_owner) && @confirm_application_owner)
411
+ option_set? :confirm_application_owner
319
412
  end
320
413
 
321
414
  def raise_on_errors?
322
415
  handle_auth_errors == :raise
323
416
  end
324
417
 
418
+ def token_secret_strategy
419
+ @token_secret_strategy ||= ::Doorkeeper::SecretStoring::Plain
420
+ end
421
+
422
+ def token_secret_fallback_strategy
423
+ @token_secret_fallback_strategy
424
+ end
425
+
426
+ def application_secret_strategy
427
+ @application_secret_strategy ||= ::Doorkeeper::SecretStoring::Plain
428
+ end
429
+
430
+ def application_secret_fallback_strategy
431
+ @application_secret_fallback_strategy
432
+ end
433
+
325
434
  def default_scopes
326
435
  @default_scopes ||= OAuth::Scopes.new
327
436
  end
@@ -334,6 +443,10 @@ module Doorkeeper
334
443
  @scopes ||= default_scopes + optional_scopes
335
444
  end
336
445
 
446
+ def scopes_by_grant_type
447
+ @scopes_by_grant_type ||= {}
448
+ end
449
+
337
450
  def client_credentials_methods
338
451
  @client_credentials_methods ||= %i[from_basic from_params]
339
452
  end
@@ -352,6 +465,12 @@ module Doorkeeper
352
465
 
353
466
  private
354
467
 
468
+ # Helper to read boolearized configuration option
469
+ def option_set?(instance_key)
470
+ var = instance_variable_get("@#{instance_key}")
471
+ !!(defined?(var) && var)
472
+ end
473
+
355
474
  # Determines what values are acceptable for 'response_type' param in
356
475
  # authorization request endpoint, and return them as an array of strings.
357
476
  #
@@ -370,5 +489,39 @@ module Doorkeeper
370
489
  types << 'refresh_token' if refresh_token_enabled?
371
490
  types
372
491
  end
492
+
493
+ # Determine whether +reuse_access_token+ and a non-restorable
494
+ # +token_secret_strategy+ have both been activated.
495
+ #
496
+ # In that case, disable reuse_access_token value and warn the user.
497
+ def validate_reuse_access_token_value
498
+ strategy = token_secret_strategy
499
+ return if !reuse_access_token || strategy.allows_restoring_secrets?
500
+
501
+ ::Rails.logger.warn(
502
+ "You have configured both reuse_access_token " \
503
+ "AND strategy strategy '#{strategy}' that cannot restore tokens. " \
504
+ "This combination is unsupported. reuse_access_token will be disabled"
505
+ )
506
+ @reuse_access_token = false
507
+ end
508
+
509
+ # Validate that the provided strategies are valid for
510
+ # tokens and applications
511
+ def validate_secret_strategies
512
+ token_secret_strategy.validate_for :token
513
+ application_secret_strategy.validate_for :application
514
+ end
515
+
516
+ def validate_token_reuse_limit
517
+ return if !reuse_access_token ||
518
+ (token_reuse_limit > 0 && token_reuse_limit <= 100)
519
+
520
+ ::Rails.logger.warn(
521
+ 'You have configured an invalid value for token_reuse_limit option. ' \
522
+ 'It will be set to default 100'
523
+ )
524
+ @token_reuse_limit = 100
525
+ end
373
526
  end
374
527
  end
@@ -55,8 +55,9 @@ module Doorkeeper
55
55
  end
56
56
 
57
57
  def enforce_content_type
58
- return if request.content_type == 'application/x-www-form-urlencoded'
59
- render json: {}, status: :unsupported_media_type
58
+ if (request.put? || request.post? || request.patch?) && request.content_type != 'application/x-www-form-urlencoded'
59
+ render json: {}, status: :unsupported_media_type
60
+ end
60
61
  end
61
62
  end
62
63
  end
@@ -9,6 +9,7 @@ module Doorkeeper
9
9
  include Models::Revocable
10
10
  include Models::Accessible
11
11
  include Models::Orderable
12
+ include Models::SecretStorable
12
13
  include Models::Scopes
13
14
 
14
15
  # never uses pkce, if pkce migrations were not generated
@@ -30,7 +31,7 @@ module Doorkeeper
30
31
  # if there is no record with such token
31
32
  #
32
33
  def by_token(token)
33
- find_by(token: token.to_s)
34
+ find_by_plaintext_token(:token, token)
34
35
  end
35
36
 
36
37
  # Revokes AccessGrant records that have not been revoked and associated
@@ -94,6 +95,20 @@ module Doorkeeper
94
95
  def pkce_supported?
95
96
  new.pkce_supported?
96
97
  end
98
+
99
+ ##
100
+ # Determines the secret storing transformer
101
+ # Unless configured otherwise, uses the plain secret strategy
102
+ def secret_strategy
103
+ ::Doorkeeper.configuration.token_secret_strategy
104
+ end
105
+
106
+ ##
107
+ # Determine the fallback storing strategy
108
+ # Unless configured, there will be no fallback
109
+ def fallback_secret_strategy
110
+ ::Doorkeeper.configuration.token_secret_fallback_strategy
111
+ end
97
112
  end
98
113
  end
99
114
  end
@@ -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,7 +127,7 @@ 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 && access_token.reusable?
129
131
  end
130
132
 
131
133
  create!(
@@ -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.
@@ -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