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
@@ -1,9 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "doorkeeper/config/option"
4
+
1
5
  module Doorkeeper
2
6
  class MissingConfiguration < StandardError
3
7
  # Defines a MissingConfiguration error for a missing Doorkeeper
4
8
  # configuration
5
9
  def initialize
6
- super('Configuration for doorkeeper missing. Do you have doorkeeper initializer?')
10
+ super("Configuration for doorkeeper missing. Do you have doorkeeper initializer?")
7
11
  end
8
12
  end
9
13
 
@@ -12,6 +16,7 @@ module Doorkeeper
12
16
  setup_orm_adapter
13
17
  setup_orm_models
14
18
  setup_application_owner if @config.enable_application_owner?
19
+ @config
15
20
  end
16
21
 
17
22
  def self.configuration
@@ -46,6 +51,7 @@ module Doorkeeper
46
51
  end
47
52
 
48
53
  def build
54
+ @config.validate
49
55
  @config
50
56
  end
51
57
 
@@ -83,6 +89,13 @@ module Doorkeeper
83
89
  @config.instance_variable_set(:@optional_scopes, OAuth::Scopes.from_array(scopes))
84
90
  end
85
91
 
92
+ # Define scopes_by_grant_type to limit certain scope to certain grant_type
93
+ # @param { Hash } with grant_types as keys.
94
+ # Default set to {} i.e. no limitation on scopes usage
95
+ def scopes_by_grant_type(hash = {})
96
+ @config.instance_variable_set(:@scopes_by_grant_type, hash)
97
+ end
98
+
86
99
  # Change the way client credentials are retrieved from the request object.
87
100
  # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
88
101
  # falls back to the `:client_id` and `:client_secret` params from the
@@ -118,6 +131,15 @@ module Doorkeeper
118
131
  @config.instance_variable_set(:@reuse_access_token, true)
119
132
  end
120
133
 
134
+ # Sets the token_reuse_limit
135
+ # It will be used only when reuse_access_token option in enabled
136
+ # By default it will be 100
137
+ # It will be used for token reusablity to some threshold percentage
138
+ # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/1189
139
+ def token_reuse_limit(percentage)
140
+ @config.instance_variable_set(:@token_reuse_limit, percentage)
141
+ end
142
+
121
143
  # Use an API mode for applications generated with --api argument
122
144
  # It will skip applications controller, disable forgery protection
123
145
  def api_only
@@ -136,63 +158,54 @@ module Doorkeeper
136
158
  def enforce_content_type
137
159
  @config.instance_variable_set(:@enforce_content_type, true)
138
160
  end
139
- end
140
161
 
141
- module Option
142
- # Defines configuration option
162
+ # Allow optional hashing of input tokens before persisting them.
163
+ # Will be used for hashing of input token and grants.
143
164
  #
144
- # When you call option, it defines two methods. One method will take place
145
- # in the +Config+ class and the other method will take place in the
146
- # +Builder+ class.
147
- #
148
- # The +name+ parameter will set both builder method and config attribute.
149
- # If the +:as+ option is defined, the builder method will be the specified
150
- # option while the config attribute will be the +name+ parameter.
151
- #
152
- # If you want to introduce another level of config DSL you can
153
- # define +builder_class+ parameter.
154
- # Builder should take a block as the initializer parameter and respond to function +build+
155
- # that returns the value of the config attribute.
156
- #
157
- # ==== Options
158
- #
159
- # * [:+as+] Set the builder method that goes inside +configure+ block
160
- # * [+:default+] The default value in case no option was set
161
- #
162
- # ==== Examples
163
- #
164
- # option :name
165
- # option :name, as: :set_name
166
- # option :name, default: 'My Name'
167
- # option :scopes builder_class: ScopesBuilder
165
+ # @param using
166
+ # Provide a different secret storage implementation class for tokens
167
+ # @param fallback
168
+ # Provide a fallback secret storage implementation class for tokens
169
+ # or use :plain to fallback to plain tokens
170
+ def hash_token_secrets(using: nil, fallback: nil)
171
+ default = "::Doorkeeper::SecretStoring::Sha256Hash"
172
+ configure_secrets_for :token,
173
+ using: using || default,
174
+ fallback: fallback
175
+ end
176
+
177
+ # Allow optional hashing of application secrets before persisting them.
178
+ # Will be used for hashing of input token and grants.
168
179
  #
