doorkeeper 5.3.3 → 5.5.0.rc2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

Files changed (233) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +125 -7
  3. data/README.md +6 -4
  4. data/app/controllers/doorkeeper/applications_controller.rb +4 -4
  5. data/app/controllers/doorkeeper/authorizations_controller.rb +46 -16
  6. data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -2
  7. data/app/controllers/doorkeeper/tokens_controller.rb +67 -22
  8. data/app/views/doorkeeper/applications/_form.html.erb +1 -1
  9. data/app/views/doorkeeper/applications/show.html.erb +35 -14
  10. data/app/views/doorkeeper/authorizations/form_post.html.erb +11 -0
  11. data/config/locales/en.yml +6 -2
  12. data/lib/doorkeeper.rb +111 -79
  13. data/lib/doorkeeper/config.rb +148 -94
  14. data/lib/doorkeeper/config/abstract_builder.rb +28 -0
  15. data/lib/doorkeeper/config/option.rb +26 -14
  16. data/lib/doorkeeper/config/validations.rb +53 -0
  17. data/lib/doorkeeper/engine.rb +1 -1
  18. data/lib/doorkeeper/grant_flow.rb +45 -0
  19. data/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
  20. data/lib/doorkeeper/grant_flow/flow.rb +44 -0
  21. data/lib/doorkeeper/grant_flow/registry.rb +50 -0
  22. data/lib/doorkeeper/grape/helpers.rb +1 -1
  23. data/lib/doorkeeper/helpers/controller.rb +8 -4
  24. data/lib/doorkeeper/models/access_grant_mixin.rb +21 -18
  25. data/lib/doorkeeper/models/access_token_mixin.rb +110 -47
  26. data/lib/doorkeeper/models/application_mixin.rb +5 -4
  27. data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
  28. data/lib/doorkeeper/models/concerns/revocable.rb +1 -1
  29. data/lib/doorkeeper/models/concerns/scopes.rb +5 -1
  30. data/lib/doorkeeper/models/concerns/secret_storable.rb +1 -3
  31. data/lib/doorkeeper/oauth/authorization/code.rb +19 -6
  32. data/lib/doorkeeper/oauth/authorization/context.rb +5 -5
  33. data/lib/doorkeeper/oauth/authorization/token.rb +18 -16
  34. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +4 -4
  35. data/lib/doorkeeper/oauth/authorization_code_request.rb +17 -14
  36. data/lib/doorkeeper/oauth/base_request.rb +12 -20
  37. data/lib/doorkeeper/oauth/client.rb +1 -1
  38. data/lib/doorkeeper/oauth/client/credentials.rb +2 -4
  39. data/lib/doorkeeper/oauth/client_credentials/creator.rb +27 -8
  40. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +4 -2
  41. data/lib/doorkeeper/oauth/client_credentials/validator.rb +4 -2
  42. data/lib/doorkeeper/oauth/client_credentials_request.rb +8 -7
  43. data/lib/doorkeeper/oauth/code_request.rb +3 -3
  44. data/lib/doorkeeper/oauth/code_response.rb +22 -12
  45. data/lib/doorkeeper/oauth/error_response.rb +6 -7
  46. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +2 -8
  47. data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
  48. data/lib/doorkeeper/oauth/invalid_token_response.rb +2 -2
  49. data/lib/doorkeeper/oauth/password_access_token_request.rb +24 -7
  50. data/lib/doorkeeper/oauth/pre_authorization.rb +63 -32
  51. data/lib/doorkeeper/oauth/refresh_token_request.rb +31 -22
  52. data/lib/doorkeeper/oauth/token.rb +5 -6
  53. data/lib/doorkeeper/oauth/token_introspection.rb +4 -8
  54. data/lib/doorkeeper/oauth/token_request.rb +3 -3
  55. data/lib/doorkeeper/oauth/token_response.rb +1 -1
  56. data/lib/doorkeeper/orm/active_record.rb +14 -7
  57. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +8 -3
  58. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +7 -3
  59. data/lib/doorkeeper/orm/active_record/mixins/application.rb +6 -3
  60. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +5 -0
  61. data/lib/doorkeeper/rails/routes.rb +14 -20
  62. data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
  63. data/lib/doorkeeper/rails/routes/mapper.rb +2 -2
  64. data/lib/doorkeeper/rails/routes/registry.rb +45 -0
  65. data/lib/doorkeeper/request.rb +49 -12
  66. data/lib/doorkeeper/request/refresh_token.rb +2 -1
  67. data/lib/doorkeeper/request/strategy.rb +2 -2
  68. data/lib/doorkeeper/server.rb +4 -4
  69. data/lib/doorkeeper/stale_records_cleaner.rb +4 -4
  70. data/lib/doorkeeper/version.rb +3 -7
  71. data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
  72. data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
  73. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +3 -1
  74. data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +2 -0
  75. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +2 -0
  76. data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
  77. data/lib/generators/doorkeeper/templates/initializer.rb +48 -10
  78. data/lib/generators/doorkeeper/templates/migration.rb.erb +14 -5
  79. metadata +30 -300
  80. data/Appraisals +0 -40
  81. data/CODE_OF_CONDUCT.md +0 -46
  82. data/CONTRIBUTING.md +0 -49
  83. data/Dangerfile +0 -67
  84. data/Dockerfile +0 -29
  85. data/Gemfile +0 -25
  86. data/NEWS.md +0 -1
  87. data/RELEASING.md +0 -11
  88. data/Rakefile +0 -28
  89. data/SECURITY.md +0 -15
  90. data/UPGRADE.md +0 -2
  91. data/bin/console +0 -16
  92. data/doorkeeper.gemspec +0 -42
  93. data/gemfiles/rails_5_0.gemfile +0 -18
  94. data/gemfiles/rails_5_1.gemfile +0 -18
  95. data/gemfiles/rails_5_2.gemfile +0 -18
  96. data/gemfiles/rails_6_0.gemfile +0 -18
  97. data/gemfiles/rails_master.gemfile +0 -18
  98. data/spec/controllers/application_metal_controller_spec.rb +0 -64
  99. data/spec/controllers/applications_controller_spec.rb +0 -274
  100. data/spec/controllers/authorizations_controller_spec.rb +0 -608
  101. data/spec/controllers/protected_resources_controller_spec.rb +0 -361
  102. data/spec/controllers/token_info_controller_spec.rb +0 -50
  103. data/spec/controllers/tokens_controller_spec.rb +0 -498
  104. data/spec/dummy/Rakefile +0 -9
  105. data/spec/dummy/app/assets/config/manifest.js +0 -2
  106. data/spec/dummy/app/controllers/application_controller.rb +0 -5
  107. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +0 -9
  108. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +0 -14
  109. data/spec/dummy/app/controllers/home_controller.rb +0 -18
  110. data/spec/dummy/app/controllers/metal_controller.rb +0 -13
  111. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +0 -13
  112. data/spec/dummy/app/helpers/application_helper.rb +0 -7
  113. data/spec/dummy/app/models/user.rb +0 -7
  114. data/spec/dummy/app/views/home/index.html.erb +0 -0
  115. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  116. data/spec/dummy/config.ru +0 -6
  117. data/spec/dummy/config/application.rb +0 -49
  118. data/spec/dummy/config/boot.rb +0 -7
  119. data/spec/dummy/config/database.yml +0 -15
  120. data/spec/dummy/config/environment.rb +0 -5
  121. data/spec/dummy/config/environments/development.rb +0 -31
  122. data/spec/dummy/config/environments/production.rb +0 -64
  123. data/spec/dummy/config/environments/test.rb +0 -45
  124. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -9
  125. data/spec/dummy/config/initializers/doorkeeper.rb +0 -166
  126. data/spec/dummy/config/initializers/secret_token.rb +0 -10
  127. data/spec/dummy/config/initializers/session_store.rb +0 -10
  128. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -16
  129. data/spec/dummy/config/locales/doorkeeper.en.yml +0 -5
  130. data/spec/dummy/config/routes.rb +0 -13
  131. data/spec/dummy/db/migrate/20111122132257_create_users.rb +0 -11
  132. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +0 -7
  133. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +0 -69
  134. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +0 -9
  135. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +0 -13
  136. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +0 -8
  137. data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +0 -13
  138. data/spec/dummy/db/schema.rb +0 -68
  139. data/spec/dummy/public/404.html +0 -26
  140. data/spec/dummy/public/422.html +0 -26
  141. data/spec/dummy/public/500.html +0 -26
  142. data/spec/dummy/public/favicon.ico +0 -0
  143. data/spec/dummy/script/rails +0 -9
  144. data/spec/factories.rb +0 -30
  145. data/spec/generators/application_owner_generator_spec.rb +0 -28
  146. data/spec/generators/confidential_applications_generator_spec.rb +0 -29
  147. data/spec/generators/install_generator_spec.rb +0 -36
  148. data/spec/generators/migration_generator_spec.rb +0 -28
  149. data/spec/generators/pkce_generator_spec.rb +0 -28
  150. data/spec/generators/previous_refresh_token_generator_spec.rb +0 -44
  151. data/spec/generators/templates/routes.rb +0 -4
  152. data/spec/generators/views_generator_spec.rb +0 -29
  153. data/spec/grape/grape_integration_spec.rb +0 -137
  154. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +0 -26
  155. data/spec/lib/config_spec.rb +0 -809
  156. data/spec/lib/doorkeeper_spec.rb +0 -27
  157. data/spec/lib/models/expirable_spec.rb +0 -61
  158. data/spec/lib/models/reusable_spec.rb +0 -40
  159. data/spec/lib/models/revocable_spec.rb +0 -59
  160. data/spec/lib/models/scopes_spec.rb +0 -53
  161. data/spec/lib/models/secret_storable_spec.rb +0 -135
  162. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -39
  163. data/spec/lib/oauth/authorization_code_request_spec.rb +0 -170
  164. data/spec/lib/oauth/base_request_spec.rb +0 -224
  165. data/spec/lib/oauth/base_response_spec.rb +0 -45
  166. data/spec/lib/oauth/client/credentials_spec.rb +0 -90
  167. data/spec/lib/oauth/client_credentials/creator_spec.rb +0 -134
  168. data/spec/lib/oauth/client_credentials/issuer_spec.rb +0 -112
  169. data/spec/lib/oauth/client_credentials/validation_spec.rb +0 -59
  170. data/spec/lib/oauth/client_credentials_integration_spec.rb +0 -27
  171. data/spec/lib/oauth/client_credentials_request_spec.rb +0 -107
  172. data/spec/lib/oauth/client_spec.rb +0 -38
  173. data/spec/lib/oauth/code_request_spec.rb +0 -46
  174. data/spec/lib/oauth/code_response_spec.rb +0 -32
  175. data/spec/lib/oauth/error_response_spec.rb +0 -64
  176. data/spec/lib/oauth/error_spec.rb +0 -21
  177. data/spec/lib/oauth/forbidden_token_response_spec.rb +0 -20
  178. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -110
  179. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -21
  180. data/spec/lib/oauth/helpers/uri_checker_spec.rb +0 -262
  181. data/spec/lib/oauth/invalid_request_response_spec.rb +0 -73
  182. data/spec/lib/oauth/invalid_token_response_spec.rb +0 -53
  183. data/spec/lib/oauth/password_access_token_request_spec.rb +0 -190
  184. data/spec/lib/oauth/pre_authorization_spec.rb +0 -223
  185. data/spec/lib/oauth/refresh_token_request_spec.rb +0 -177
  186. data/spec/lib/oauth/scopes_spec.rb +0 -146
  187. data/spec/lib/oauth/token_request_spec.rb +0 -157
  188. data/spec/lib/oauth/token_response_spec.rb +0 -84
  189. data/spec/lib/oauth/token_spec.rb +0 -156
  190. data/spec/lib/request/strategy_spec.rb +0 -54
  191. data/spec/lib/secret_storing/base_spec.rb +0 -60
  192. data/spec/lib/secret_storing/bcrypt_spec.rb +0 -49
  193. data/spec/lib/secret_storing/plain_spec.rb +0 -44
  194. data/spec/lib/secret_storing/sha256_hash_spec.rb +0 -48
  195. data/spec/lib/server_spec.rb +0 -49
  196. data/spec/lib/stale_records_cleaner_spec.rb +0 -89
  197. data/spec/models/doorkeeper/access_grant_spec.rb +0 -161
  198. data/spec/models/doorkeeper/access_token_spec.rb +0 -622
  199. data/spec/models/doorkeeper/application_spec.rb +0 -482
  200. data/spec/requests/applications/applications_request_spec.rb +0 -259
  201. data/spec/requests/applications/authorized_applications_spec.rb +0 -32
  202. data/spec/requests/endpoints/authorization_spec.rb +0 -91
  203. data/spec/requests/endpoints/token_spec.rb +0 -75
  204. data/spec/requests/flows/authorization_code_errors_spec.rb +0 -79
  205. data/spec/requests/flows/authorization_code_spec.rb +0 -525
  206. data/spec/requests/flows/client_credentials_spec.rb +0 -166
  207. data/spec/requests/flows/implicit_grant_errors_spec.rb +0 -46
  208. data/spec/requests/flows/implicit_grant_spec.rb +0 -91
  209. data/spec/requests/flows/password_spec.rb +0 -316
  210. data/spec/requests/flows/refresh_token_spec.rb +0 -233
  211. data/spec/requests/flows/revoke_token_spec.rb +0 -157
  212. data/spec/requests/flows/skip_authorization_spec.rb +0 -66
  213. data/spec/requests/protected_resources/metal_spec.rb +0 -16
  214. data/spec/requests/protected_resources/private_api_spec.rb +0 -83
  215. data/spec/routing/custom_controller_routes_spec.rb +0 -133
  216. data/spec/routing/default_routes_spec.rb +0 -41
  217. data/spec/routing/scoped_routes_spec.rb +0 -47
  218. data/spec/spec_helper.rb +0 -54
  219. data/spec/spec_helper_integration.rb +0 -4
  220. data/spec/support/dependencies/factory_bot.rb +0 -4
  221. data/spec/support/doorkeeper_rspec.rb +0 -22
  222. data/spec/support/helpers/access_token_request_helper.rb +0 -13
  223. data/spec/support/helpers/authorization_request_helper.rb +0 -43
  224. data/spec/support/helpers/config_helper.rb +0 -11
  225. data/spec/support/helpers/model_helper.rb +0 -78
  226. data/spec/support/helpers/request_spec_helper.rb +0 -110
  227. data/spec/support/helpers/url_helper.rb +0 -62
  228. data/spec/support/orm/active_record.rb +0 -5
  229. data/spec/support/shared/controllers_shared_context.rb +0 -133
  230. data/spec/support/shared/hashing_shared_context.rb +0 -36
  231. data/spec/support/shared/models_shared_examples.rb +0 -54
  232. data/spec/validators/redirect_uri_validator_spec.rb +0 -183
  233. data/spec/version/version_spec.rb +0 -17
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ class Config
5
+ # Abstract base class for Doorkeeper and it's extensions configuration
6
+ # builder. Instantiates and validates gem configuration.
7
+ #
8
+ class AbstractBuilder
9
+ attr_reader :config
10
+
11
+ # @param [Class] config class
12
+ #
13
+ def initialize(config = Config.new, &block)
14
+ @config = config
15
+ instance_eval(&block)
16
+ end
17
+
18
+ # Builds and validates configuration.
19
+ #
20
+ # @return [Doorkeeper::Config] config instance
21
+ #
22
+ def build
23
+ @config.validate! if @config.respond_to?(:validate!)
24
+ @config
25
+ end
26
+ end
27
+ end
28
+ end
@@ -36,22 +36,27 @@ module Doorkeeper
36
36
  attribute = options[:as] || name
