doorkeeper 4.4.3 → 5.5.2

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 (282) hide show
  1. checksums.yaml +5 -5
  2. data/{NEWS.md → CHANGELOG.md} +393 -19
  3. data/README.md +97 -393
  4. data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
  5. data/app/controllers/doorkeeper/application_controller.rb +8 -5
  6. data/app/controllers/doorkeeper/application_metal_controller.rb +7 -11
  7. data/app/controllers/doorkeeper/applications_controller.rb +62 -27
  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 +98 -32
  12. data/app/helpers/doorkeeper/dashboard_helper.rb +9 -7
  13. data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
  14. data/app/views/doorkeeper/applications/_form.html.erb +27 -26
  15. data/app/views/doorkeeper/applications/edit.html.erb +1 -1
  16. data/app/views/doorkeeper/applications/index.html.erb +17 -7
  17. data/app/views/doorkeeper/applications/new.html.erb +1 -1
  18. data/app/views/doorkeeper/applications/show.html.erb +38 -17
  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/layouts/doorkeeper/admin.html.erb +16 -14
  23. data/config/locales/en.yml +23 -3
  24. data/lib/doorkeeper/config/abstract_builder.rb +28 -0
  25. data/lib/doorkeeper/config/option.rb +82 -0
  26. data/lib/doorkeeper/config/validations.rb +53 -0
  27. data/lib/doorkeeper/config.rb +471 -140
  28. data/lib/doorkeeper/engine.rb +8 -2
  29. data/lib/doorkeeper/errors.rb +25 -16
  30. data/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
  31. data/lib/doorkeeper/grant_flow/flow.rb +44 -0
  32. data/lib/doorkeeper/grant_flow/registry.rb +50 -0
  33. data/lib/doorkeeper/grant_flow.rb +45 -0
  34. data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
  35. data/lib/doorkeeper/grape/helpers.rb +13 -7
  36. data/lib/doorkeeper/helpers/controller.rb +43 -10
  37. data/lib/doorkeeper/models/access_grant_mixin.rb +97 -3
  38. data/lib/doorkeeper/models/access_token_mixin.rb +272 -66
  39. data/lib/doorkeeper/models/application_mixin.rb +50 -5
  40. data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
  41. data/lib/doorkeeper/models/concerns/expirable.rb +7 -3
  42. data/lib/doorkeeper/models/concerns/orderable.rb +2 -0
  43. data/lib/doorkeeper/models/concerns/ownership.rb +4 -7
  44. data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
  45. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  46. data/lib/doorkeeper/models/concerns/revocable.rb +3 -27
  47. data/lib/doorkeeper/models/concerns/scopes.rb +12 -2
  48. data/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
  49. data/lib/doorkeeper/oauth/authorization/code.rb +48 -12
  50. data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
  51. data/lib/doorkeeper/oauth/authorization/token.rb +58 -24
  52. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +7 -5
  53. data/lib/doorkeeper/oauth/authorization_code_request.rb +58 -10
  54. data/lib/doorkeeper/oauth/base_request.rb +35 -24
  55. data/lib/doorkeeper/oauth/base_response.rb +2 -0
  56. data/lib/doorkeeper/oauth/client/credentials.rb +5 -5
  57. data/lib/doorkeeper/oauth/client.rb +10 -11
  58. data/lib/doorkeeper/oauth/client_credentials/creator.rb +47 -4
  59. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +16 -9
  60. data/lib/doorkeeper/oauth/client_credentials/validator.rb +56 -0
  61. data/lib/doorkeeper/oauth/client_credentials_request.rb +10 -11
  62. data/lib/doorkeeper/oauth/code_request.rb +8 -12
  63. data/lib/doorkeeper/oauth/code_response.rb +27 -15
  64. data/lib/doorkeeper/oauth/error.rb +3 -1
  65. data/lib/doorkeeper/oauth/error_response.rb +35 -14
  66. data/lib/doorkeeper/oauth/forbidden_token_response.rb +10 -3
  67. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -18
  68. data/lib/doorkeeper/oauth/helpers/unique_token.rb +20 -3
  69. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +42 -7
  70. data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
  71. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  72. data/lib/doorkeeper/oauth/invalid_token_response.rb +29 -4
  73. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  74. data/lib/doorkeeper/oauth/password_access_token_request.rb +43 -10
  75. data/lib/doorkeeper/oauth/pre_authorization.rb +133 -26
  76. data/lib/doorkeeper/oauth/refresh_token_request.rb +59 -31
  77. data/lib/doorkeeper/oauth/scopes.rb +8 -4
  78. data/lib/doorkeeper/oauth/token.rb +12 -8
  79. data/lib/doorkeeper/oauth/token_introspection.rb +97 -23
  80. data/lib/doorkeeper/oauth/token_request.rb +8 -20
  81. data/lib/doorkeeper/oauth/token_response.rb +14 -10
  82. data/lib/doorkeeper/oauth.rb +13 -0
  83. data/lib/doorkeeper/orm/active_record/access_grant.rb +5 -30
  84. data/lib/doorkeeper/orm/active_record/access_token.rb +5 -43
  85. data/lib/doorkeeper/orm/active_record/application.rb +6 -57
  86. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +68 -0
  87. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +59 -0
  88. data/lib/doorkeeper/orm/active_record/mixins/application.rb +198 -0
  89. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +66 -0
  90. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +33 -0
  91. data/lib/doorkeeper/orm/active_record.rb +27 -9
  92. data/lib/doorkeeper/rails/helpers.rb +10 -8
  93. data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
  94. data/lib/doorkeeper/rails/routes/mapper.rb +4 -2
  95. data/lib/doorkeeper/rails/routes/mapping.rb +9 -7
  96. data/lib/doorkeeper/rails/routes/registry.rb +45 -0
  97. data/lib/doorkeeper/rails/routes.rb +37 -30
  98. data/lib/doorkeeper/rake/db.rake +40 -0
  99. data/lib/doorkeeper/rake/setup.rake +11 -0
  100. data/lib/doorkeeper/rake.rb +14 -0
  101. data/lib/doorkeeper/request/authorization_code.rb +6 -4
  102. data/lib/doorkeeper/request/client_credentials.rb +3 -3
  103. data/lib/doorkeeper/request/code.rb +1 -1
  104. data/lib/doorkeeper/request/password.rb +4 -3
  105. data/lib/doorkeeper/request/refresh_token.rb +6 -5
  106. data/lib/doorkeeper/request/strategy.rb +4 -2
  107. data/lib/doorkeeper/request/token.rb +1 -1
  108. data/lib/doorkeeper/request.rb +61 -34
  109. data/lib/doorkeeper/secret_storing/base.rb +64 -0
  110. data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  111. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  112. data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  113. data/lib/doorkeeper/server.rb +9 -11
  114. data/lib/doorkeeper/stale_records_cleaner.rb +24 -0
  115. data/lib/doorkeeper/validations.rb +2 -0
  116. data/lib/doorkeeper/version.rb +7 -29
  117. data/lib/doorkeeper.rb +111 -64
  118. data/lib/generators/doorkeeper/application_owner_generator.rb +24 -18
  119. data/lib/generators/doorkeeper/confidential_applications_generator.rb +33 -0
  120. data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
  121. data/lib/generators/doorkeeper/install_generator.rb +19 -9
  122. data/lib/generators/doorkeeper/migration_generator.rb +23 -18
  123. data/lib/generators/doorkeeper/pkce_generator.rb +33 -0
  124. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +28 -22
  125. data/{spec/dummy/db/migrate/20180210183654_add_confidential_to_application.rb → lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb} +2 -2
  126. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +3 -1
  127. data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +2 -0
  128. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +8 -0
  129. data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
  130. data/lib/generators/doorkeeper/templates/initializer.rb +382 -30
  131. data/lib/generators/doorkeeper/templates/migration.rb.erb +35 -16
  132. data/lib/generators/doorkeeper/views_generator.rb +8 -4
  133. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  134. metadata +95 -309
  135. data/.coveralls.yml +0 -1
  136. data/.github/ISSUE_TEMPLATE.md +0 -25
  137. data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  138. data/.gitignore +0 -19
  139. data/.hound.yml +0 -2
  140. data/.rspec +0 -1
  141. data/.rubocop.yml +0 -17
  142. data/.travis.yml +0 -38
  143. data/Appraisals +0 -18
  144. data/CODE_OF_CONDUCT.md +0 -46
  145. data/CONTRIBUTING.md +0 -47
  146. data/Gemfile +0 -10
  147. data/RELEASING.md +0 -10
  148. data/Rakefile +0 -20
  149. data/SECURITY.md +0 -15
  150. data/app/validators/redirect_uri_validator.rb +0 -44
  151. data/doorkeeper.gemspec +0 -32
  152. data/gemfiles/rails_4_2.gemfile +0 -13
  153. data/gemfiles/rails_5_0.gemfile +0 -12
  154. data/gemfiles/rails_5_1.gemfile +0 -12
  155. data/gemfiles/rails_5_2.gemfile +0 -12
  156. data/gemfiles/rails_master.gemfile +0 -14
  157. data/lib/doorkeeper/oauth/client_credentials/validation.rb +0 -45
  158. data/lib/generators/doorkeeper/add_client_confidentiality_generator.rb +0 -31
  159. data/lib/generators/doorkeeper/templates/add_confidential_to_application_migration.rb.erb +0 -11
  160. data/spec/controllers/application_metal_controller.rb +0 -10
  161. data/spec/controllers/applications_controller_spec.rb +0 -69
  162. data/spec/controllers/authorizations_controller_spec.rb +0 -250
  163. data/spec/controllers/protected_resources_controller_spec.rb +0 -309
  164. data/spec/controllers/token_info_controller_spec.rb +0 -56
  165. data/spec/controllers/tokens_controller_spec.rb +0 -274
  166. data/spec/dummy/Rakefile +0 -7
  167. data/spec/dummy/app/controllers/application_controller.rb +0 -3
  168. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +0 -7
  169. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +0 -12
  170. data/spec/dummy/app/controllers/home_controller.rb +0 -17
  171. data/spec/dummy/app/controllers/metal_controller.rb +0 -11
  172. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +0 -11
  173. data/spec/dummy/app/helpers/application_helper.rb +0 -5
  174. data/spec/dummy/app/models/user.rb +0 -5
  175. data/spec/dummy/app/views/home/index.html.erb +0 -0
  176. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  177. data/spec/dummy/config/application.rb +0 -23
  178. data/spec/dummy/config/boot.rb +0 -9
  179. data/spec/dummy/config/database.yml +0 -15
  180. data/spec/dummy/config/environment.rb +0 -5
  181. data/spec/dummy/config/environments/development.rb +0 -29
  182. data/spec/dummy/config/environments/production.rb +0 -62
  183. data/spec/dummy/config/environments/test.rb +0 -44
  184. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  185. data/spec/dummy/config/initializers/doorkeeper.rb +0 -112
  186. data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -6
  187. data/spec/dummy/config/initializers/secret_token.rb +0 -8
  188. data/spec/dummy/config/initializers/session_store.rb +0 -8
  189. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  190. data/spec/dummy/config/locales/doorkeeper.en.yml +0 -5
  191. data/spec/dummy/config/routes.rb +0 -52
  192. data/spec/dummy/config.ru +0 -4
  193. data/spec/dummy/db/migrate/20111122132257_create_users.rb +0 -11
  194. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +0 -7
  195. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +0 -62
  196. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +0 -9
  197. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +0 -13
  198. data/spec/dummy/db/schema.rb +0 -68
  199. data/spec/dummy/public/404.html +0 -26
  200. data/spec/dummy/public/422.html +0 -26
  201. data/spec/dummy/public/500.html +0 -26
  202. data/spec/dummy/public/favicon.ico +0 -0
  203. data/spec/dummy/script/rails +0 -6
  204. data/spec/factories.rb +0 -28
  205. data/spec/generators/application_owner_generator_spec.rb +0 -41
  206. data/spec/generators/install_generator_spec.rb +0 -31
  207. data/spec/generators/migration_generator_spec.rb +0 -41
  208. data/spec/generators/previous_refresh_token_generator_spec.rb +0 -57
  209. data/spec/generators/templates/routes.rb +0 -3
  210. data/spec/generators/views_generator_spec.rb +0 -27
  211. data/spec/grape/grape_integration_spec.rb +0 -135
  212. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +0 -24
  213. data/spec/lib/config_spec.rb +0 -462
  214. data/spec/lib/doorkeeper_spec.rb +0 -150
  215. data/spec/lib/models/expirable_spec.rb +0 -50
  216. data/spec/lib/models/revocable_spec.rb +0 -59
  217. data/spec/lib/models/scopes_spec.rb +0 -43
  218. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -41
  219. data/spec/lib/oauth/authorization_code_request_spec.rb +0 -123
  220. data/spec/lib/oauth/base_request_spec.rb +0 -155
  221. data/spec/lib/oauth/base_response_spec.rb +0 -45
  222. data/spec/lib/oauth/client/credentials_spec.rb +0 -90
  223. data/spec/lib/oauth/client_credentials/creator_spec.rb +0 -44
  224. data/spec/lib/oauth/client_credentials/issuer_spec.rb +0 -86
  225. data/spec/lib/oauth/client_credentials/validation_spec.rb +0 -54
  226. data/spec/lib/oauth/client_credentials_integration_spec.rb +0 -27
  227. data/spec/lib/oauth/client_credentials_request_spec.rb +0 -105
  228. data/spec/lib/oauth/client_spec.rb +0 -39
  229. data/spec/lib/oauth/code_request_spec.rb +0 -43
  230. data/spec/lib/oauth/code_response_spec.rb +0 -34
  231. data/spec/lib/oauth/error_response_spec.rb +0 -61
  232. data/spec/lib/oauth/error_spec.rb +0 -23
  233. data/spec/lib/oauth/forbidden_token_response_spec.rb +0 -23
  234. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -64
  235. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -20
  236. data/spec/lib/oauth/helpers/uri_checker_spec.rb +0 -218
  237. data/spec/lib/oauth/invalid_token_response_spec.rb +0 -56
  238. data/spec/lib/oauth/password_access_token_request_spec.rb +0 -96
  239. data/spec/lib/oauth/pre_authorization_spec.rb +0 -160
  240. data/spec/lib/oauth/refresh_token_request_spec.rb +0 -166
  241. data/spec/lib/oauth/scopes_spec.rb +0 -149
  242. data/spec/lib/oauth/token_request_spec.rb +0 -96
  243. data/spec/lib/oauth/token_response_spec.rb +0 -85
  244. data/spec/lib/oauth/token_spec.rb +0 -116
  245. data/spec/lib/request/strategy_spec.rb +0 -53
  246. data/spec/lib/server_spec.rb +0 -59
  247. data/spec/models/doorkeeper/access_grant_spec.rb +0 -36
  248. data/spec/models/doorkeeper/access_token_spec.rb +0 -418
  249. data/spec/models/doorkeeper/application_spec.rb +0 -303
  250. data/spec/requests/applications/applications_request_spec.rb +0 -94
  251. data/spec/requests/applications/authorized_applications_spec.rb +0 -30
  252. data/spec/requests/endpoints/authorization_spec.rb +0 -71
  253. data/spec/requests/endpoints/token_spec.rb +0 -71
  254. data/spec/requests/flows/authorization_code_errors_spec.rb +0 -76
  255. data/spec/requests/flows/authorization_code_spec.rb +0 -149
  256. data/spec/requests/flows/client_credentials_spec.rb +0 -86
  257. data/spec/requests/flows/implicit_grant_errors_spec.rb +0 -32
  258. data/spec/requests/flows/implicit_grant_spec.rb +0 -61
  259. data/spec/requests/flows/password_spec.rb +0 -197
  260. data/spec/requests/flows/refresh_token_spec.rb +0 -174
  261. data/spec/requests/flows/revoke_token_spec.rb +0 -157
  262. data/spec/requests/flows/skip_authorization_spec.rb +0 -59
  263. data/spec/requests/protected_resources/metal_spec.rb +0 -14
  264. data/spec/requests/protected_resources/private_api_spec.rb +0 -81
  265. data/spec/routing/custom_controller_routes_spec.rb +0 -75
  266. data/spec/routing/default_routes_spec.rb +0 -39
  267. data/spec/routing/scoped_routes_spec.rb +0 -31
  268. data/spec/spec_helper.rb +0 -4
  269. data/spec/spec_helper_integration.rb +0 -74
  270. data/spec/support/dependencies/factory_girl.rb +0 -2
  271. data/spec/support/helpers/access_token_request_helper.rb +0 -11
  272. data/spec/support/helpers/authorization_request_helper.rb +0 -41
  273. data/spec/support/helpers/config_helper.rb +0 -9
  274. data/spec/support/helpers/model_helper.rb +0 -72
  275. data/spec/support/helpers/request_spec_helper.rb +0 -88
  276. data/spec/support/helpers/url_helper.rb +0 -56
  277. data/spec/support/http_method_shim.rb +0 -38
  278. data/spec/support/orm/active_record.rb +0 -3
  279. data/spec/support/shared/controllers_shared_context.rb +0 -65
  280. data/spec/support/shared/models_shared_examples.rb +0 -52
  281. data/spec/validators/redirect_uri_validator_spec.rb +0 -123
  282. data/spec/version/version_spec.rb +0 -15