169
- def option(name, options = {})
170
- attribute = options[:as] || name
171
- attribute_builder = options[:builder_class]
172
-
173
- Builder.instance_eval do
174
- remove_method name if method_defined?(name)
175
- define_method name do |*args, &block|
176
- # TODO: is builder_class option being used?
177
- value = if attribute_builder
178
- attribute_builder.new(&block).build
179
- else
180
- block || args.first
181
- end
182
-
183
- @config.instance_variable_set(:"@#{attribute}", value)
184
- end
185
- end
180
+ # @param using
181
+ # Provide a different secret storage implementation for applications
182
+ # @param fallback
183
+ # Provide a fallback secret storage implementation for applications
184
+ # or use :plain to fallback to plain application secrets
185
+ def hash_application_secrets(using: nil, fallback: nil)
186
+ default = "::Doorkeeper::SecretStoring::Sha256Hash"
187
+ configure_secrets_for :application,
188
+ using: using || default,
189
+ fallback: fallback
190
+ end
191
+
192
+ private
186
193
 
187
- define_method attribute do |*_args|
188
- if instance_variable_defined?(:"@#{attribute}")
189
- instance_variable_get(:"@#{attribute}")
190
- else
191
- options[:default]
192
- end
194
+ # Configure the secret storing functionality
195
+ def configure_secrets_for(type, using:, fallback:)
196
+ raise ArgumentError, "Invalid type #{type}" if %i[application token].exclude?(type)
197
+
198
+ @config.instance_variable_set(:"@#{type}_secret_strategy",
199
+ using.constantize)
200
+
201
+ if fallback.nil?
202
+ return
203
+ elsif fallback.to_sym == :plain
204
+ fallback = "::Doorkeeper::SecretStoring::Plain"
193
205
  end
194
206
 
195
- public attribute
207
+ @config.instance_variable_set(:"@#{type}_secret_fallback_strategy",
208
+ fallback.constantize)
196
209
  end
197
210
  end
198
211
 
@@ -202,7 +215,7 @@ module Doorkeeper
202
215
  as: :authenticate_resource_owner,
