doorkeeper 4.2.0 → 5.6.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1119 -0
  3. data/README.md +112 -349
  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 -20
  8. data/app/controllers/doorkeeper/authorizations_controller.rb +115 -18
  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 +118 -38
  12. data/app/helpers/doorkeeper/dashboard_helper.rb +10 -6
  13. data/app/views/doorkeeper/applications/_delete_form.html.erb +4 -3
  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 +4 -2
  20. data/app/views/doorkeeper/authorizations/form_post.html.erb +15 -0
  21. data/app/views/doorkeeper/authorizations/new.html.erb +17 -11
  22. data/app/views/doorkeeper/authorized_applications/_delete_form.html.erb +1 -2
  23. data/app/views/doorkeeper/authorized_applications/index.html.erb +0 -1
  24. data/app/views/layouts/doorkeeper/admin.html.erb +16 -14
  25. data/config/locales/en.yml +36 -9
  26. data/lib/doorkeeper/config/abstract_builder.rb +28 -0
  27. data/lib/doorkeeper/config/option.rb +82 -0
  28. data/lib/doorkeeper/config/validations.rb +53 -0
  29. data/lib/doorkeeper/config.rb +551 -155
  30. data/lib/doorkeeper/engine.rb +19 -6
  31. data/lib/doorkeeper/errors.rb +55 -10
  32. data/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
  33. data/lib/doorkeeper/grant_flow/flow.rb +44 -0
  34. data/lib/doorkeeper/grant_flow/registry.rb +50 -0
  35. data/lib/doorkeeper/grant_flow.rb +45 -0
  36. data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
  37. data/lib/doorkeeper/grape/helpers.rb +24 -12
  38. data/lib/doorkeeper/helpers/controller.rb +49 -27
  39. data/lib/doorkeeper/models/access_grant_mixin.rb +100 -21
  40. data/lib/doorkeeper/models/access_token_mixin.rb +383 -75
  41. data/lib/doorkeeper/models/application_mixin.rb +72 -25
  42. data/lib/doorkeeper/models/concerns/accessible.rb +6 -0
  43. data/lib/doorkeeper/models/concerns/expirable.rb +20 -6
  44. data/lib/doorkeeper/models/concerns/expiration_time_sql_math.rb +88 -0
  45. data/lib/doorkeeper/models/concerns/orderable.rb +15 -0
  46. data/lib/doorkeeper/models/concerns/ownership.rb +4 -7
  47. data/lib/doorkeeper/models/concerns/polymorphic_resource_owner.rb +30 -0
  48. data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
  49. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  50. data/lib/doorkeeper/models/concerns/revocable.rb +12 -18
  51. data/lib/doorkeeper/models/concerns/scopes.rb +12 -2
  52. data/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
  53. data/lib/doorkeeper/oauth/authorization/code.rb +54 -12
  54. data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
  55. data/lib/doorkeeper/oauth/authorization/token.rb +72 -28
  56. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +22 -18
  57. data/lib/doorkeeper/oauth/authorization_code_request.rb +77 -17
  58. data/lib/doorkeeper/oauth/base_request.rb +67 -0
  59. data/lib/doorkeeper/oauth/base_response.rb +31 -0
  60. data/lib/doorkeeper/oauth/client/credentials.rb +23 -10
  61. data/lib/doorkeeper/oauth/client.rb +10 -12
  62. data/lib/doorkeeper/oauth/client_credentials/creator.rb +44 -4
  63. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +21 -13
  64. data/lib/doorkeeper/oauth/client_credentials/validator.rb +55 -0
  65. data/lib/doorkeeper/oauth/client_credentials_request.rb +20 -16
  66. data/lib/doorkeeper/oauth/code_request.rb +9 -13
  67. data/lib/doorkeeper/oauth/code_response.rb +28 -15
  68. data/lib/doorkeeper/oauth/error.rb +5 -3
  69. data/lib/doorkeeper/oauth/error_response.rb +43 -20
  70. data/lib/doorkeeper/oauth/forbidden_token_response.rb +11 -3
  71. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +23 -18
  72. data/lib/doorkeeper/oauth/helpers/unique_token.rb +20 -3
  73. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +53 -3
  74. data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
  75. data/lib/doorkeeper/oauth/invalid_request_response.rb +47 -0
  76. data/lib/doorkeeper/oauth/invalid_token_response.rb +31 -5
  77. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  78. data/lib/doorkeeper/oauth/password_access_token_request.rb +46 -14
  79. data/lib/doorkeeper/oauth/pre_authorization.rb +138 -28
  80. data/lib/doorkeeper/oauth/refresh_token_request.rb +74 -41
  81. data/lib/doorkeeper/oauth/scopes.rb +26 -12
  82. data/lib/doorkeeper/oauth/token.rb +25 -23
  83. data/lib/doorkeeper/oauth/token_introspection.rb +204 -0
  84. data/lib/doorkeeper/oauth/token_request.rb +9 -22
  85. data/lib/doorkeeper/oauth/token_response.rb +13 -10
  86. data/lib/doorkeeper/oauth.rb +13 -0
  87. data/lib/doorkeeper/orm/active_record/access_grant.rb +6 -4
  88. data/lib/doorkeeper/orm/active_record/access_token.rb +5 -25
  89. data/lib/doorkeeper/orm/active_record/application.rb +6 -15
  90. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +63 -0
  91. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +77 -0
  92. data/lib/doorkeeper/orm/active_record/mixins/application.rb +210 -0
  93. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +66 -0
  94. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +36 -0
  95. data/lib/doorkeeper/orm/active_record.rb +34 -12
  96. data/lib/doorkeeper/rails/helpers.rb +14 -15
  97. data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
  98. data/lib/doorkeeper/rails/routes/mapper.rb +3 -1
  99. data/lib/doorkeeper/rails/routes/mapping.rb +10 -8
  100. data/lib/doorkeeper/rails/routes/registry.rb +45 -0
  101. data/lib/doorkeeper/rails/routes.rb +50 -29
  102. data/lib/doorkeeper/rake/db.rake +40 -0
  103. data/lib/doorkeeper/rake/setup.rake +6 -0
  104. data/lib/doorkeeper/rake.rb +14 -0
  105. data/lib/doorkeeper/request/authorization_code.rb +12 -4
  106. data/lib/doorkeeper/request/client_credentials.rb +3 -3
  107. data/lib/doorkeeper/request/code.rb +1 -1
  108. data/lib/doorkeeper/request/password.rb +5 -14
  109. data/lib/doorkeeper/request/refresh_token.rb +6 -5
  110. data/lib/doorkeeper/request/strategy.rb +4 -2
  111. data/lib/doorkeeper/request/token.rb +1 -1
  112. data/lib/doorkeeper/request.rb +62 -29
  113. data/lib/doorkeeper/secret_storing/base.rb +64 -0
  114. data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  115. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  116. data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  117. data/lib/doorkeeper/server.rb +9 -19
  118. data/lib/doorkeeper/stale_records_cleaner.rb +24 -0
  119. data/lib/doorkeeper/validations.rb +5 -2
  120. data/lib/doorkeeper/version.rb +12 -1
  121. data/lib/doorkeeper.rb +180 -57
  122. data/lib/generators/doorkeeper/application_owner_generator.rb +28 -13
  123. data/lib/generators/doorkeeper/confidential_applications_generator.rb +33 -0
  124. data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
  125. data/lib/generators/doorkeeper/install_generator.rb +19 -9
  126. data/lib/generators/doorkeeper/migration_generator.rb +27 -10
  127. data/lib/generators/doorkeeper/pkce_generator.rb +33 -0
  128. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +31 -19
  129. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  130. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +9 -0
  131. data/lib/generators/doorkeeper/templates/{add_previous_refresh_token_to_access_tokens.rb → add_previous_refresh_token_to_access_tokens.rb.erb} +3 -1
  132. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +8 -0
  133. data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
  134. data/lib/generators/doorkeeper/templates/initializer.rb +436 -33
  135. data/lib/generators/doorkeeper/templates/migration.rb.erb +98 -0
  136. data/lib/generators/doorkeeper/views_generator.rb +8 -4
  137. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  138. metadata +129 -281
  139. data/.gitignore +0 -14
  140. data/.hound.yml +0 -13
  141. data/.rspec +0 -1
  142. data/.travis.yml +0 -20
  143. data/CONTRIBUTING.md +0 -47
  144. data/Gemfile +0 -14
  145. data/NEWS.md +0 -593
  146. data/RELEASING.md +0 -17
  147. data/Rakefile +0 -20
  148. data/app/validators/redirect_uri_validator.rb +0 -34
  149. data/doorkeeper.gemspec +0 -28
  150. data/lib/doorkeeper/oauth/client/methods.rb +0 -18
  151. data/lib/doorkeeper/oauth/client_credentials/validation.rb +0 -45
  152. data/lib/doorkeeper/oauth/request_concern.rb +0 -48
  153. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb +0 -7
  154. data/lib/generators/doorkeeper/templates/migration.rb +0 -68
  155. data/spec/controllers/application_metal_controller.rb +0 -10
  156. data/spec/controllers/applications_controller_spec.rb +0 -58
  157. data/spec/controllers/authorizations_controller_spec.rb +0 -189
  158. data/spec/controllers/protected_resources_controller_spec.rb +0 -300
  159. data/spec/controllers/token_info_controller_spec.rb +0 -52
  160. data/spec/controllers/tokens_controller_spec.rb +0 -88
  161. data/spec/dummy/Rakefile +0 -7
  162. data/spec/dummy/app/controllers/application_controller.rb +0 -3
  163. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +0 -7
  164. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +0 -12
  165. data/spec/dummy/app/controllers/home_controller.rb +0 -17
  166. data/spec/dummy/app/controllers/metal_controller.rb +0 -11
  167. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +0 -11
  168. data/spec/dummy/app/helpers/application_helper.rb +0 -5
  169. data/spec/dummy/app/models/user.rb +0 -5
  170. data/spec/dummy/app/views/home/index.html.erb +0 -0
  171. data/spec/dummy/app/views/layouts/application.html.erb +0 -14
  172. data/spec/dummy/config/application.rb +0 -23
  173. data/spec/dummy/config/boot.rb +0 -9
  174. data/spec/dummy/config/database.yml +0 -15
  175. data/spec/dummy/config/environment.rb +0 -5
  176. data/spec/dummy/config/environments/development.rb +0 -29
  177. data/spec/dummy/config/environments/production.rb +0 -62
  178. data/spec/dummy/config/environments/test.rb +0 -44
  179. data/spec/dummy/config/initializers/active_record_belongs_to_required_by_default.rb +0 -6
  180. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  181. data/spec/dummy/config/initializers/doorkeeper.rb +0 -96
  182. data/spec/dummy/config/initializers/secret_token.rb +0 -9
  183. data/spec/dummy/config/initializers/session_store.rb +0 -8
  184. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  185. data/spec/dummy/config/locales/doorkeeper.en.yml +0 -5
  186. data/spec/dummy/config/routes.rb +0 -52
  187. data/spec/dummy/config.ru +0 -4
  188. data/spec/dummy/db/migrate/20111122132257_create_users.rb +0 -9
  189. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +0 -5
  190. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +0 -60
  191. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +0 -7
  192. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +0 -11
  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 -28
  208. data/spec/lib/models/expirable_spec.rb +0 -51
  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 -42
  212. data/spec/lib/oauth/authorization_code_request_spec.rb +0 -80
  213. data/spec/lib/oauth/client/credentials_spec.rb +0 -47
  214. data/spec/lib/oauth/client/methods_spec.rb +0 -54
  215. data/spec/lib/oauth/client_credentials/creator_spec.rb +0 -44
  216. data/spec/lib/oauth/client_credentials/issuer_spec.rb +0 -86
  217. data/spec/lib/oauth/client_credentials/validation_spec.rb +0 -54
  218. data/spec/lib/oauth/client_credentials_integration_spec.rb +0 -27
  219. data/spec/lib/oauth/client_credentials_request_spec.rb +0 -104
  220. data/spec/lib/oauth/client_spec.rb +0 -39
  221. data/spec/lib/oauth/code_request_spec.rb +0 -45
  222. data/spec/lib/oauth/code_response_spec.rb +0 -34
  223. data/spec/lib/oauth/error_response_spec.rb +0 -61
  224. data/spec/lib/oauth/error_spec.rb +0 -23
  225. data/spec/lib/oauth/forbidden_token_response_spec.rb +0 -23
  226. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -64
  227. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -20
  228. data/spec/lib/oauth/helpers/uri_checker_spec.rb +0 -104
  229. data/spec/lib/oauth/invalid_token_response_spec.rb +0 -28
  230. data/spec/lib/oauth/password_access_token_request_spec.rb +0 -90
  231. data/spec/lib/oauth/pre_authorization_spec.rb +0 -155
  232. data/spec/lib/oauth/refresh_token_request_spec.rb +0 -154
  233. data/spec/lib/oauth/scopes_spec.rb +0 -122
  234. data/spec/lib/oauth/token_request_spec.rb +0 -98
  235. data/spec/lib/oauth/token_response_spec.rb +0 -85
  236. data/spec/lib/oauth/token_spec.rb +0 -116
  237. data/spec/lib/request/strategy_spec.rb +0 -53
  238. data/spec/lib/server_spec.rb +0 -52
  239. data/spec/models/doorkeeper/access_grant_spec.rb +0 -36
  240. data/spec/models/doorkeeper/access_token_spec.rb +0 -394
  241. data/spec/models/doorkeeper/application_spec.rb +0 -179
  242. data/spec/requests/applications/applications_request_spec.rb +0 -94
  243. data/spec/requests/applications/authorized_applications_spec.rb +0 -30
  244. data/spec/requests/endpoints/authorization_spec.rb +0 -72
  245. data/spec/requests/endpoints/token_spec.rb +0 -64
  246. data/spec/requests/flows/authorization_code_errors_spec.rb +0 -66
  247. data/spec/requests/flows/authorization_code_spec.rb +0 -156
  248. data/spec/requests/flows/client_credentials_spec.rb +0 -58
  249. data/spec/requests/flows/implicit_grant_errors_spec.rb +0 -32
  250. data/spec/requests/flows/implicit_grant_spec.rb +0 -61
  251. data/spec/requests/flows/password_spec.rb +0 -115
  252. data/spec/requests/flows/refresh_token_spec.rb +0 -174
  253. data/spec/requests/flows/revoke_token_spec.rb +0 -157
  254. data/spec/requests/flows/skip_authorization_spec.rb +0 -59
  255. data/spec/requests/protected_resources/metal_spec.rb +0 -14
  256. data/spec/requests/protected_resources/private_api_spec.rb +0 -81
  257. data/spec/routing/custom_controller_routes_spec.rb +0 -71
  258. data/spec/routing/default_routes_spec.rb +0 -35
  259. data/spec/routing/scoped_routes_spec.rb +0 -31
  260. data/spec/spec_helper.rb +0 -2
  261. data/spec/spec_helper_integration.rb +0 -59
  262. data/spec/support/dependencies/factory_girl.rb +0 -2
  263. data/spec/support/helpers/access_token_request_helper.rb +0 -11
  264. data/spec/support/helpers/authorization_request_helper.rb +0 -41
  265. data/spec/support/helpers/config_helper.rb +0 -9
  266. data/spec/support/helpers/model_helper.rb +0 -67
  267. data/spec/support/helpers/request_spec_helper.rb +0 -76
  268. data/spec/support/helpers/url_helper.rb +0 -55
  269. data/spec/support/http_method_shim.rb +0 -24
  270. data/spec/support/orm/active_record.rb +0 -3
  271. data/spec/support/shared/controllers_shared_context.rb +0 -69
  272. data/spec/support/shared/models_shared_examples.rb +0 -52
  273. data/spec/validators/redirect_uri_validator_spec.rb +0 -78