@@ -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://tools.ietf.org/html/rfc6749#appendix-A.12
15
+ # @see https://tools.ietf.org/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,11 +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)
6
- return true if native_uri?(url)
10
+ return true if oob_uri?(url)
11
+
7
12
  uri = as_uri(url)
8
- uri.fragment.nil? && !uri.host.nil? && !uri.scheme.nil?
13
+ valid_scheme?(uri) && iff_host?(uri) && uri.fragment.nil? && uri.opaque.nil?
9
14
  rescue URI::InvalidURIError
10
15
  false
11
16
  end
@@ -14,16 +19,31 @@ module Doorkeeper
14
19
  url = as_uri(url)
15
20
  client_url = as_uri(client_url)
16
21
 
17
- if client_url.query.present?
22
+ unless client_url.query.nil?
18
23
  return false unless query_matches?(url.query, client_url.query)
24
+
19
25
  # Clear out queries so rest of URI can be tested. This allows query
20
26
  # params to be in the request but order not mattering.
21
27
  client_url.query = nil
22
28
  end
29
+
30
+ # RFC8252, Paragraph 7.3
31
+ # @see https://tools.ietf.org/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
+
23
37
  url.query = nil
24
38
  url == client_url
25
39
  end
26
40
 
41
+ def self.loopback_uri?(uri)
42
+ IPAddr.new(uri.host).loopback?
43
+ rescue IPAddr::Error
44
+ false
45
+ end
46
+
27
47
  def self.valid_for_authorization?(url, client_url)
