doorkeeper 4.2.6 → 5.5.4

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 (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1049 -0
  3. data/README.md +110 -353
  4. data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
  5. data/app/controllers/doorkeeper/application_controller.rb +6 -7
  6. data/app/controllers/doorkeeper/application_metal_controller.rb +7 -11
  7. data/app/controllers/doorkeeper/applications_controller.rb +65 -16
  8. data/app/controllers/doorkeeper/authorizations_controller.rb +97 -17
  9. data/app/controllers/doorkeeper/authorized_applications_controller.rb +22 -3
  10. data/app/controllers/doorkeeper/token_info_controller.rb +16 -4
  11. data/app/controllers/doorkeeper/tokens_controller.rb +115 -38
  12. data/app/helpers/doorkeeper/dashboard_helper.rb +10 -6
  13. data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
  14. data/app/views/doorkeeper/applications/_form.html.erb +33 -21
  15. data/app/views/doorkeeper/applications/edit.html.erb +1 -1
  16. data/app/views/doorkeeper/applications/index.html.erb +18 -6
  17. data/app/views/doorkeeper/applications/new.html.erb +1 -1
  18. data/app/views/doorkeeper/applications/show.html.erb +40 -16
  19. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  20. data/app/views/doorkeeper/authorizations/form_post.html.erb +15 -0
  21. data/app/views/doorkeeper/authorizations/new.html.erb +6 -0
  22. data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
  23. data/app/views/layouts/doorkeeper/admin.html.erb +16 -14
  24. data/config/locales/en.yml +34 -7
  25. data/lib/doorkeeper/config/abstract_builder.rb +28 -0
  26. data/lib/doorkeeper/config/option.rb +82 -0
  27. data/lib/doorkeeper/config/validations.rb +53 -0
  28. data/lib/doorkeeper/config.rb +514 -167
  29. data/lib/doorkeeper/engine.rb +11 -5
  30. data/lib/doorkeeper/errors.rb +25 -16
  31. data/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
  32. data/lib/doorkeeper/grant_flow/flow.rb +44 -0
  33. data/lib/doorkeeper/grant_flow/registry.rb +50 -0
  34. data/lib/doorkeeper/grant_flow.rb +45 -0
  35. data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
  36. data/lib/doorkeeper/grape/helpers.rb +23 -12
  37. data/lib/doorkeeper/helpers/controller.rb +51 -14
  38. data/lib/doorkeeper/models/access_grant_mixin.rb +94 -27
  39. data/lib/doorkeeper/models/access_token_mixin.rb +284 -96
  40. data/lib/doorkeeper/models/application_mixin.rb +58 -27
  41. data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
  42. data/lib/doorkeeper/models/concerns/expirable.rb +12 -6
  43. data/lib/doorkeeper/models/concerns/orderable.rb +15 -0
  44. data/lib/doorkeeper/models/concerns/ownership.rb +4 -7
  45. data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
  46. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  47. data/lib/doorkeeper/models/concerns/revocable.rb +3 -27
  48. data/lib/doorkeeper/models/concerns/scopes.rb +12 -2
  49. data/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
  50. data/lib/doorkeeper/oauth/authorization/code.rb +48 -12
  51. data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
  52. data/lib/doorkeeper/oauth/authorization/token.rb +66 -28
  53. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +7 -5
  54. data/lib/doorkeeper/oauth/authorization_code_request.rb +63 -10
  55. data/lib/doorkeeper/oauth/base_request.rb +35 -19
  56. data/lib/doorkeeper/oauth/base_response.rb +2 -0
  57. data/lib/doorkeeper/oauth/client/credentials.rb +9 -7
  58. data/lib/doorkeeper/oauth/client.rb +10 -11
  59. data/lib/doorkeeper/oauth/client_credentials/creator.rb +47 -4
  60. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +16 -9
  61. data/lib/doorkeeper/oauth/client_credentials/validator.rb +56 -0
  62. data/lib/doorkeeper/oauth/client_credentials_request.rb +10 -11
  63. data/lib/doorkeeper/oauth/code_request.rb +8 -12
  64. data/lib/doorkeeper/oauth/code_response.rb +27 -15
  65. data/lib/doorkeeper/oauth/error.rb +5 -3
  66. data/lib/doorkeeper/oauth/error_response.rb +35 -15
  67. data/lib/doorkeeper/oauth/forbidden_token_response.rb +11 -3
  68. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -18
  69. data/lib/doorkeeper/oauth/helpers/unique_token.rb +20 -3
  70. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +53 -3
  71. data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
  72. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  73. data/lib/doorkeeper/oauth/invalid_token_response.rb +29 -5
  74. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  75. data/lib/doorkeeper/oauth/password_access_token_request.rb +44 -10
  76. data/lib/doorkeeper/oauth/pre_authorization.rb +135 -26
  77. data/lib/doorkeeper/oauth/refresh_token_request.rb +60 -31
  78. data/lib/doorkeeper/oauth/scopes.rb +26 -12
  79. data/lib/doorkeeper/oauth/token.rb +13 -9
  80. data/lib/doorkeeper/oauth/token_introspection.rb +202 -0
  81. data/lib/doorkeeper/oauth/token_request.rb +8 -20
  82. data/lib/doorkeeper/oauth/token_response.rb +14 -10
  83. data/lib/doorkeeper/oauth.rb +13 -0
  84. data/lib/doorkeeper/orm/active_record/access_grant.rb +6 -4
  85. data/lib/doorkeeper/orm/active_record/access_token.rb +5 -42
  86. data/lib/doorkeeper/orm/active_record/application.rb +6 -20
  87. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +69 -0
  88. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +60 -0
  89. data/lib/doorkeeper/orm/active_record/mixins/application.rb +199 -0
  90. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +66 -0
  91. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +33 -0
  92. data/lib/doorkeeper/orm/active_record.rb +37 -8
  93. data/lib/doorkeeper/rails/helpers.rb +14 -13
  94. data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
  95. data/lib/doorkeeper/rails/routes/mapper.rb +4 -2
  96. data/lib/doorkeeper/rails/routes/mapping.rb +9 -7
  97. data/lib/doorkeeper/rails/routes/registry.rb +45 -0
  98. data/lib/doorkeeper/rails/routes.rb +41 -28
  99. data/lib/doorkeeper/rake/db.rake +40 -0
  100. data/lib/doorkeeper/rake/setup.rake +11 -0
  101. data/lib/doorkeeper/rake.rb +14 -0
  102. data/lib/doorkeeper/request/authorization_code.rb +6 -4
  103. data/lib/doorkeeper/request/client_credentials.rb +3 -3
  104. data/lib/doorkeeper/request/code.rb +1 -1
  105. data/lib/doorkeeper/request/password.rb +5 -14
  106. data/lib/doorkeeper/request/refresh_token.rb +6 -5
  107. data/lib/doorkeeper/request/strategy.rb +4 -2
  108. data/lib/doorkeeper/request/token.rb +1 -1
  109. data/lib/doorkeeper/request.rb +62 -29
  110. data/lib/doorkeeper/secret_storing/base.rb +64 -0
  111. data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  112. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  113. data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  114. data/lib/doorkeeper/server.rb +9 -11
  115. data/lib/doorkeeper/stale_records_cleaner.rb +24 -0
  116. data/lib/doorkeeper/validations.rb +5 -2
  117. data/lib/doorkeeper/version.rb +12 -1
  118. data/lib/doorkeeper.rb +111 -62
  119. data/lib/generators/doorkeeper/application_owner_generator.rb +28 -13
  120. data/lib/generators/doorkeeper/confidential_applications_generator.rb +33 -0
  121. data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
  122. data/lib/generators/doorkeeper/install_generator.rb +19 -9
  123. data/lib/generators/doorkeeper/migration_generator.rb +27 -10
  124. data/lib/generators/doorkeeper/pkce_generator.rb +33 -0
  125. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +31 -19
  126. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  127. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +9 -0
  128. data/{spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb → lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb} +3 -1
  129. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +8 -0
  130. data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
  131. data/lib/generators/doorkeeper/templates/initializer.rb +412 -33
  132. data/lib/generators/doorkeeper/templates/migration.rb.erb +88 -0
  133. data/lib/generators/doorkeeper/views_generator.rb +8 -4
  134. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  135. metadata +114 -276
  136. data/.coveralls.yml +0 -1
  137. data/.gitignore +0 -19
  138. data/.hound.yml +0 -13
  139. data/.rspec +0 -1
  140. data/.travis.yml +0 -26
  141. data/Appraisals +0 -14
  142. data/CONTRIBUTING.md +0 -47
  143. data/Gemfile +0 -10
  144. data/NEWS.md +0 -606
  145. data/RELEASING.md +0 -10
  146. data/Rakefile +0 -20
  147. data/app/validators/redirect_uri_validator.rb +0 -34
  148. data/doorkeeper.gemspec +0 -29
  149. data/gemfiles/rails_4_2.gemfile +0 -11
  150. data/gemfiles/rails_5_0.gemfile +0 -12
  151. data/gemfiles/rails_5_1.gemfile +0 -13
  152. data/lib/doorkeeper/oauth/client_credentials/validation.rb +0 -45
  153. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb +0 -7
  154. data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb +0 -11
  155. data/lib/generators/doorkeeper/templates/migration.rb +0 -68
  156. data/spec/controllers/application_metal_controller.rb +0 -10
  157. data/spec/controllers/applications_controller_spec.rb +0 -58
  158. data/spec/controllers/authorizations_controller_spec.rb +0 -218
  159. data/spec/controllers/protected_resources_controller_spec.rb +0 -300
  160. data/spec/controllers/token_info_controller_spec.rb +0 -52
  161. data/spec/controllers/tokens_controller_spec.rb +0 -88
  162. data/spec/dummy/Rakefile +0 -7
  163. data/spec/dummy/app/controllers/application_controller.rb +0 -3
  164. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +0 -7
  165. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +0 -12
  166. data/spec/dummy/app/controllers/home_controller.rb +0 -17
  167. data/spec/dummy/app/controllers/metal_controller.rb +0 -11
  168. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +0 -11
  169. data/spec/dummy/app/helpers/application_helper.rb +0 -5
  170. data/spec/dummy/app/models/user.rb +0 -5
  171. data/spec/dummy/app/views/home/index.html.erb +0 -0
  172. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  173. data/spec/dummy/config/application.rb +0 -23
  174. data/spec/dummy/config/boot.rb +0 -9
  175. data/spec/dummy/config/database.yml +0 -15
  176. data/spec/dummy/config/environment.rb +0 -5
  177. data/spec/dummy/config/environments/development.rb +0 -29
  178. data/spec/dummy/config/environments/production.rb +0 -62
  179. data/spec/dummy/config/environments/test.rb +0 -44
  180. data/spec/dummy/config/initializers/active_record_belongs_to_required_by_default.rb +0 -6
  181. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  182. data/spec/dummy/config/initializers/doorkeeper.rb +0 -96
  183. data/spec/dummy/config/initializers/secret_token.rb +0 -9
  184. data/spec/dummy/config/initializers/session_store.rb +0 -8
  185. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  186. data/spec/dummy/config/locales/doorkeeper.en.yml +0 -5
  187. data/spec/dummy/config/routes.rb +0 -52
  188. data/spec/dummy/config.ru +0 -4
  189. data/spec/dummy/db/migrate/20111122132257_create_users.rb +0 -9
  190. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +0 -5
  191. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +0 -60
  192. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +0 -7
  193. data/spec/dummy/db/schema.rb +0 -67
  194. data/spec/dummy/public/404.html +0 -26
  195. data/spec/dummy/public/422.html +0 -26
  196. data/spec/dummy/public/500.html +0 -26
  197. data/spec/dummy/public/favicon.ico +0 -0
  198. data/spec/dummy/script/rails +0 -6
  199. data/spec/factories.rb +0 -28
  200. data/spec/generators/application_owner_generator_spec.rb +0 -22
  201. data/spec/generators/install_generator_spec.rb +0 -31
  202. data/spec/generators/migration_generator_spec.rb +0 -20
  203. data/spec/generators/templates/routes.rb +0 -3
  204. data/spec/generators/views_generator_spec.rb +0 -27
  205. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +0 -24
  206. data/spec/lib/config_spec.rb +0 -334
  207. data/spec/lib/doorkeeper_spec.rb +0 -150
  208. data/spec/lib/models/expirable_spec.rb +0 -50
  209. data/spec/lib/models/revocable_spec.rb +0 -59
  210. data/spec/lib/models/scopes_spec.rb +0 -43
  211. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -41
  212. data/spec/lib/oauth/authorization_code_request_spec.rb +0 -80
  213. data/spec/lib/oauth/base_request_spec.rb +0 -160
  214. data/spec/lib/oauth/base_response_spec.rb +0 -45
  215. data/spec/lib/oauth/client/credentials_spec.rb +0 -88
  216. data/spec/lib/oauth/client_credentials/creator_spec.rb +0 -44
  217. data/spec/lib/oauth/client_credentials/issuer_spec.rb +0 -86
  218. data/spec/lib/oauth/client_credentials/validation_spec.rb +0 -54
  219. data/spec/lib/oauth/client_credentials_integration_spec.rb +0 -27
  220. data/spec/lib/oauth/client_credentials_request_spec.rb +0 -104
  221. data/spec/lib/oauth/client_spec.rb +0 -39
  222. data/spec/lib/oauth/code_request_spec.rb +0 -45
  223. data/spec/lib/oauth/code_response_spec.rb +0 -34
  224. data/spec/lib/oauth/error_response_spec.rb +0 -61
  225. data/spec/lib/oauth/error_spec.rb +0 -23
  226. data/spec/lib/oauth/forbidden_token_response_spec.rb +0 -23
  227. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -64
  228. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -20
  229. data/spec/lib/oauth/helpers/uri_checker_spec.rb +0 -104
  230. data/spec/lib/oauth/invalid_token_response_spec.rb +0 -56
  231. data/spec/lib/oauth/password_access_token_request_spec.rb +0 -90
  232. data/spec/lib/oauth/pre_authorization_spec.rb +0 -155
  233. data/spec/lib/oauth/refresh_token_request_spec.rb +0 -154
  234. data/spec/lib/oauth/scopes_spec.rb +0 -122
  235. data/spec/lib/oauth/token_request_spec.rb +0 -98
  236. data/spec/lib/oauth/token_response_spec.rb +0 -85
  237. data/spec/lib/oauth/token_spec.rb +0 -116
  238. data/spec/lib/request/strategy_spec.rb +0 -53
  239. data/spec/lib/server_spec.rb +0 -49
  240. data/spec/models/doorkeeper/access_grant_spec.rb +0 -36
  241. data/spec/models/doorkeeper/access_token_spec.rb +0 -394
  242. data/spec/models/doorkeeper/application_spec.rb +0 -179
  243. data/spec/requests/applications/applications_request_spec.rb +0 -94
  244. data/spec/requests/applications/authorized_applications_spec.rb +0 -30
  245. data/spec/requests/endpoints/authorization_spec.rb +0 -71
  246. data/spec/requests/endpoints/token_spec.rb +0 -64
  247. data/spec/requests/flows/authorization_code_errors_spec.rb +0 -76
  248. data/spec/requests/flows/authorization_code_spec.rb +0 -148
  249. data/spec/requests/flows/client_credentials_spec.rb +0 -58
  250. data/spec/requests/flows/implicit_grant_errors_spec.rb +0 -32
  251. data/spec/requests/flows/implicit_grant_spec.rb +0 -61
  252. data/spec/requests/flows/password_spec.rb +0 -115
  253. data/spec/requests/flows/refresh_token_spec.rb +0 -174
  254. data/spec/requests/flows/revoke_token_spec.rb +0 -157
  255. data/spec/requests/flows/skip_authorization_spec.rb +0 -59
  256. data/spec/requests/protected_resources/metal_spec.rb +0 -14
  257. data/spec/requests/protected_resources/private_api_spec.rb +0 -81
  258. data/spec/routing/custom_controller_routes_spec.rb +0 -71
  259. data/spec/routing/default_routes_spec.rb +0 -35
  260. data/spec/routing/scoped_routes_spec.rb +0 -31
  261. data/spec/spec_helper.rb +0 -4
  262. data/spec/spec_helper_integration.rb +0 -63
  263. data/spec/support/dependencies/factory_girl.rb +0 -2
  264. data/spec/support/helpers/access_token_request_helper.rb +0 -11
  265. data/spec/support/helpers/authorization_request_helper.rb +0 -41
  266. data/spec/support/helpers/config_helper.rb +0 -9
  267. data/spec/support/helpers/model_helper.rb +0 -67
  268. data/spec/support/helpers/request_spec_helper.rb +0 -84
  269. data/spec/support/helpers/url_helper.rb +0 -55
  270. data/spec/support/http_method_shim.rb +0 -38
  271. data/spec/support/orm/active_record.rb +0 -3
  272. data/spec/support/shared/controllers_shared_context.rb +0 -69
  273. data/spec/support/shared/models_shared_examples.rb +0 -52
  274. data/spec/validators/redirect_uri_validator_spec.rb +0 -78
@@ -1,11 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  module Helpers
6
+ # Default Doorkeeper token generator. Follows OAuth RFC and
7
+ # could be customized using `default_generator_method` in
8
+ # configuration.
4
9
  module UniqueToken
5
10
  def self.generate(options = {})
6
- generator_method = options.delete(:generator) || SecureRandom.method(:hex)
7
- token_size = options.delete(:size) || 32
8
- generator_method.call(token_size)
11
+ # Access Token value must be 1*VSCHAR or
12
+ # 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
13
+ #
14
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.12
15
+ # @see https://datatracker.ietf.org/doc/html/rfc6750#section-2.1
16
+ #
17
+ generator = options.delete(:generator) || SecureRandom.method(default_generator_method)
18
+ token_size = options.delete(:size) || 32
19
+ generator.call(token_size)
20
+ end
21
+
22
+ # Generator method for default generator class (SecureRandom)
23
+ #
24
+ def self.default_generator_method
25
+ Doorkeeper.config.default_generator_method
9
26
  end
10
27
  end
11
28
  end
@@ -1,10 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ipaddr"
4
+
1
5
  module Doorkeeper
2
6
  module OAuth
3
7
  module Helpers
4
8
  module URIChecker
5
9
  def self.valid?(url)
10
+ return true if oob_uri?(url)
11
+
6
12
  uri = as_uri(url)
7
- uri.fragment.nil? && !uri.host.nil? && !uri.scheme.nil?
13
+ valid_scheme?(uri) && iff_host?(uri) && uri.fragment.nil? && uri.opaque.nil?
8
14
  rescue URI::InvalidURIError
9
15
  false
10
16
  end
@@ -12,10 +18,32 @@ module Doorkeeper
12
18
  def self.matches?(url, client_url)
13
19
  url = as_uri(url)
14
20
  client_url = as_uri(client_url)
21
+
22
+ unless client_url.query.nil?
23
+ return false unless query_matches?(url.query, client_url.query)
24
+
25
+ # Clear out queries so rest of URI can be tested. This allows query
26
+ # params to be in the request but order not mattering.
27
+ client_url.query = nil
28
+ end
29
+
30
+ # RFC8252, Paragraph 7.3
31
+ # @see https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
32
+ if loopback_uri?(url) && loopback_uri?(client_url)
33
+ url.port = nil
34
+ client_url.port = nil
35
+ end
36
+
15
37
  url.query = nil
16
38
  url == client_url
17
39
  end
18
40
 
41
+ def self.loopback_uri?(uri)
42
+ IPAddr.new(uri.host).loopback?
43
+ rescue IPAddr::Error
44
+ false
45
+ end
46
+
19
47
  def self.valid_for_authorization?(url, client_url)
20
48
  valid?(url) && client_url.split.any? { |other_url| matches?(url, other_url) }
21
49
  end
@@ -24,8 +52,30 @@ module Doorkeeper
24
52
  URI.parse(url)
25
53
  end
26
54
 
27
- def self.native_uri?(url)
28
- url == Doorkeeper.configuration.native_redirect_uri
55
+ def self.query_matches?(query, client_query)
56
+ return true if client_query.blank? && query.blank?
57
+ return false if client_query.nil? || query.nil?
58
+
59
+ # Will return true independent of query order
60
+ client_query.split("&").sort == query.split("&").sort
61
+ end
62
+
63
+ def self.valid_scheme?(uri)
64
+ return false if uri.scheme.nil?
65
+
66
+ %w[localhost].include?(uri.scheme) == false
67
+ end
68
+
69
+ def self.hypertext_scheme?(uri)
70
+ %w[http https].include?(uri.scheme)
71
+ end
72
+
73
+ def self.iff_host?(uri)
74
+ !(hypertext_scheme?(uri) && uri.host.nil?)
75
+ end
76
+
77
+ def self.oob_uri?(uri)
78
+ NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
29
79
  end
30
80
  end
31
81
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ module Hooks
6
+ class Context
7
+ attr_reader :auth, :pre_auth
8
+
9
+ def initialize(**attributes)
10
+ attributes.each do |name, value|
11
+ instance_variable_set(:"@#{name}", value) if respond_to?(name)
12
+ end
13
+ end
14
+
15
+ def issued_token
16
+ auth&.issued_token
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ class InvalidRequestResponse < ErrorResponse
6
+ attr_reader :reason
7
+
8
+ def self.from_request(request, attributes = {})
9
+ new(
10
+ attributes.merge(
11
+ state: request.try(:state),
12
+ redirect_uri: request.try(:redirect_uri),
13
+ missing_param: request.try(:missing_param),
14
+ reason: request.try(:invalid_request_reason),
15
+ ),
16
+ )
17
+ end
18
+
19
+ def initialize(attributes = {})
20
+ super(attributes.merge(name: :invalid_request))
21
+ @missing_param = attributes[:missing_param]
22
+ @reason = @missing_param.nil? ? attributes[:reason] : :missing_param
23
+ end
24
+
25
+ def status
26
+ :bad_request
27
+ end
28
+
29
+ def description
30
+ I18n.translate(
31
+ reason,
32
+ scope: %i[doorkeeper errors messages invalid_request],
33
+ default: :unknown,
34
+ value: @missing_param,
35
+ )
36
+ end
37
+
38
+ def redirectable?
39
+ super && @missing_param != :client_id
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,13 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class InvalidTokenResponse < ErrorResponse
4
6
  attr_reader :reason
5
7
 
6
8
  def self.from_access_token(access_token, attributes = {})
7
- reason = case
8
- when access_token.try(:revoked?)
9
+ reason = if access_token&.revoked?
9
10
  :revoked
10
- when access_token.try(:expired?)
11
+ elsif access_token&.expired?
11
12
  :expired
12
13
  else
13
14
  :unknown
@@ -21,9 +22,32 @@ module Doorkeeper
21
22
  @reason = attributes[:reason] || :unknown
22
23
  end
23
24
 
25
+ def status
26
+ :unauthorized
27
+ end
28
+
24
29
  def description
25
- scope = { scope: [:doorkeeper, :errors, :messages, :invalid_token] }
26
- @description ||= I18n.translate @reason, scope
30
+ @description ||=
31
+ I18n.translate(
32
+ @reason,
33
+ scope: %i[doorkeeper errors messages invalid_token],
34
+ )
35
+ end
36
+
37
+ protected
38
+
39
+ def exception_class
40
+ errors_mapping.fetch(reason)
41
+ end
42
+
43
+ private
44
+
45
+ def errors_mapping
46
+ {
47
+ expired: Doorkeeper::Errors::TokenExpired,
48
+ revoked: Doorkeeper::Errors::TokenRevoked,
49
+ unknown: Doorkeeper::Errors::TokenUnknown,
50
+ }
27
51
  end
28
52
  end
29
53
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ class NonStandard
6
+ # These are not part of the OAuth 2 specification but are still in use by Google
7
+ # and in some other implementations. Native applications should use one of the
8
+ # approaches discussed in RFC8252. OOB is 'Out of Band'
9
+
10
+ # This value signals to the Google Authorization Server that the authorization
11
+ # code should be returned in the title bar of the browser, with the page text
12
+ # prompting the user to copy the code and paste it in the application.
13
+ # This is useful when the client (such as a Windows application) cannot listen
14
+ # on an HTTP port without significant client configuration.
15
+
16
+ # When you use this value, your application can then detect that the page has loaded, and can
17
+ # read the title of the HTML page to obtain the authorization code. It is then up to your
18
+ # application to close the browser window if you want to ensure that the user never sees the
19
+ # page that contains the authorization code. The mechanism for doing this varies from platform
20
+ # to platform.
21
+ #
22
+ # If your platform doesn't allow you to detect that the page has loaded or read the title of
23
+ # the page, you can have the user paste the code back to your application, as prompted by the
24
+ # text in the confirmation page that the OAuth 2.0 server generates.
25
+ IETF_WG_OAUTH2_OOB = "urn:ietf:wg:oauth:2.0:oob"
26
+
27
+ # This is identical to urn:ietf:wg:oauth:2.0:oob, but the text in the confirmation page that
28
+ # the OAuth 2.0 server generates won't instruct the user to copy the authorization code, but
29
+ # instead will simply ask the user to close the window.
30
+ #
31
+ # This is useful when your application reads the title of the HTML page (by checking window
32
+ # titles on the desktop, for example) to obtain the authorization code, but can't close the
33
+ # page on its own.
34
+ IETF_WG_OAUTH2_OOB_AUTO = "urn:ietf:wg:oauth:2.0:oob:auto"
35
+
36
+ IETF_WG_OAUTH2_OOB_METHODS = [IETF_WG_OAUTH2_OOB, IETF_WG_OAUTH2_OOB_AUTO].freeze
37
+ end
38
+ end
39
+ end
@@ -1,40 +1,74 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class PasswordAccessTokenRequest < BaseRequest
4
6
  include OAuth::Helpers
5
7
 
6
- validate :client, error: :invalid_client
8
+ validate :client, error: :invalid_client
9
+ validate :client_supports_grant_flow, error: :unauthorized_client
7
10
  validate :resource_owner, error: :invalid_grant
8
- validate :scopes, error: :invalid_scope
11
+ validate :scopes, error: :invalid_scope
9
12
 
10
- attr_accessor :server, :client, :resource_owner, :parameters,
11
- :access_token
13
+ attr_reader :client, :credentials, :resource_owner, :parameters, :access_token
12
14
 
13
- def initialize(server, client, resource_owner, parameters = {})
15
+ def initialize(server, client, credentials, resource_owner, parameters = {})
14
16
  @server = server
15
17
  @resource_owner = resource_owner
16
18
  @client = client
19
+ @credentials = credentials
17
20
  @parameters = parameters
18
21
  @original_scopes = parameters[:scope]
22
+ @grant_type = Doorkeeper::OAuth::PASSWORD
19
23
  end
20
24
 
21
25
  private
22
26
 
23
27
  def before_successful_response
24
- find_or_create_access_token(client, resource_owner.id, scopes, server)
28
+ find_or_create_access_token(client, resource_owner, scopes, server)
29
+ super
25
30
  end
26
31
 
27
32
  def validate_scopes
28
- return true unless @original_scopes.present?
29
- ScopeChecker.valid? @original_scopes, server.scopes, client.try(:scopes)
33
+ return true if scopes.blank?
34
+
35
+ ScopeChecker.valid?(
36
+ scope_str: scopes.to_s,
37
+ server_scopes: server.scopes,
38
+ app_scopes: client.try(:scopes),
39
+ grant_type: grant_type,
40
+ )
30
41
  end
31
42
 
32
43
  def validate_resource_owner
33
- !!resource_owner
44
+ resource_owner.present?
34
45
  end
35
46
 
47
+ # Section 4.3.2. Access Token Request for Resource Owner Password Credentials Grant:
48
+ #
49
+ # If the client type is confidential or the client was issued client credentials (or assigned
50
+ # other authentication requirements), the client MUST authenticate with the authorization
51
+ # server as described in Section 3.2.1.
52
+ #
53
+ # The authorization server MUST:
54
+ #
55
+ # o require client authentication for confidential clients or for any client that was
56
+ # issued client credentials (or with other authentication requirements)
57
+ #
58
+ # o authenticate the client if client authentication is included,
59
+ #
60
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#section-4.3
61
+ #
36
62
  def validate_client
37
- !parameters[:client_id] || !!client
63
+ if Doorkeeper.config.skip_client_authentication_for_password_grant
64
+ client.present? || (!parameters[:client_id] && credentials.blank?)
65
+ else
66
+ client.present?
67
+ end
68
+ end
69
+
70
+ def validate_client_supports_grant_flow
71
+ server_config.allow_grant_flow_for_client?(grant_type, client&.application)
38
72
  end
39
73
  end
40
74
  end
@@ -1,23 +1,36 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class PreAuthorization
4
6
  include Validations
5
7
 
6
- validate :response_type, error: :unsupported_response_type
8
+ validate :client_id, error: :invalid_request
7
9
  validate :client, error: :invalid_client
8
- validate :scopes, error: :invalid_scope
10
+ validate :client_supports_grant_flow, error: :unauthorized_client
11
+ validate :resource_owner_authorize_for_client, error: :invalid_client
9
12
  validate :redirect_uri, error: :invalid_redirect_uri
13
+ validate :params, error: :invalid_request
14
+ validate :response_type, error: :unsupported_response_type
15
+ validate :response_mode, error: :unsupported_response_mode
16
+ validate :scopes, error: :invalid_scope
17
+ validate :code_challenge_method, error: :invalid_code_challenge_method
10
18
 
11
- attr_accessor :server, :client, :response_type, :redirect_uri, :state
12
- attr_writer :scope
19
+ attr_reader :client, :code_challenge, :code_challenge_method, :missing_param,
20
+ :redirect_uri, :resource_owner, :response_type, :state,
21
+ :authorization_response_flow, :response_mode
13
22
 
14
- def initialize(server, client, attrs = {})
15
- @server = server
16
- @client = client
17
- @response_type = attrs[:response_type]
18
- @redirect_uri = attrs[:redirect_uri]
19
- @scope = attrs[:scope]
20
- @state = attrs[:state]
23
+ def initialize(server, parameters = {}, resource_owner = nil)
24
+ @server = server
25
+ @client_id = parameters[:client_id]
26
+ @response_type = parameters[:response_type]
27
+ @response_mode = parameters[:response_mode]
28
+ @redirect_uri = parameters[:redirect_uri]
29
+ @scope = parameters[:scope]
30
+ @state = parameters[:state]
31
+ @code_challenge = parameters[:code_challenge]
32
+ @code_challenge_method = parameters[:code_challenge_method]
33
+ @resource_owner = resource_owner
21
34
  end
22
35
 
23
36
  def authorizable?
@@ -25,41 +38,137 @@ module Doorkeeper
25
38
  end
26
39
 
27
40
  def scopes
28
- Scopes.from_string scope
41
+ Scopes.from_string(scope)
29
42
  end
30
43
 
31
44
  def scope
32
- @scope.presence || server.default_scopes.to_s
45
+ @scope.presence || (server.default_scopes.presence && build_scopes)
33
46
  end
34
47
 
35
48
  def error_response
36
- OAuth::ErrorResponse.from_request(self)
49
+ if error == :invalid_request
50
+ OAuth::InvalidRequestResponse.from_request(
51
+ self,
52
+ response_on_fragment: response_on_fragment?,
53
+ )
54
+ else
55
+ OAuth::ErrorResponse.from_request(self, response_on_fragment: response_on_fragment?)
56
+ end
57
+ end
58
+
59
+ def as_json(_options = nil)
60
+ pre_auth_hash
61
+ end
62
+
63
+ def form_post_response?
64
+ response_mode == "form_post"
37
65
  end
38
66
 
39
67
  private
40
68
 
41
- def validate_response_type
42
- server.authorization_response_types.include? response_type
69
+ attr_reader :client_id, :server
70
+
71
+ def build_scopes
72
+ client_scopes = client.scopes
73
+ if client_scopes.blank?
74
+ server.default_scopes.to_s
75
+ else
76
+ (server.default_scopes & client_scopes).to_s
77
+ end
78
+ end
79
+
80
+ def validate_client_id
81
+ @missing_param = :client_id if client_id.blank?
82
+ @missing_param.nil?
43
83
  end
44
84
 
45
85
  def validate_client
46
- client.present?
86
+ @client = OAuth::Client.find(client_id)
87
+ @client.present?
88
+ end
89
+
90
+ def validate_client_supports_grant_flow
91
+ Doorkeeper.config.allow_grant_flow_for_client?(grant_type, client.application)
92
+ end
93
+
94
+ def validate_resource_owner_authorize_for_client
95
+ # The `authorize_resource_owner_for_client` config option is used for this validation
96
+ client.application.authorized_for_resource_owner?(@resource_owner)
97
+ end
98
+
99
+ def validate_redirect_uri
100
+ return false if redirect_uri.blank?
101
+
102
+ Helpers::URIChecker.valid_for_authorization?(
103
+ redirect_uri,
104
+ client.redirect_uri,
105
+ )
106
+ end
107
+
108
+ def validate_params
109
+ @missing_param = if response_type.blank?
110
+ :response_type
111
+ elsif @scope.blank? && server.default_scopes.blank?
112
+ :scope
113
+ end
114
+
115
+ @missing_param.nil?
116
+ end
117
+
118
+ def validate_response_type
119
+ server.authorization_response_flows.any? do |flow|
120
+ if flow.matches_response_type?(response_type)
121
+ @authorization_response_flow = flow
122
+ true
123
+ end
124
+ end
125
+ end
126
+
127
+ def validate_response_mode
128
+ if response_mode.blank?
129
+ @response_mode = authorization_response_flow.default_response_mode
130
+ return true
131
+ end
132
+
133
+ authorization_response_flow.matches_response_mode?(response_mode)
47
134
  end
48
135
 
49
136
  def validate_scopes
50
- return true unless scope.present?
51
137
  Helpers::ScopeChecker.valid?(
52
- scope,
53
- server.scopes,
54
- client.application.scopes
138
+ scope_str: scope,
139
+ server_scopes: server.scopes,
140
+ app_scopes: client.scopes,
141
+ grant_type: grant_type,
55
142
  )
56
143
  end
57
144
 
58
- # TODO: test uri should be matched against the client's one
59
- def validate_redirect_uri
60
- return false unless redirect_uri.present?
61
- Helpers::URIChecker.native_uri?(redirect_uri) ||
62
- Helpers::URIChecker.valid_for_authorization?(redirect_uri, client.redirect_uri)
145
+ def validate_code_challenge_method
146
+ return true unless Doorkeeper.config.access_grant_model.pkce_supported?
147
+
148
+ code_challenge.blank? ||
149
+ (code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
150
+ end
151
+
152
+ def response_on_fragment?
153
+ return response_type == "token" if response_mode.nil?
154
+
155
+ response_mode == "fragment"
156
+ end
157
+
158
+ def grant_type
159
+ response_type == "code" ? AUTHORIZATION_CODE : IMPLICIT
160
+ end
161
+
162
+ def pre_auth_hash
163
+ {
164
+ client_id: client.uid,
165
+ redirect_uri: redirect_uri,
166
+ state: state,
167
+ response_type: response_type,
168
+ scope: scope,
169
+ client_name: client.name,
170
+ status: I18n.t("doorkeeper.pre_authorization.status"),
171
+ }
63
172
  end
64
173
  end
65
174
  end