@@ -1,27 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/utils"
4
+
1
5
  module Doorkeeper
2
6
  module OAuth
3
7
  module Authorization
4
- module URIBuilder
5
- include Rack::Utils
8
+ class URIBuilder
9
+ class << self
10
+ def uri_with_query(url, parameters = {})
11
+ uri = URI.parse(url)
12
+ original_query = Rack::Utils.parse_query(uri.query)
13
+ uri.query = build_query(original_query.merge(parameters))
14
+ uri.to_s
15
+ end
6
16
 
7
- extend self
17
+ def uri_with_fragment(url, parameters = {})
18
+ uri = URI.parse(url)
19
+ uri.fragment = build_query(parameters)
20
+ uri.to_s
21
+ end
8
22
 
9
- def uri_with_query(url, parameters = {})
10
- uri = URI.parse(url)
11
- original_query = parse_query(uri.query)
12
- uri.query = build_query(original_query.merge(parameters))
13
- uri.to_s
14
- end
15
-
16
- def uri_with_fragment(url, parameters = {})
17
- uri = URI.parse(url)
18
- uri.fragment = build_query(parameters)
19
- uri.to_s
20
- end
23
+ private
21
24
 
22
- def build_query(parameters = {})
23
- parameters = parameters.reject { |_, v| v.blank? }
24
- super parameters
25
+ def build_query(parameters = {})
26
+ parameters.reject! { |_, value| value.blank? }
27
+ Rack::Utils.build_query(parameters)
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -1,21 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
- class AuthorizationCodeRequest
4
- include Validations
5
- include OAuth::RequestConcern
6
-
7
- validate :attributes, error: :invalid_request
8
- validate :client, error: :invalid_client
9
- validate :grant, error: :invalid_grant
10
- validate :redirect_uri, error: :invalid_grant
5
+ class AuthorizationCodeRequest < BaseRequest
6
+ validate :params, error: Errors::InvalidRequest
7
+ validate :client, error: Errors::InvalidClient
8
+ validate :grant, error: Errors::InvalidGrant
9
+ # @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
10
+ validate :redirect_uri, error: Errors::InvalidGrant
11
+ validate :code_verifier, error: Errors::InvalidGrant
11
12
 