37
37
  attribute_builder = options[:builder_class]
38
38
 
39
- Builder.instance_eval do
40
- remove_method name if method_defined?(name)
41
- if options[:deprecated]
42
- define_method name do |*_, &_|
43
- Kernel.warn "[DOORKEEPER] #{name} has been deprecated and will soon be removed"
44
- end
45
- else
46
- define_method name do |*args, &block|
47
- value = if attribute_builder
48
- attribute_builder.new(&block).build
49
- else
50
- block || args.first
51
- end
39
+ builder_class.instance_eval do
40
+ if method_defined?(name)
41
+ Kernel.warn "[DOORKEEPER] Option #{name} already defined and will be overridden"
42
+ remove_method name
43
+ end
52
44
 
53
- @config.instance_variable_set(:"@#{attribute}", value)
45
+ define_method name do |*args, &block|
46
+ if (deprecation_opts = options[:deprecated])
47
+ warning = "[DOORKEEPER] #{name} has been deprecated and will soon be removed"
48
+ warning = "#{warning}\n#{deprecation_opts.fetch(:message)}" if deprecation_opts.is_a?(Hash)
49
+
50
+ Kernel.warn(warning)
54
51
  end
52
+
53
+ value = if attribute_builder
54
+ attribute_builder.new(&block).build
55
+ else
56
+ block || args.first
57
+ end
58
+
59
+ @config.instance_variable_set(:"@#{attribute}", value)
55
60
  end