203
216
  default: (lambda do |_routes|
204
217
  ::Rails.logger.warn(
205
- I18n.t('doorkeeper.errors.messages.resource_owner_authenticator_not_configured')
218
+ I18n.t("doorkeeper.errors.messages.resource_owner_authenticator_not_configured")
206
219
  )
207
220
 
208
221
  nil
@@ -212,7 +225,7 @@ module Doorkeeper
212
225
  as: :authenticate_admin,
213
226
  default: (lambda do |_routes|
214
227
  ::Rails.logger.warn(
215
- I18n.t('doorkeeper.errors.messages.admin_authenticator_not_configured')
228
+ I18n.t("doorkeeper.errors.messages.admin_authenticator_not_configured")
216
229
  )
217
230
 
218
231
  head :forbidden
@@ -221,22 +234,27 @@ module Doorkeeper
221
234
  option :resource_owner_from_credentials,
222
235
  default: (lambda do |_routes|
223
236
  ::Rails.logger.warn(
224
- I18n.t('doorkeeper.errors.messages.credential_flow_not_configured')
237
+ I18n.t("doorkeeper.errors.messages.credential_flow_not_configured")
225
238
  )
226
239
 
227
240
  nil
228
241
  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) {}
242
+
243
+ # Hooks for authorization
244
+ option :before_successful_authorization, default: ->(_context) {}
245
+ option :after_successful_authorization, default: ->(_context) {}
246
+ # Hooks for strategies responses
247
+ option :before_successful_strategy_response, default: ->(_request) {}
248
+ option :after_successful_strategy_response, default: ->(_request, _response) {}
249
+ # Allows to customize Token Introspection response
250
+ option :custom_introspection_response, default: ->(_token, _context) { {} }
251
+
234
252
  option :skip_authorization, default: ->(_routes) {}
235
253
  option :access_token_expires_in, default: 7200
236
254
  option :custom_access_token_expires_in, default: ->(_context) { nil }
237
255
  option :authorization_code_expires_in, default: 600
238
256
  option :orm, default: :active_record
239
- option :native_redirect_uri, default: 'urn:ietf:wg:oauth:2.0:oob'
257
+ option :native_redirect_uri, default: "urn:ietf:wg:oauth:2.0:oob"
240
258
  option :active_record_options, default: {}
241
259
  option :grant_flows, default: %w[authorization_code client_credentials]
242
260
  option :handle_auth_errors, default: :render
@@ -252,7 +270,7 @@ module Doorkeeper
252
270
  #
253
271
  # @param realm [String] ("Doorkeeper") Authentication realm
254
272
  #
255
- option :realm, default: 'Doorkeeper'
273
+ option :realm, default: "Doorkeeper"
256
274
 
257
275
  # Forces the usage of the HTTPS protocol in non-native redirect uris
258
276
  # (enabled by default in non-development environments). OAuth2
@@ -276,7 +294,15 @@ module Doorkeeper
276
294
  # the name of the access token generator class
277
295
  #
278
296
  option :access_token_generator,
279
- default: 'Doorkeeper::OAuth::Helpers::UniqueToken'
297
+ default: "Doorkeeper::OAuth::Helpers::UniqueToken"
298
+
299
+ # Default access token generator is a SecureRandom class from Ruby stdlib.
300
+ # This option defines which method will be used to generate a unique token value.
301
+ #
302
+ # @param access_token_generator [String]
303
+ # the name of the access token generator class
304
+ #
305
+ option :default_generator_method, default: :urlsafe_base64
280
306
 
281
307
  # The controller Doorkeeper::ApplicationController inherits from.
282
308
  # Defaults to ActionController::Base.
@@ -284,11 +310,29 @@ module Doorkeeper
284
310
  #
285
311
  # @param base_controller [String] the name of the base controller
286
312
  option :base_controller,
287
- default: 'ActionController::Base'
313
+ default: "ActionController::Base"
288
314
 
289
- attr_reader :reuse_access_token
290
- attr_reader :api_only
291
- attr_reader :enforce_content_type
315
+ # Allows to set blank redirect URIs for Applications in case
316
+ # server configured to use URI-less grant flows.
317
+ #
318
+ option :allow_blank_redirect_uri,
319
+ default: (lambda do |grant_flows, _application|
320
+ grant_flows.exclude?("authorization_code") &&
321
+ grant_flows.exclude?("implicit")
322
+ end)
323
+
324
+ attr_reader :api_only,
325
+ :enforce_content_type,
326
+ :reuse_access_token,
327
+ :token_secret_fallback_strategy,
328
+ :application_secret_fallback_strategy
329
+
330
+ # Return the valid subset of this configuration
331
+ def validate
332
+ validate_reuse_access_token_value
333
+ validate_token_reuse_limit
334
+ validate_secret_strategies
335
+ end
292
336
 
293
337
  def api_only
294
338
  @api_only ||= false
@@ -306,22 +350,34 @@ module Doorkeeper
306
350
  end
307
351
  end
308
352
 
353
+ def token_reuse_limit
354
+ @token_reuse_limit ||= 100
355
+ end
356
+
309
357
  def enforce_configured_scopes?
310
- !!(defined?(@enforce_configured_scopes) && @enforce_configured_scopes)
358
+ option_set? :enforce_configured_scopes
311
359
  end
312
360
 
313
361
  def enable_application_owner?
314
- !!(defined?(@enable_application_owner) && @enable_application_owner)
362
+ option_set? :enable_application_owner
315
363
  end
316
364
 
317
365
  def confirm_application_owner?
318
- !!(defined?(@confirm_application_owner) && @confirm_application_owner)
366
+ option_set? :confirm_application_owner
319
367
  end
320
368
 
321
369
  def raise_on_errors?
322
370
  handle_auth_errors == :raise
323
371
  end
324
372
 
373
+ def token_secret_strategy
374
+ @token_secret_strategy ||= ::Doorkeeper::SecretStoring::Plain
375
+ end
376
+
377
+ def application_secret_strategy
378
+ @application_secret_strategy ||= ::Doorkeeper::SecretStoring::Plain
379
+ end
380
+
325
381
  def default_scopes
326
382
  @default_scopes ||= OAuth::Scopes.new
327
383
  end
@@ -334,12 +390,20 @@ module Doorkeeper
334
390
  @scopes ||= default_scopes + optional_scopes
335
391
  end
336
392
 
393
+ def scopes_by_grant_type
394
+ @scopes_by_grant_type ||= {}
395
+ end
396
+
337
397
  def client_credentials_methods
338
398
  @client_credentials_methods ||= %i[from_basic from_params]
339
399
  end
340
400
 
341
401
  def access_token_methods
342
- @access_token_methods ||= %i[from_bearer_authorization from_access_token_param from_bearer_param]
402
+ @access_token_methods ||= %i[
403
+ from_bearer_authorization
404
+ from_access_token_param
405
+ from_bearer_param
406
+ ]
343
407
  end
344
408
 
345
409
  def authorization_response_types
@@ -350,15 +414,33 @@ module Doorkeeper
350
414
  @token_grant_types ||= calculate_token_grant_types.freeze
351
415
  end
352
416
 
417
+ def allow_blank_redirect_uri?(application = nil)
418
+ if allow_blank_redirect_uri.respond_to?(:call)
419
+ allow_blank_redirect_uri.call(grant_flows, application)
420
+ else
421
+ allow_blank_redirect_uri
422
+ end
423
+ end
424
+
425
+ def option_defined?(name)
426
+ instance_variable_defined?("@#{name}")
427
+ end
428
+
353
429
  private
354
430
 
431
+ # Helper to read boolearized configuration option
432
+ def option_set?(instance_key)
433
+ var = instance_variable_get("@#{instance_key}")
434
+ !!(defined?(var) && var)
435
+ end
436
+
355
437
  # Determines what values are acceptable for 'response_type' param in
356
438
  # authorization request endpoint, and return them as an array of strings.
357
439
  #
358
440
  def calculate_authorization_response_types
359
441
  types = []
360
- types << 'code' if grant_flows.include? 'authorization_code'
361
- types << 'token' if grant_flows.include? 'implicit'
442
+ types << "code" if grant_flows.include? "authorization_code"
443
+ types << "token" if grant_flows.include? "implicit"
362
444
  types
363
445
  end
364
446
 
@@ -366,9 +448,43 @@ module Doorkeeper
366
448
  # request endpoint, and return them in array.
367
449
  #
368
450
  def calculate_token_grant_types
369
- types = grant_flows - ['implicit']
370
- types << 'refresh_token' if refresh_token_enabled?
451
+ types = grant_flows - ["implicit"]
452
+ types << "refresh_token" if refresh_token_enabled?
371
453
  types
372
454
  end
455
+
456
+ # Determine whether +reuse_access_token+ and a non-restorable
457
+ # +token_secret_strategy+ have both been activated.
458
+ #
459
+ # In that case, disable reuse_access_token value and warn the user.
460
+ def validate_reuse_access_token_value
461
+ strategy = token_secret_strategy
462
+ return if !reuse_access_token || strategy.allows_restoring_secrets?
463
+
464
+ ::Rails.logger.warn(
465
+ "You have configured both reuse_access_token " \
466
+ "AND strategy strategy '#{strategy}' that cannot restore tokens. " \
467
+ "This combination is unsupported. reuse_access_token will be disabled"
468
+ )
469
+ @reuse_access_token = false
470
+ end
471
+
472
+ # Validate that the provided strategies are valid for
473
+ # tokens and applications
474
+ def validate_secret_strategies
475
+ token_secret_strategy.validate_for :token
476
+ application_secret_strategy.validate_for :application
477
+ end
478
+
479
+ def validate_token_reuse_limit
480
+ return if !reuse_access_token ||
481
+ (token_reuse_limit > 0 && token_reuse_limit <= 100)
482
+
483
+ ::Rails.logger.warn(
484
+ "You have configured an invalid value for token_reuse_limit option. " \
485
+ "It will be set to default 100"
486
+ )
487
+ @token_reuse_limit = 100
488
+ end
373
489
  end
374
490
  end
@@ -18,7 +18,7 @@ module Doorkeeper
18
18
  end
19
19
 
20
20
  if defined?(Sprockets) && Sprockets::VERSION.chr.to_i >= 4
21
- initializer 'doorkeeper.assets.precompile' do |app|
21
+ initializer "doorkeeper.assets.precompile" do |app|
22
22
  # Force users to use:
23
23
  # //= link doorkeeper/admin/application.css
24
24
  # in Doorkeeper 5 for Sprockets 4 instead of precompile.
@@ -9,10 +9,10 @@ module Doorkeeper
9
9
 
10
10
  def authorization
11
11
  env = __getobj__.env
12
- env['HTTP_AUTHORIZATION'] ||
13
- env['X-HTTP_AUTHORIZATION'] ||
14
- env['X_HTTP_AUTHORIZATION'] ||
15
- env['REDIRECT_X_HTTP_AUTHORIZATION']
12
+ env["HTTP_AUTHORIZATION"] ||
13
+ env["X-HTTP_AUTHORIZATION"] ||
14
+ env["X_HTTP_AUTHORIZATION"] ||
15
+ env["REDIRECT_X_HTTP_AUTHORIZATION"]
16
16
  end
17
17
  end
18
18
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'doorkeeper/grape/authorization_decorator'
3
+ require "doorkeeper/grape/authorization_decorator"
4
4
 
5
5
  module Doorkeeper
6
6
  module Grape
@@ -29,7 +29,7 @@ module Doorkeeper
29
29
  private
30
30
 
31
31
  def endpoint
32
- env['api.endpoint']
32
+ env["api.endpoint"]
33
33
  end
34
34
 
35
35
  def doorkeeper_token
@@ -46,7 +46,7 @@ module Doorkeeper
46
46
  def error_status_codes
47
47
  {
48
48
  unauthorized: 401,
49
- forbidden: 403
49
+ forbidden: 403,
50
50
  }
51
51
  end
52
52
  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
@@ -85,15 +86,31 @@ module Doorkeeper
85
86
 
86
87
  # @param code_verifier [#to_s] a one time use value (any object that responds to `#to_s`)
87
88
  #
88
- # @return [#to_s] An encoded code challenge based on the provided verifier suitable for PKCE validation
89
+ # @return [#to_s] An encoded code challenge based on the provided verifier
90
+ # suitable for PKCE validation
91
+ #
89
92
  def generate_code_challenge(code_verifier)
90
93
  padded_result = Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier))
91
- padded_result.split('=')[0] # Remove any trailing '='
94
+ padded_result.split("=")[0] # Remove any trailing '='
92
95
  end
93
96
 
94
97
  def pkce_supported?
95
98
  new.pkce_supported?
96
99
  end
100
+
101
+ ##
102
+ # Determines the secret storing transformer
103
+ # Unless configured otherwise, uses the plain secret strategy
104
+ def secret_strategy
105
+ ::Doorkeeper.configuration.token_secret_strategy
106
+ end
107
+
108
+ ##
109
+ # Determine the fallback storing strategy
110
+ # Unless configured, there will be no fallback
111
+ def fallback_secret_strategy
112
+ ::Doorkeeper.configuration.token_secret_fallback_strategy
113
+ end
97
114
  end
98
115
  end
99
116
  end