12
- attr_accessor :server, :grant, :client, :redirect_uri, :access_token
13
+ attr_reader :grant, :client, :redirect_uri, :access_token, :code_verifier,
14
+ :invalid_request_reason, :missing_param
13
15
 
14
16
  def initialize(server, grant, client, parameters = {})
15
17
  @server = server
16
18
  @client = client
17
19
  @grant = grant
20
+ @grant_type = Doorkeeper::OAuth::AUTHORIZATION_CODE
18
21
  @redirect_uri = parameters[:redirect_uri]
22
+ @code_verifier = parameters[:code_verifier]
19
23
  end
20
24
 
21
25
  private
@@ -26,28 +30,84 @@ module Doorkeeper
26
30
  raise Errors::InvalidGrantReuse if grant.revoked?
27
31
 
28
32
  grant.revoke
29
- find_or_create_access_token(grant.application,
30
- grant.resource_owner_id,
31
- grant.scopes,
32
- server)
33
+
34
+ find_or_create_access_token(
35
+ client,
36
+ resource_owner,
37
+ grant.scopes,
38
+ custom_token_attributes_with_data,
39
+ server,
40
+ )
41
+ end
42
+
43
+ super
44
+ end
45
+
46
+ def resource_owner
47
+ if Doorkeeper.config.polymorphic_resource_owner?
48
+ grant.resource_owner
49
+ else
50
+ grant.resource_owner_id
33
51
  end