56
61
  end
57
62
 
@@ -65,6 +70,13 @@ module Doorkeeper
65
70
 
66
71
  public attribute
67
72
  end
73
+
74
+ def self.extended(base)
75
+ return if base.respond_to?(:builder_class)
76
+
77
+ raise Doorkeeper::MissingConfigurationBuilderClass, "Define `self.builder_class` method " \
78
+ "for #{base} that returns your custom Builder class to use options DSL!"
79
+ end
68
80
  end
69
81
  end
70
82
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ class Config
5
+ # Doorkeeper configuration validator.
6
+ #
7
+ module Validations
8
+ # Validates configuration options to be set properly.
9
+ #
10
+ def validate!
11
+ validate_reuse_access_token_value
12
+ validate_token_reuse_limit
13
+ validate_secret_strategies
14
+ end
15
+
16
+ private
17
+
18
+ # Determine whether +reuse_access_token+ and a non-restorable
19
+ # +token_secret_strategy+ have both been activated.
20
+ #
21
+ # In that case, disable reuse_access_token value and warn the user.
22
+ def validate_reuse_access_token_value
23
+ strategy = token_secret_strategy
24
+ return if !reuse_access_token || strategy.allows_restoring_secrets?
25
+
26
+ ::Rails.logger.warn(
27
+ "You have configured both reuse_access_token " \
28
+ "AND strategy strategy '#{strategy}' that cannot restore tokens. " \
29
+ "This combination is unsupported. reuse_access_token will be disabled",
30
+ )
31
+ @reuse_access_token = false
32
+ end
33
+
34
+ # Validate that the provided strategies are valid for
35
+ # tokens and applications
36
+ def validate_secret_strategies
37
+ token_secret_strategy.validate_for(:token)
38
+ application_secret_strategy.validate_for(:application)
39
+ end
40
+
41
+ def validate_token_reuse_limit
42
+ return if !reuse_access_token ||
43
+ (token_reuse_limit > 0 && token_reuse_limit <= 100)
44
+
45
+ ::Rails.logger.warn(
46
+ "You have configured an invalid value for token_reuse_limit option. " \
47
+ "It will be set to default 100",
48
+ )
49
+ @token_reuse_limit = 100
50
+ end
51
+ end
52
+ end
53
+ end
@@ -4,7 +4,7 @@ module Doorkeeper
4
4
  class Engine < Rails::Engine