28
48
  valid?(url) && client_url.split.any? { |other_url| matches?(url, other_url) }
29
49
  end
@@ -33,14 +53,29 @@ module Doorkeeper
33
53
  end
34
54
 
35
55
  def self.query_matches?(query, client_query)
36
- return true if client_query.nil? && query.nil?
56
+ return true if client_query.blank? && query.blank?
37
57
  return false if client_query.nil? || query.nil?
58
+
38
59
  # Will return true independent of query order
39
- client_query.split('&').sort == query.split('&').sort
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?)
40
75
  end
41
76
 
42
- def self.native_uri?(url)
43
- url == Doorkeeper.configuration.native_redirect_uri
77
+ def self.oob_uri?(uri)
78
+ NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
44
79
  end
45
80
  end
46
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,12 +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 = if access_token.try(:revoked?)
9
+ reason = if access_token&.revoked?
8
10
  :revoked
9
- elsif access_token.try(:expired?)
11
+ elsif access_token&.expired?
10
12
  :expired
11
13
  else
12
14
  :unknown
@@ -20,9 +22,32 @@ module Doorkeeper
20
22
  @reason = attributes[:reason] || :unknown
21
23
  end
22
24
 
25
+ def status
26
+ :unauthorized
27
+ end
28
+
23
29
  def description