34
52
  end
35
53
 
36
- def validate_attributes
37
- redirect_uri.present?
54
+ def pkce_supported?
55
+ Doorkeeper.config.access_grant_model.pkce_supported?
56
+ end
57
+
58
+ def validate_params
59
+ @missing_param =
60
+ if grant&.uses_pkce? && code_verifier.blank?
61
+ :code_verifier
62
+ elsif redirect_uri.blank?
63
+ :redirect_uri
64
+ end
65
+
66
+ @missing_param.nil?
38
67
  end
39
68
 
40
69
  def validate_client
41
- !!client
70
+ client.present?
42
71
  end
43
72
 
44
73
  def validate_grant
45
74
  return false unless grant && grant.application_id == client.id
75
+
46
76
  grant.accessible?
47
77
  end
48
78
 
49
79
  def validate_redirect_uri
50
- grant.redirect_uri == redirect_uri
80
+ Helpers::URIChecker.valid_for_authorization?(
81
+ redirect_uri,
82
+ grant.redirect_uri,
83
+ )
84
+ end
85
+
86
+ # if either side (server or client) request PKCE, check the verifier
87
+ # against the DB - if PKCE is supported
88
+ def validate_code_verifier
89
+ return true unless pkce_supported?
90
+ return grant.code_challenge.blank? if code_verifier.blank?
91
+
92
+ if grant.code_challenge_method == "S256"
93
+ grant.code_challenge == generate_code_challenge(code_verifier)
94
+ elsif grant.code_challenge_method == "plain"
95
+ grant.code_challenge == code_verifier
96
+ else
97
+ false
98
+ end
99
+ end
100
+
101
+ def generate_code_challenge(code_verifier)
102
+ Doorkeeper.config.access_grant_model.generate_code_challenge(code_verifier)
103
+ end
104
+
105
+ def custom_token_attributes_with_data
106
+ grant
107
+ .attributes
108
+ .with_indifferent_access
109
+ .slice(*Doorkeeper.config.custom_access_token_attributes)
110
+ .symbolize_keys
51
111
  end