5
5
  initializer "doorkeeper.params.filter" do |app|
6
6
  parameters = %w[client_secret code authentication_token access_token refresh_token]
7
- app.config.filter_parameters << /^(#{Regexp.union parameters})$/
7
+ app.config.filter_parameters << /^(#{Regexp.union(parameters)})$/
8
8
  end
9
9
 
10
10
  initializer "doorkeeper.routes" do
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "doorkeeper/grant_flow/flow"
4
+ require "doorkeeper/grant_flow/fallback_flow"
5
+ require "doorkeeper/grant_flow/registry"
6
+
7
+ module Doorkeeper
8
+ module GrantFlow
9
+ extend Registry
10
+
11
+ register(
12
+ :implicit,
13
+ response_type_matches: "token",
14
+ response_mode_matches: %w[fragment form_post],
15
+ response_type_strategy: Doorkeeper::Request::Token,
16
+ )
17
+
18
+ register(
19
+ :authorization_code,
20
+ response_type_matches: "code",
21
+ response_mode_matches: %w[query fragment form_post],
22
+ response_type_strategy: Doorkeeper::Request::Code,
23
+ grant_type_matches: "authorization_code",
24
+ grant_type_strategy: Doorkeeper::Request::AuthorizationCode,
25
+ )
26
+
27
+ register(
28
+ :client_credentials,
29
+ grant_type_matches: "client_credentials",
30
+ grant_type_strategy: Doorkeeper::Request::ClientCredentials,
31
+ )
32
+
33
+ register(
34
+ :password,
35
+ grant_type_matches: "password",
36
+ grant_type_strategy: Doorkeeper::Request::Password,
37
+ )
38
+
39
+ register(
40
+ :refresh_token,
41
+ grant_type_matches: "refresh_token",
42
+ grant_type_strategy: Doorkeeper::Request::RefreshToken,
43
+ )
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module GrantFlow
5
+ class FallbackFlow < Flow
6
+ def handles_grant_type?
7
+ false
8
+ end
9
+
10
+ def handles_response_type?
11
+ false
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module GrantFlow
5
+ class Flow
6
+ attr_reader :name, :grant_type_matches, :grant_type_strategy,
7
+ :response_type_matches, :response_type_strategy,
8
+ :response_mode_matches
9
+
10
+ def initialize(name, **options)
11
+ @name = name
12
+ @grant_type_matches = options[:grant_type_matches]
13
+ @grant_type_strategy = options[:grant_type_strategy]
14
+ @response_type_matches = options[:response_type_matches]
15
+ @response_type_strategy = options[:response_type_strategy]
16
+ @response_mode_matches = options[:response_mode_matches]
17
+ end
18
+
19
+ def handles_grant_type?
20
+ grant_type_matches.present?
21
+ end
22
+
23
+ def handles_response_type?
24
+ response_type_matches.present?
25
+ end
26
+
27
+ def matches_grant_type?(value)
28
+ grant_type_matches === value
29
+ end
30
+
31
+ def matches_response_type?(value)
32
+ response_type_matches === value
33
+ end
34
+
35
+ def default_response_mode
36
+ response_mode_matches[0]
37
+ end
38
+
39
+ def matches_response_mode?(value)
40
+ response_mode_matches.include?(value)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module GrantFlow
5
+ module Registry
6
+ mattr_accessor :flows
7
+ self.flows = {}
8
+
9
+ mattr_accessor :aliases
10
+ self.aliases = {}
11
+
12
+ # Allows to register custom OAuth grant flow so that Doorkeeper
13
+ # could recognize and process it.
14
+ #
15
+ def register(name_or_flow, **options)
16
+ unless name_or_flow.is_a?(Doorkeeper::GrantFlow::Flow)
17
+ name_or_flow = Flow.new(name_or_flow, **options)
18
+ end
19
+
20
+ flow_key = name_or_flow.name.to_sym
21
+
22
+ if flows.key?(flow_key)
23
+ ::Kernel.warn <<~WARNING
24
+ [DOORKEEPER] '#{flow_key}' grant flow already registered and will be overridden
25
+ in #{caller(1..1).first}
26
+ WARNING
27
+ end
28
+
29
+ flows[flow_key] = name_or_flow
30
+ end
31
+
32
+ # Allows to register aliases that could be used in `grant_flows`
33
+ # configuration option. It is possible to have aliases like 1:1 or
34
+ # 1:N, i.e. "implicit_oidc" => ['token', 'id_token', 'id_token token'].
35
+ #
36
+ def register_alias(alias_name, **options)
37
+ aliases[alias_name.to_sym] = Array.wrap(options.fetch(:as))
38
+ end
39
+
40
+ def expand_alias(alias_name)
41
+ aliases.fetch(alias_name.to_sym, [])
42
+ end
43
+
44
+ # [NOTE]: make it to use #fetch after removing fallbacks
45
+ def get(name)
46
+ flows[name.to_sym]
47
+ end
48
+ end
49
+ end
50
+ end
@@ -18,7 +18,7 @@ module Doorkeeper
18
18
 
19
19
  scopes = if endpoint_scopes
20
20
  Doorkeeper::OAuth::Scopes.from_array(endpoint_scopes)
21
- elsif scopes && !scopes.empty?
21
+ elsif scopes.present?
22
22
  Doorkeeper::OAuth::Scopes.from_array(scopes)
23
23
  end
24
24
 
@@ -16,6 +16,8 @@ module Doorkeeper
16
16
 
17
17
  # :doc:
18
18
  def current_resource_owner
19
+ return @current_resource_owner if defined?(@current_resource_owner)
20
+
19
21
  @current_resource_owner ||= begin
20
22
  instance_eval(&Doorkeeper.config.authenticate_resource_owner)
21
23
  end
@@ -36,7 +38,9 @@ module Doorkeeper
36
38
 
37
39
  # :doc:
38
40
  def doorkeeper_token
39
- @doorkeeper_token ||= OAuth::Token.authenticate request, *config_methods
41
+ return @doorkeeper_token if defined?(@doorkeeper_token)
42
+
43
+ @doorkeeper_token ||= OAuth::Token.authenticate(request, *config_methods)
40
44
  end
41
45
 
42
46
  def config_methods
@@ -58,10 +62,10 @@ module Doorkeeper
58
62
  end
59
63
 
60
64
  def handle_token_exception(exception)
61
- error = get_error_response_from_exception exception
62
- headers.merge! error.headers
65
+ error = get_error_response_from_exception(exception)
66
+ headers.merge!(error.headers)
63
67
  self.response_body = error.body.to_json
64
- self.status = error.status
68
+ self.status = error.status
65
69
  end
66
70
 
67
71
  def skip_authorization?
@@ -11,14 +11,11 @@ module Doorkeeper
11
11
  include Models::Orderable
12
12
  include Models::SecretStorable
13
13
  include Models::Scopes
14
+ include Models::ResourceOwnerable
14
15
 
15
- # never uses pkce, if pkce migrations were not generated
16
+ # Never uses PKCE if PKCE migrations were not generated
16
17
  def uses_pkce?
17
- pkce_supported? && code_challenge.present?
18
- end
19
-
20
- def pkce_supported?
21
- respond_to? :code_challenge
18
+ self.class.pkce_supported? && code_challenge.present?
22
19
  end
23
20
 
24
21
  module ClassMethods
@@ -27,8 +24,8 @@ module Doorkeeper
27
24
  #
28
25
  # @param token [#to_s] token value (any object that responds to `#to_s`)
29
26
  #
30
- # @return [Doorkeeper::AccessGrant, nil] AccessGrant object or nil
31
- # if there is no record with such token
27
+ # @return [Doorkeeper::AccessGrant, nil]
28
+ # AccessGrant object or nil if there is no record with such token
32
29
  #
33
30
  def by_token(token)
34
31
  find_by_plaintext_token(:token, token)
@@ -39,15 +36,16 @@ module Doorkeeper
39
36
  #
40
37
  # @param application_id [Integer]
41
38
  # ID of the Application
42
- # @param resource_owner [ActiveRecord::Base]
43
- # instance of the Resource Owner model
39
+ # @param resource_owner [ActiveRecord::Base, Integer]
40
+ # instance of the Resource Owner model or it's ID
44
41
  #
45
42
  def revoke_all_for(application_id, resource_owner, clock = Time)
46
- where(
47
- application_id: application_id,
48
- resource_owner_id: resource_owner.id,
49
- revoked_at: nil,
50
- ).update_all(revoked_at: clock.now.utc)
43
+ by_resource_owner(resource_owner)
44
+ .where(
45
+ application_id: application_id,
46
+ revoked_at: nil,
47
+ )
48
+ .update_all(revoked_at: clock.now.utc)
51
49
  end
52
50
 
53
51
  # Implements PKCE code_challenge encoding without base64 padding as described in the spec.
@@ -91,17 +89,19 @@ module Doorkeeper
91
89
  # suitable for PKCE validation
92
90
  #
93
91
  def generate_code_challenge(code_verifier)
94
- padded_result = Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier))
95
- padded_result.split("=")[0] # Remove any trailing '='
92
+ Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier), padding: false)
96
93
  end