24
- scope = { scope: %i[doorkeeper errors messages invalid_token] }
25
- @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
+ }
26
51
  end
27
52
  end
28
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,41 +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)
25
29
  super
26
30
  end
27
31
 
28
32
  def validate_scopes
29
- return true unless @original_scopes.present?
30
- 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
+ )
31
41
  end
32
42
 
33
43
  def validate_resource_owner
34
- !!resource_owner
44
+ resource_owner.present?
35
45
  end
36
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://tools.ietf.org/html/rfc6749#section-4.3
61
+ #
37
62
  def validate_client
38
- !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)
39
72
  end
40
73
  end
41
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,44 +38,138 @@ 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?
47
88
  end
48
89
 
49
- def validate_scopes
50
- return true unless scope.present?
51
- Helpers::ScopeChecker.valid?(
52
- scope,
53
- server.scopes,
54
- client.application.scopes
55
- )
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)
56
97
  end
57
98
 
58
- # TODO: test uri should be matched against the client's one
59
99
  def validate_redirect_uri
60
100
  return false if redirect_uri.blank?
61
101
 
62
102
  Helpers::URIChecker.valid_for_authorization?(
63
- redirect_uri, client.redirect_uri
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)
134
+ end
135
+
136
+ def validate_scopes
137
+ Helpers::ScopeChecker.valid?(
138
+ scope_str: scope,
139
+ server_scopes: server.scopes,
140
+ app_scopes: client.scopes,
141
+ grant_type: grant_type,
64
142
  )
65
143
  end
144
+
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
+ }
172
+ end
66
173
  end
67
174
  end
68
175
  end