52
112
  end
53
113
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ class BaseRequest
6
+ include Validations
7
+
8
+ attr_reader :grant_type, :server
9
+
10
+ delegate :default_scopes, to: :server
11
+
12
+ def authorize
13
+ if valid?
14
+ before_successful_response
15
+ @response = TokenResponse.new(access_token)
16
+ after_successful_response
17
+ @response
18
+ elsif error == Errors::InvalidRequest
19
+ @response = InvalidRequestResponse.from_request(self)
20
+ else
21
+ @response = ErrorResponse.from_request(self)
22
+ end
23
+ end
24
+
25
+ def scopes
26
+ @scopes ||= build_scopes
27
+ end
28
+
29
+ def find_or_create_access_token(client, resource_owner, scopes, custom_attributes, server)
30
+ context = Authorization::Token.build_context(client, grant_type, scopes, resource_owner)
31
+ application = client.is_a?(Doorkeeper.config.application_model) ? client : client&.application
32
+
33
+ token_attributes = {
34
+ application: application,
35
+ resource_owner: resource_owner,
36
+ scopes: scopes,
37
+ expires_in: Authorization::Token.access_token_expires_in(server, context),
38
+ use_refresh_token: Authorization::Token.refresh_token_enabled?(server, context),
39
+ }
40
+
41
+ @access_token =
42
+ Doorkeeper.config.access_token_model.find_or_create_for(**token_attributes.merge(custom_attributes))
43
+ end
44
+
45
+ def before_successful_response
46
+ Doorkeeper.config.before_successful_strategy_response.call(self)
47
+ end
48
+
49
+ def after_successful_response
50
+ Doorkeeper.config.after_successful_strategy_response.call(self, @response)
51
+ end
52
+
53
+ private
54
+
55
+ def build_scopes
56
+ if @original_scopes.present?
57
+ OAuth::Scopes.from_string(@original_scopes)
58
+ else
59
+ client_scopes = @client&.scopes
60
+ return default_scopes if client_scopes.blank?
61
+
62
+ default_scopes & client_scopes
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ class BaseResponse
6
+ def body
7
+ {}
8
+ end
9
+
10
+ def description
11
+ ""
12
+ end
13
+
14
+ def headers
15
+ {}
16
+ end
17
+
18
+ def redirectable?
19
+ false
20
+ end
21
+
22
+ def redirect_uri
23
+ ""
24
+ end
25
+
26
+ def status
27
+ :ok
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,20 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class Client
4
- class Credentials < Struct.new(:uid, :secret)
5
- extend Methods
6
+ Credentials = Struct.new(:uid, :secret) do
7
+ class << self
8
+ def from_request(request, *credentials_methods)
9
+ credentials_methods.inject(nil) do |_, method|
10
+ method = self.method(method) if method.is_a?(Symbol)
11
+ credentials = Credentials.new(*method.call(request))
12
+ break credentials if credentials.present?
13
+ end
14
+ end
6
15
 