97
94
 
98
95
  def pkce_supported?
99
- new.pkce_supported?
96
+ column_names.include?("code_challenge")
100
97
  end
101
98
 
102
99
  ##
103
100
  # Determines the secret storing transformer
104
101
  # Unless configured otherwise, uses the plain secret strategy
102
+ #
103
+ # @return [Doorkeeper::SecretStoring::Base]
104
+ #
105
105
  def secret_strategy
106
106
  ::Doorkeeper.config.token_secret_strategy
107
107
  end
@@ -109,6 +109,9 @@ module Doorkeeper
109
109
  ##
110
110
  # Determine the fallback storing strategy
111
111
  # Unless configured, there will be no fallback
112
+ #
113
+ # @return [Doorkeeper::SecretStoring::Base]
114
+ #
112
115
  def fallback_secret_strategy
113
116
  ::Doorkeeper.config.token_secret_fallback_strategy
114
117
  end
@@ -12,6 +12,7 @@ module Doorkeeper
12
12
  include Models::Orderable
13
13
  include Models::SecretStorable
14
14
  include Models::Scopes
15
+ include Models::ResourceOwnerable
15
16
 
16
17
  module ClassMethods
17
18
  # Returns an instance of the Doorkeeper::AccessToken with
@@ -60,15 +61,16 @@ module Doorkeeper
60
61
  #