7
- def self.from_request(request, *credentials_methods)
8
- credentials_methods.inject(nil) do |credentials, method|
9
- method = self.method(method) if method.is_a?(Symbol)
10
- credentials = Credentials.new(*method.call(request))
11
- break credentials unless credentials.blank?
16
+ def from_params(request)
17
+ request.parameters.values_at(:client_id, :client_secret)
12
18
  end
13
- end
14
19
 
15
- def blank?
16
- uid.blank? || secret.blank?
20
+ def from_basic(request)
21
+ authorization = request.authorization
22
+ if authorization.present? && authorization =~ /^Basic (.*)/m
23
+ Base64.decode64(Regexp.last_match(1)).split(/:/, 2)
24
+ end
25
+ end
17
26
  end
27
+
28
+ # Public clients may have their secret blank, but "credentials" are
29
+ # still present
30
+ delegate :blank?, to: :uid
18
31
  end
19
32
  end
20
33
  end
@@ -1,10 +1,9 @@
1
- require 'doorkeeper/oauth/client/methods'
2
- require 'doorkeeper/oauth/client/credentials'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Doorkeeper
5
4
  module OAuth
6
5
  class Client
7
- attr_accessor :application
6
+ attr_reader :application
8
7
 
9
8
  delegate :id, :name, :uid, :redirect_uri, :scopes, to: :@application
10
9
 
@@ -12,18 +11,17 @@ module Doorkeeper
12
11
  @application = application
13
12
  end
14
13
 
15
- def self.find(uid, method = Application.method(:by_uid))
16
- if application = method.call(uid)
17
- new(application)
18
- end
14
+ def self.find(uid, method = Doorkeeper.config.application_model.method(:by_uid))
15
+ return unless (application = method.call(uid))
16
+
17
+ new(application)
19
18
  end
20
19
 