61
62
  # @param application_id [Integer]
62
63
  # ID of the Application
63
- # @param resource_owner [ActiveRecord::Base]
64
- # instance of the Resource Owner model
64
+ # @param resource_owner [ActiveRecord::Base, Integer]
65
+ # instance of the Resource Owner model or it's ID
65
66
  #
66
67
  def revoke_all_for(application_id, resource_owner, clock = Time)
67
- where(
68
- application_id: application_id,
69
- resource_owner_id: resource_owner.id,
70
- revoked_at: nil,
71
- ).update_all(revoked_at: clock.now.utc)
68
+ by_resource_owner(resource_owner)
69
+ .where(
70
+ application_id: application_id,
71
+ revoked_at: nil,
72
+ )
73
+ .update_all(revoked_at: clock.now.utc)
72
74
  end
73
75
 
74
76
  # Looking for not revoked Access Token with a matching set of scopes
@@ -76,7 +78,7 @@ module Doorkeeper
76
78
  #
77
79
  # @param application [Doorkeeper::Application]
78
80
  # Application instance
79
- # @param resource_owner_or_id [ActiveRecord::Base, Integer]
81
+ # @param resource_owner [ActiveRecord::Base, Integer]
80
82
  # Resource Owner model instance or it's ID
81
83
  # @param scopes [String, Doorkeeper::OAuth::Scopes]
82
84
  # set of scopes
@@ -84,22 +86,16 @@ module Doorkeeper
84
86
  # @return [Doorkeeper::AccessToken, nil] Access Token instance or
85
87
  # nil if matching record was not found
86
88
  #
87
- def matching_token_for(application, resource_owner_or_id, scopes)
88
- resource_owner_id = if resource_owner_or_id.respond_to?(:to_key)
89
- resource_owner_or_id.id
90
- else
91
- resource_owner_or_id
92
- end
93
-
94
- tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
89
+ def matching_token_for(application, resource_owner, scopes)
90
+ tokens = authorized_tokens_for(application&.id, resource_owner)
95
91
  find_matching_token(tokens, application, scopes)
96
92
  end
97
93
 
98
94
  # Interface to enumerate access token records in batches in order not
99
95
  # to bloat the memory. Could be overloaded in any ORM extension.
100
96
  #
101
- def find_access_token_in_batches(relation, *args, &block)
102
- relation.find_in_batches(*args, &block)
97
+ def find_access_token_in_batches(relation, **args, &block)
98
+ relation.find_in_batches(**args, &block)
103
99
  end
104
100
 
105
101
  # Enumerates AccessToken records in batches to find a matching token.
@@ -130,7 +126,7 @@ module Doorkeeper
130
126
 
131
127
  find_access_token_in_batches(relation, batch_size: batch_size) do |batch|
132
128
  tokens = batch.select do |token|
133
- scopes_match?(token.scopes, scopes, application.try(:scopes))
129
+ scopes_match?(token.scopes, scopes, application&.scopes)
134
130
  end
135
131
 
136
132
  matching_tokens.concat(tokens)
@@ -170,47 +166,79 @@ module Doorkeeper
170
166
  #
171
167
  # @param application [Doorkeeper::Application]
172
168
  # Application instance
173
- # @param resource_owner_id [ActiveRecord::Base, Integer]
169
+ # @param resource_owner [ActiveRecord::Base, Integer]
174
170
  # Resource Owner model instance or it's ID
175
171
  # @param scopes [#to_s]
176
172
  # set of scopes (any object that responds to `#to_s`)
177
- # @param expires_in [Integer]
173
+ # @param token_attributes [Hash]
174
+ # Additional attributes to use when creating a token
175
+ # @option token_attributes [Integer] :expires_in
178
176
  # token lifetime in seconds
179
- # @param use_refresh_token [Boolean]
177
+ # @option token_attributes [Boolean] :use_refresh_token
180
178
  # whether to use the refresh token
181
179
  #
182
180
  # @return [Doorkeeper::AccessToken] existing record or a new one
183
181
  #
184
- def find_or_create_for(application, resource_owner_id, scopes, expires_in, use_refresh_token)
182
+ def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes)
185
183
  if Doorkeeper.config.reuse_access_token
186
- access_token = matching_token_for(application, resource_owner_id, scopes)
184
+ access_token = matching_token_for(application, resource_owner, scopes)
187
185
 
188
186
  return access_token if access_token&.reusable?
189
187
  end
190
188
 