21
- def self.authenticate(credentials, method = Application.method(:by_uid_and_secret))
22
- return false if credentials.blank?
20
+ def self.authenticate(credentials, method = Doorkeeper.config.application_model.method(:by_uid_and_secret))
21
+ return if credentials.blank?
22
+ return unless (application = method.call(credentials.uid, credentials.secret))
23
23
 
24
- if application = method.call(credentials.uid, credentials.secret)
25
- new(application)
26
- end
24
+ new(application)
27
25
  end
28
26
  end
29
27
  end
@@ -1,11 +1,51 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
- class ClientCredentialsRequest
5
+ module ClientCredentials
4
6
  class Creator
5
7
  def call(client, scopes, attributes = {})
6
- AccessToken.find_or_create_for(
7
- client, nil, scopes, attributes[:expires_in],
8
- attributes[:use_refresh_token])
8
+ existing_token = nil
9
+
10
+ if lookup_existing_token?
11
+ existing_token = find_active_existing_token_for(client, scopes)
12
+ return existing_token if Doorkeeper.config.reuse_access_token && existing_token&.reusable?
13
+ end
14
+
15
+ with_revocation(existing_token: existing_token) do
16
+ application = client.is_a?(Doorkeeper.config.application_model) ? client : client&.application
17
+ Doorkeeper.config.access_token_model.create_for(
18
+ application: application,
19
+ resource_owner: nil,
20
+ scopes: scopes,
21
+ **attributes,
22
+ )
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def with_revocation(existing_token:)
29
+ if existing_token && Doorkeeper.config.revoke_previous_client_credentials_token?
30
+ existing_token.with_lock do
31
+ raise Errors::DoorkeeperError, :invalid_token_reuse if existing_token.revoked?
32
+
33
+ existing_token.revoke
34
+
35
+ yield
36
+ end
37
+ else
38
+ yield
39
+ end
40
+ end
41
+
42
+ def lookup_existing_token?
43
+ Doorkeeper.config.reuse_access_token ||
44
+ Doorkeeper.config.revoke_previous_client_credentials_token?
45
+ end
46
+
47
+ def find_active_existing_token_for(client, scopes)
48
+ Doorkeeper.config.access_token_model.matching_token_for(client, nil, scopes, include_expired: false)
9
49
  end
10
50
  end
11
51
  end
@@ -1,37 +1,45 @@
1
- require 'doorkeeper/oauth/client_credentials/validation'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Doorkeeper
4
4
  module OAuth
5
- class ClientCredentialsRequest
5
+ module ClientCredentials
6
6
  class Issuer
7
- attr_accessor :token, :validation, :error
7
+ attr_reader :token, :validator, :error
8
8
 
9
- def initialize(server, validation)
9
+ def initialize(server, validator)
10
10
  @server = server
11
- @validation = validation
11
+ @validator = validator
12
12
  end
13
13
 
14
- def create(client, scopes, creator = Creator.new)
15
- if validation.valid?
16
- @token = create_token(client, scopes, creator)
17
- @error = :server_error unless @token
14
+ def create(client, scopes, attributes = {}, creator = Creator.new)
15
+ if validator.valid?
16
+ @token = create_token(client, scopes, attributes, creator)
17
+ @error = Errors::ServerError unless @token
18
18
  else
19
19
  @token = false
20
- @error = validation.error
20
+ @error = validator.error
21
21
  end
22
+
22
23
  @token
23
24
  end
24
25
 
25
26
  private
26
27
 
27
- def create_token(client, scopes, creator)
28
- ttl = Authorization::Token.access_token_expires_in(@server, client)
28
+ def create_token(client, scopes, attributes, creator)
29
+ context = Authorization::Token.build_context(
30
+ client,
31
+ Doorkeeper::OAuth::CLIENT_CREDENTIALS,
32
+ scopes,
33
+ nil,
34
+ )
35
+ ttl = Authorization::Token.access_token_expires_in(@server, context)
29
36
 
30
37
  creator.call(
31
38
  client,
32
39
  scopes,
33
40
  use_refresh_token: false,
34
- expires_in: ttl
41
+ expires_in: ttl,
42
+ **attributes
35
43
  )
36
44
  end