191
- create!(
192
- application_id: application.try(:id),
193
- resource_owner_id: resource_owner_id,
194
- scopes: scopes.to_s,
195
- expires_in: expires_in,
196
- use_refresh_token: use_refresh_token,
189
+ create_for(
190
+ application: application,
191
+ resource_owner: resource_owner,
192
+ scopes: scopes,
193
+ **token_attributes,
197
194
  )
198
195
  end
199
196
 
197
+ # Creates a not expired AccessToken record with a matching set of
198
+ # scopes that belongs to specific Application and Resource Owner.
199
+ #
200
+ # @param application [Doorkeeper::Application]
201
+ # Application instance
202
+ # @param resource_owner [ActiveRecord::Base, Integer]
203
+ # Resource Owner model instance or it's ID
204
+ # @param scopes [#to_s]
205
+ # set of scopes (any object that responds to `#to_s`)
206
+ # @param token_attributes [Hash]
207
+ # Additional attributes to use when creating a token
208
+ # @option token_attributes [Integer] :expires_in
209
+ # token lifetime in seconds
210
+ # @option token_attributes [Boolean] :use_refresh_token
211
+ # whether to use the refresh token
212
+ #
213
+ # @return [Doorkeeper::AccessToken] new access token
214
+ #
215
+ def create_for(application:, resource_owner:, scopes:, **token_attributes)
216
+ token_attributes[:application_id] = application&.id
217
+ token_attributes[:scopes] = scopes.to_s
218
+
219
+ if Doorkeeper.config.polymorphic_resource_owner?
220
+ token_attributes[:resource_owner] = resource_owner
221
+ else
222
+ token_attributes[:resource_owner_id] = resource_owner_id_for(resource_owner)
223
+ end
224
+
225
+ create!(token_attributes)
226
+ end
227
+
200
228
  # Looking for not revoked Access Token records that belongs to specific
201
229
  # Application and Resource Owner.
202
230
  #
203
231
  # @param application_id [Integer]
204
232
  # ID of the Application model instance
205
- # @param resource_owner_id [Integer]
206
- # ID of the Resource Owner model instance
233
+ # @param resource_owner [ActiveRecord::Base, Integer]
234
+ # Resource Owner model instance or it's ID
207
235
  #
208
- # @return [Doorkeeper::AccessToken] array of matching AccessToken objects
236
+ # @return [ActiveRecord::Relation]
237
+ # collection of matching AccessToken objects
209
238
  #
210
- def authorized_tokens_for(application_id, resource_owner_id)
211
- where(
239
+ def authorized_tokens_for(application_id, resource_owner)
240
+ by_resource_owner(resource_owner).where(
212
241
  application_id: application_id,
213
- resource_owner_id: resource_owner_id,
214
242
  revoked_at: nil,
215
243
  )
216
244
  end
@@ -220,20 +248,24 @@ module Doorkeeper
220
248
  #
221
249
  # @param application_id [Integer]
222
250
  # ID of the Application model instance
223
- # @param resource_owner_id [Integer]
251
+ # @param resource_owner [ActiveRecord::Base, Integer]
224
252
  # ID of the Resource Owner model instance
225
253
  #
226
254
  # @return [Doorkeeper::AccessToken, nil] matching AccessToken object or
227
255
  # nil if nothing was found
228
256
  #
229
- def last_authorized_token_for(application_id, resource_owner_id)
230
- authorized_tokens_for(application_id, resource_owner_id)
231
- .ordered_by(:created_at, :desc).first
257
+ def last_authorized_token_for(application_id, resource_owner)
258
+ authorized_tokens_for(application_id, resource_owner)
259
+ .ordered_by(:created_at, :desc)
260
+ .first
232
261
  end
233
262
 
234
263
  ##
235
264
  # Determines the secret storing transformer
236
265
  # Unless configured otherwise, uses the plain secret strategy
266
+ #
267
+ # @return [Doorkeeper::SecretStoring::Base]
268
+ #
237
269
  def secret_strategy
238
270
  ::Doorkeeper.config.token_secret_strategy
239
271
  end
@@ -269,7 +301,11 @@ module Doorkeeper
269
301
  expires_in: expires_in_seconds,
270
302
  application: { uid: application.try(:uid) },
271
303
  created_at: created_at.to_i,
272
- }
304
+ }.tap do |json|
305
+ if Doorkeeper.configuration.polymorphic_resource_owner?
306
+ json[:resource_owner_type] = resource_owner_type
307
+ end
308
+ end
273
309
  end
274
310
 
275
311
  # Indicates whether the token instance have the same credential
@@ -281,7 +317,22 @@ module Doorkeeper
281
317
  #
282
318
  def same_credential?(access_token)
283
319
  application_id == access_token.application_id &&
320
+ same_resource_owner?(access_token)
321
+ end
322
+
323
+ # Indicates whether the token instance have the same credential
324
+ # as the other Access Token.
325
+ #
326
+ # @param access_token [Doorkeeper::AccessToken] other token
327
+ #
328
+ # @return [Boolean] true if credentials are same of false in other cases
329
+ #
330
+ def same_resource_owner?(access_token)
331
+ if Doorkeeper.configuration.polymorphic_resource_owner?
332
+ resource_owner == access_token.resource_owner
333
+ else
284
334
  resource_owner_id == access_token.resource_owner_id
335
+ end
285
336
  end
286
337
 
287
338
  # Indicates if token is acceptable for specific scopes.
@@ -326,7 +377,7 @@ module Doorkeeper
326
377
  return unless self.class.refresh_token_revoked_on_use?
327
378
 
328
379
  old_refresh_token&.revoke
329
- update_attribute :previous_refresh_token, ""
380
+ update_attribute(:previous_refresh_token, "") if previous_refresh_token.present?
330
381
  end
331
382
 
332
383
  private
@@ -363,16 +414,28 @@ module Doorkeeper
363
414
  def generate_token
364
415
  self.created_at ||= Time.now.utc
365
416
 
366
- @raw_token = token_generator.generate(
417
+ @raw_token = token_generator.generate(attributes_for_token_generator)
418
+ secret_strategy.store_secret(self, :token, @raw_token)
419
+ @raw_token
420
+ end
421
+
422
+ # Set of attributes that would be passed to token generator to
423
+ # generate unique token based on them.
424
+ #
425
+ # @return [Hash] set of attributes
426
+ #
427
+ def attributes_for_token_generator
428
+ {
367
429
  resource_owner_id: resource_owner_id,
368
430
  scopes: scopes,
369
431
  application: application,
370
432
  expires_in: expires_in,
371
433
  created_at: created_at,
372
- )
373
-
374
- secret_strategy.store_secret(self, :token, @raw_token)
375
- @raw_token
434
+ }.tap do |attributes|
435
+ if Doorkeeper.config.polymorphic_resource_owner?
436
+ attributes[:resource_owner] = resource_owner
437
+ end
438
+ end
376
439
  end
377
440
 
378
441
  def token_generator