37
45
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ module ClientCredentials
6
+ class Validator
7
+ include Validations
8
+ include OAuth::Helpers
9
+
10
+ validate :client, error: Errors::InvalidClient
11
+ validate :client_supports_grant_flow, error: Errors::UnauthorizedClient
12
+ validate :scopes, error: Errors::InvalidScope
13
+
14
+ def initialize(server, request)
15
+ @server = server
16
+ @request = request
17
+ @client = request.client
18
+
19
+ validate
20
+ end
21
+
22
+ private
23
+
24
+ def validate_client
25
+ @client.present?
26
+ end
27
+
28
+ def validate_client_supports_grant_flow
29
+ return if @client.blank?
30
+
31
+ Doorkeeper.config.allow_grant_flow_for_client?(
32
+ Doorkeeper::OAuth::CLIENT_CREDENTIALS,
33
+ @client.application,
34
+ )
35
+ end
36
+
37
+ def validate_scopes
38
+ application_scopes = if @client.present?
39
+ @client.application.scopes
40
+ else
41
+ ""
42
+ end
43
+ return true if @request.scopes.blank? && application_scopes.blank?
44
+
45
+ ScopeChecker.valid?(
46
+ scope_str: @request.scopes.to_s,
47
+ server_scopes: @server.scopes,
48
+ app_scopes: application_scopes,
49
+ grant_type: Doorkeeper::OAuth::CLIENT_CREDENTIALS,
50
+ )
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,40 +1,44 @@
1
- require 'doorkeeper/oauth/client_credentials/creator'
2
- require 'doorkeeper/oauth/client_credentials/issuer'
3
- require 'doorkeeper/oauth/client_credentials/validation'
1
+ # frozen_string_literal: true
4
2
 
5
3
  module Doorkeeper
6
4
  module OAuth
7
- class ClientCredentialsRequest
8
- include Validations
9
- include OAuth::RequestConcern
5
+ class ClientCredentialsRequest < BaseRequest
6
+ attr_reader :client, :original_scopes, :parameters, :response
10
7
 
11
- attr_accessor :server, :client, :original_scopes
12
- attr_reader :response
13
- attr_writer :issuer
14
-
15
- alias_method :error_response, :response
8
+ alias error_response response
16
9
 
17
10
  delegate :error, to: :issuer
18
11
 
19
- def issuer
20
- @issuer ||= Issuer.new(server, Validation.new(server, self))
21
- end
22
-
23
12
  def initialize(server, client, parameters = {})
24
13
  @client = client
25
14
  @server = server
26
15
  @response = nil
27
16
  @original_scopes = parameters[:scope]
17
+ @parameters = parameters.except(:scope)
28
18
  end
29
19
 
30
20
  def access_token
31
21
  issuer.token
32
22
  end
33
23
 
24
+ def issuer
25
+ @issuer ||= ClientCredentials::Issuer.new(
26
+ server,
27
+ ClientCredentials::Validator.new(server, self),
28
+ )
29
+ end
30
+
34
31
  private
35
32
 
36
33
  def valid?
37
- issuer.create(client, scopes)
34
+ issuer.create(client, scopes, custom_token_attributes_with_data)
35
+ end
36
+
37
+ def custom_token_attributes_with_data
38
+ parameters
39
+ .with_indifferent_access
40
+ .slice(*Doorkeeper.config.custom_access_token_attributes)
41
+ .symbolize_keys
38
42
  end
39
43
  end
40
44
  end
@@ -1,28 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class CodeRequest
4
- attr_accessor :pre_auth, :resource_owner, :client
6
+ attr_reader :pre_auth, :resource_owner
5
7
 
6
8
  def initialize(pre_auth, resource_owner)
7
- @pre_auth = pre_auth
8
- @client = pre_auth.client
9
+ @pre_auth = pre_auth
9
10
  @resource_owner = resource_owner
10
11
  end
11
12
 
12
13
  def authorize
13
- if pre_auth.authorizable?
14
- auth = Authorization::Code.new(pre_auth, resource_owner)
15
- auth.issue_token
16
- @response = CodeResponse.new pre_auth, auth
17
- else
18
- @response = ErrorResponse.from_request pre_auth
19
- end
14
+ auth = Authorization::Code.new(pre_auth, resource_owner)
15
+ auth.issue_token!
16
+ CodeResponse.new(pre_auth, auth, response_on_fragment: pre_auth.response_mode == "fragment")
20
17
  end
21
18
 
22
19
  def deny
23
- pre_auth.error = :access_denied
24
- ErrorResponse.from_request pre_auth,
25
- redirect_uri: pre_auth.redirect_uri
20
+ pre_auth.error = Errors::AccessDenied
21
+ pre_auth.error_response
26
22
  end
27
23
  end
28
24
  end