doorkeeper 3.1.0 → 5.6.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 (270) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1079 -0
  3. data/README.md +114 -326
  4. data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
  5. data/app/controllers/doorkeeper/application_controller.rb +7 -6
  6. data/app/controllers/doorkeeper/application_metal_controller.rb +9 -12
  7. data/app/controllers/doorkeeper/applications_controller.rb +66 -21
  8. data/app/controllers/doorkeeper/authorizations_controller.rb +100 -18
  9. data/app/controllers/doorkeeper/authorized_applications_controller.rb +23 -4
  10. data/app/controllers/doorkeeper/token_info_controller.rb +16 -4
  11. data/app/controllers/doorkeeper/tokens_controller.rb +138 -22
  12. data/app/helpers/doorkeeper/dashboard_helper.rb +15 -9
  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 +1 -1
  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 +37 -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 +602 -142
  30. data/lib/doorkeeper/engine.rb +22 -7
  31. data/lib/doorkeeper/errors.rb +37 -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 +99 -16
  40. data/lib/doorkeeper/models/access_token_mixin.rb +386 -77
  41. data/lib/doorkeeper/models/application_mixin.rb +73 -30
  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 -2
  47. data/lib/doorkeeper/models/concerns/resource_ownerable.rb +47 -0
  48. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  49. data/lib/doorkeeper/models/concerns/revocable.rb +13 -2
  50. data/lib/doorkeeper/models/concerns/scopes.rb +12 -2
  51. data/lib/doorkeeper/models/concerns/secret_storable.rb +106 -0
  52. data/lib/doorkeeper/oauth/authorization/code.rb +48 -12
  53. data/lib/doorkeeper/oauth/authorization/context.rb +17 -0
  54. data/lib/doorkeeper/oauth/authorization/token.rb +72 -28
  55. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +22 -18
  56. data/lib/doorkeeper/oauth/authorization_code_request.rb +64 -14
  57. data/lib/doorkeeper/oauth/base_request.rb +66 -0
  58. data/lib/doorkeeper/oauth/base_response.rb +31 -0
  59. data/lib/doorkeeper/oauth/client/credentials.rb +23 -10
  60. data/lib/doorkeeper/oauth/client.rb +10 -12
  61. data/lib/doorkeeper/oauth/client_credentials/creator.rb +48 -4
  62. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +17 -9
  63. data/lib/doorkeeper/oauth/client_credentials/validator.rb +55 -0
  64. data/lib/doorkeeper/oauth/client_credentials_request.rb +14 -15
  65. data/lib/doorkeeper/oauth/code_request.rb +8 -12
  66. data/lib/doorkeeper/oauth/code_response.rb +31 -19
  67. data/lib/doorkeeper/oauth/error.rb +5 -3
  68. data/lib/doorkeeper/oauth/error_response.rb +41 -20
  69. data/lib/doorkeeper/oauth/forbidden_token_response.rb +11 -3
  70. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +24 -19
  71. data/lib/doorkeeper/oauth/helpers/unique_token.rb +20 -3
  72. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +55 -4
  73. data/lib/doorkeeper/oauth/hooks/context.rb +21 -0
  74. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  75. data/lib/doorkeeper/oauth/invalid_token_response.rb +31 -5
  76. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  77. data/lib/doorkeeper/oauth/password_access_token_request.rb +46 -18
  78. data/lib/doorkeeper/oauth/pre_authorization.rb +135 -26
  79. data/lib/doorkeeper/oauth/refresh_token_request.rb +67 -30
  80. data/lib/doorkeeper/oauth/scopes.rb +26 -12
  81. data/lib/doorkeeper/oauth/token.rb +28 -25
  82. data/lib/doorkeeper/oauth/token_introspection.rb +202 -0
  83. data/lib/doorkeeper/oauth/token_request.rb +8 -21
  84. data/lib/doorkeeper/oauth/token_response.rb +14 -10
  85. data/lib/doorkeeper/oauth.rb +13 -0
  86. data/lib/doorkeeper/orm/active_record/access_grant.rb +6 -4
  87. data/lib/doorkeeper/orm/active_record/access_token.rb +5 -17
  88. data/lib/doorkeeper/orm/active_record/application.rb +6 -20
  89. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +69 -0
  90. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +81 -0
  91. data/lib/doorkeeper/orm/active_record/mixins/application.rb +214 -0
  92. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +66 -0
  93. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +33 -0
  94. data/lib/doorkeeper/orm/active_record.rb +36 -26
  95. data/lib/doorkeeper/rails/helpers.rb +14 -15
  96. data/lib/doorkeeper/rails/routes/abstract_router.rb +35 -0
  97. data/lib/doorkeeper/rails/routes/mapper.rb +4 -2
  98. data/lib/doorkeeper/rails/routes/mapping.rb +10 -8
  99. data/lib/doorkeeper/rails/routes/registry.rb +45 -0
  100. data/lib/doorkeeper/rails/routes.rb +45 -28
  101. data/lib/doorkeeper/rake/db.rake +40 -0
  102. data/lib/doorkeeper/rake/setup.rake +6 -0
  103. data/lib/doorkeeper/rake.rb +14 -0
  104. data/lib/doorkeeper/request/authorization_code.rb +12 -4
  105. data/lib/doorkeeper/request/client_credentials.rb +3 -3
  106. data/lib/doorkeeper/request/code.rb +1 -1
  107. data/lib/doorkeeper/request/password.rb +5 -4
  108. data/lib/doorkeeper/request/refresh_token.rb +6 -5
  109. data/lib/doorkeeper/request/strategy.rb +4 -2
  110. data/lib/doorkeeper/request/token.rb +1 -1
  111. data/lib/doorkeeper/request.rb +62 -29
  112. data/lib/doorkeeper/secret_storing/base.rb +64 -0
  113. data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  114. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  115. data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  116. data/lib/doorkeeper/server.rb +9 -19
  117. data/lib/doorkeeper/stale_records_cleaner.rb +24 -0
  118. data/lib/doorkeeper/validations.rb +5 -2
  119. data/lib/doorkeeper/version.rb +12 -1
  120. data/lib/doorkeeper.rb +112 -56
  121. data/lib/generators/doorkeeper/application_owner_generator.rb +28 -13
  122. data/lib/generators/doorkeeper/confidential_applications_generator.rb +33 -0
  123. data/lib/generators/doorkeeper/enable_polymorphic_resource_owner_generator.rb +39 -0
  124. data/lib/generators/doorkeeper/install_generator.rb +19 -9
  125. data/lib/generators/doorkeeper/migration_generator.rb +27 -10
  126. data/lib/generators/doorkeeper/pkce_generator.rb +33 -0
  127. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +41 -0
  128. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  129. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +9 -0
  130. data/lib/generators/doorkeeper/templates/add_previous_refresh_token_to_access_tokens.rb.erb +13 -0
  131. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +8 -0
  132. data/lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb +17 -0
  133. data/lib/generators/doorkeeper/templates/initializer.rb +417 -32
  134. data/lib/generators/doorkeeper/templates/migration.rb.erb +88 -0
  135. data/lib/generators/doorkeeper/views_generator.rb +8 -4
  136. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  137. metadata +163 -280
  138. data/.gitignore +0 -14
  139. data/.hound.yml +0 -13
  140. data/.rspec +0 -1
  141. data/.travis.yml +0 -22
  142. data/CONTRIBUTING.md +0 -45
  143. data/Gemfile +0 -10
  144. data/NEWS.md +0 -525
  145. data/RELEASING.md +0 -17
  146. data/Rakefile +0 -20
  147. data/app/validators/redirect_uri_validator.rb +0 -34
  148. data/doorkeeper.gemspec +0 -27
  149. data/lib/doorkeeper/oauth/client/methods.rb +0 -18
  150. data/lib/doorkeeper/oauth/client_credentials/validation.rb +0 -45
  151. data/lib/doorkeeper/oauth/request_concern.rb +0 -48
  152. data/lib/generators/doorkeeper/application_scopes_generator.rb +0 -34
  153. data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb +0 -7
  154. data/lib/generators/doorkeeper/templates/add_scopes_to_oauth_applications.rb +0 -5
  155. data/lib/generators/doorkeeper/templates/migration.rb +0 -50
  156. data/spec/controllers/applications_controller_spec.rb +0 -58
  157. data/spec/controllers/authorizations_controller_spec.rb +0 -203
  158. data/spec/controllers/protected_resources_controller_spec.rb +0 -271
  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 -9
  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 -57
  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 -55
  179. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  180. data/spec/dummy/config/initializers/doorkeeper.rb +0 -96
  181. data/spec/dummy/config/initializers/secret_token.rb +0 -9
  182. data/spec/dummy/config/initializers/session_store.rb +0 -8
  183. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  184. data/spec/dummy/config/locales/doorkeeper.en.yml +0 -5
  185. data/spec/dummy/config/routes.rb +0 -52
  186. data/spec/dummy/config.ru +0 -4
  187. data/spec/dummy/db/migrate/20111122132257_create_users.rb +0 -9
  188. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +0 -5
  189. data/spec/dummy/db/migrate/20130902165751_create_doorkeeper_tables.rb +0 -41
  190. data/spec/dummy/db/migrate/20130902175349_add_owner_to_application.rb +0 -7
  191. data/spec/dummy/db/migrate/20141209001746_add_scopes_to_oauth_applications.rb +0 -5
  192. data/spec/dummy/db/schema.rb +0 -66
  193. data/spec/dummy/public/404.html +0 -26
  194. data/spec/dummy/public/422.html +0 -26
  195. data/spec/dummy/public/500.html +0 -26
  196. data/spec/dummy/public/favicon.ico +0 -0
  197. data/spec/dummy/script/rails +0 -6
  198. data/spec/factories.rb +0 -26
  199. data/spec/generators/application_owner_generator_spec.rb +0 -22
  200. data/spec/generators/install_generator_spec.rb +0 -31
  201. data/spec/generators/migration_generator_spec.rb +0 -20
  202. data/spec/generators/templates/routes.rb +0 -3
  203. data/spec/generators/views_generator_spec.rb +0 -27
  204. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +0 -24
  205. data/spec/lib/config_spec.rb +0 -317
  206. data/spec/lib/doorkeeper_spec.rb +0 -28
  207. data/spec/lib/models/expirable_spec.rb +0 -51
  208. data/spec/lib/models/revocable_spec.rb +0 -36
  209. data/spec/lib/models/scopes_spec.rb +0 -43
  210. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -42
  211. data/spec/lib/oauth/authorization_code_request_spec.rb +0 -80
  212. data/spec/lib/oauth/client/credentials_spec.rb +0 -47
  213. data/spec/lib/oauth/client/methods_spec.rb +0 -54
  214. data/spec/lib/oauth/client_credentials/creator_spec.rb +0 -44
  215. data/spec/lib/oauth/client_credentials/issuer_spec.rb +0 -86
  216. data/spec/lib/oauth/client_credentials/validation_spec.rb +0 -54
  217. data/spec/lib/oauth/client_credentials_integration_spec.rb +0 -27
  218. data/spec/lib/oauth/client_credentials_request_spec.rb +0 -104
  219. data/spec/lib/oauth/client_spec.rb +0 -39
  220. data/spec/lib/oauth/code_request_spec.rb +0 -45
  221. data/spec/lib/oauth/error_response_spec.rb +0 -61
  222. data/spec/lib/oauth/error_spec.rb +0 -23
  223. data/spec/lib/oauth/forbidden_token_response_spec.rb +0 -23
  224. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -64
  225. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -20
  226. data/spec/lib/oauth/helpers/uri_checker_spec.rb +0 -104
  227. data/spec/lib/oauth/invalid_token_response_spec.rb +0 -28
  228. data/spec/lib/oauth/password_access_token_request_spec.rb +0 -90
  229. data/spec/lib/oauth/pre_authorization_spec.rb +0 -155
  230. data/spec/lib/oauth/refresh_token_request_spec.rb +0 -123
  231. data/spec/lib/oauth/scopes_spec.rb +0 -123
  232. data/spec/lib/oauth/token_request_spec.rb +0 -98
  233. data/spec/lib/oauth/token_response_spec.rb +0 -85
  234. data/spec/lib/oauth/token_spec.rb +0 -109
  235. data/spec/lib/request/strategy_spec.rb +0 -53
  236. data/spec/lib/server_spec.rb +0 -52
  237. data/spec/models/doorkeeper/access_grant_spec.rb +0 -36
  238. data/spec/models/doorkeeper/access_token_spec.rb +0 -350
  239. data/spec/models/doorkeeper/application_spec.rb +0 -187
  240. data/spec/requests/applications/applications_request_spec.rb +0 -94
  241. data/spec/requests/applications/authorized_applications_spec.rb +0 -30
  242. data/spec/requests/endpoints/authorization_spec.rb +0 -72
  243. data/spec/requests/endpoints/token_spec.rb +0 -64
  244. data/spec/requests/flows/authorization_code_errors_spec.rb +0 -66
  245. data/spec/requests/flows/authorization_code_spec.rb +0 -156
  246. data/spec/requests/flows/client_credentials_spec.rb +0 -58
  247. data/spec/requests/flows/implicit_grant_errors_spec.rb +0 -32
  248. data/spec/requests/flows/implicit_grant_spec.rb +0 -61
  249. data/spec/requests/flows/password_spec.rb +0 -94
  250. data/spec/requests/flows/refresh_token_spec.rb +0 -104
  251. data/spec/requests/flows/revoke_token_spec.rb +0 -143
  252. data/spec/requests/flows/skip_authorization_spec.rb +0 -59
  253. data/spec/requests/protected_resources/metal_spec.rb +0 -14
  254. data/spec/requests/protected_resources/private_api_spec.rb +0 -81
  255. data/spec/routing/custom_controller_routes_spec.rb +0 -71
  256. data/spec/routing/default_routes_spec.rb +0 -35
  257. data/spec/routing/scoped_routes_spec.rb +0 -31
  258. data/spec/spec_helper.rb +0 -2
  259. data/spec/spec_helper_integration.rb +0 -56
  260. data/spec/support/dependencies/factory_girl.rb +0 -2
  261. data/spec/support/helpers/access_token_request_helper.rb +0 -11
  262. data/spec/support/helpers/authorization_request_helper.rb +0 -41
  263. data/spec/support/helpers/config_helper.rb +0 -9
  264. data/spec/support/helpers/model_helper.rb +0 -45
  265. data/spec/support/helpers/request_spec_helper.rb +0 -76
  266. data/spec/support/helpers/url_helper.rb +0 -55
  267. data/spec/support/orm/active_record.rb +0 -3
  268. data/spec/support/shared/controllers_shared_context.rb +0 -60
  269. data/spec/support/shared/models_shared_examples.rb +0 -52
  270. data/spec/validators/redirect_uri_validator_spec.rb +0 -78
@@ -1,12 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
- class ErrorResponse
4
- include OAuth::Authorization::URIBuilder
5
+ class ErrorResponse < BaseResponse
5
6
  include OAuth::Helpers
6
7
 
8
+ NON_REDIRECTABLE_STATES = %i[invalid_redirect_uri invalid_client unauthorized_client].freeze
9
+
7
10
  def self.from_request(request, attributes = {})
8
- state = request.state if request.respond_to?(:state)
9
- new(attributes.merge(name: request.error, state: state))
11
+ new(
12
+ attributes.merge(
13
+ name: request.error,
14
+ state: request.try(:state),
15
+ redirect_uri: request.try(:redirect_uri),
16
+ ),
17
+ )
10
18
  end
11
19
 
12
20
  delegate :name, :description, :state, to: :@error
@@ -21,44 +29,57 @@ module Doorkeeper
21
29
  {
22
30
  error: name,
23
31
  error_description: description,
24
- state: state
32
+ state: state,
25
33
  }.reject { |_, v| v.blank? }
26
34
  end
27
35
 
28
36
  def status
29
- :unauthorized
37
+ if name == :invalid_client || name == :unauthorized_client
38
+ :unauthorized
39
+ else
40
+ :bad_request
41
+ end
30
42
  end
31
43
 
32
44
  def redirectable?
33
- name != :invalid_redirect_uri && name != :invalid_client &&
34
- !URIChecker.native_uri?(@redirect_uri)
45
+ !NON_REDIRECTABLE_STATES.include?(name) && !URIChecker.oob_uri?(@redirect_uri)
35
46
  end
36
47
 
37
48
  def redirect_uri
38
49
  if @response_on_fragment
39
- uri_with_fragment @redirect_uri, body
50
+ Authorization::URIBuilder.uri_with_fragment(@redirect_uri, body)
40
51
  else
41
- uri_with_query @redirect_uri, body
52
+ Authorization::URIBuilder.uri_with_query(@redirect_uri, body)
42
53
  end
43
54
  end
44
55
 
45
- def authenticate_info
46
- %(Bearer realm="#{realm}", error="#{name}", error_description="#{description}")
56
+ def headers
57
+ {
58
+ "Cache-Control" => "no-store",
59
+ "Pragma" => "no-cache",
60
+ "Content-Type" => "application/json; charset=utf-8",
61
+ "WWW-Authenticate" => authenticate_info,
62
+ }
47
63
  end
48
64
 
49
- def headers
50
- { 'Cache-Control' => 'no-store',
51
- 'Pragma' => 'no-cache',
52
- 'Content-Type' => 'application/json; charset=utf-8',
53
- 'WWW-Authenticate' => authenticate_info }
65
+ def raise_exception!
66
+ raise exception_class.new(self), description
54
67
  end
55
68
 
56
69
  protected
57
70
 
58
- delegate :realm, to: :configuration
71
+ def realm
72
+ Doorkeeper.config.realm
73
+ end
74
+
75
+ def exception_class
76
+ raise NotImplementedError, "error response must define #exception_class"
77
+ end
59
78
 
60
- def configuration
61
- Doorkeeper.configuration
79
+ private
80
+
81
+ def authenticate_info
82
+ %(Bearer realm="#{realm}", error="#{name}", error_description="#{description}")
62
83
  end
63
84
  end
64
85
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class ForbiddenTokenResponse < ErrorResponse
@@ -16,13 +18,19 @@ module Doorkeeper
16
18
 
17
19
  def headers
18
20
  headers = super
19
- headers.delete 'WWW-Authenticate'
21
+ headers.delete "WWW-Authenticate"
20
22
  headers
21
23
  end
22
24
 
23
25
  def description
24
- scope = { scope: [:doorkeeper, :scopes] }
25
- @description ||= @scopes.map { |r| I18n.translate r, scope }.join('\n')
26
+ @description ||= I18n.t("doorkeeper.errors.messages.forbidden_token.missing_scope",
27
+ oauth_scopes: @scopes.map(&:to_s).join(" "),)
28
+ end
29
+
30
+ protected
31
+
32
+ def exception_class
33
+ Doorkeeper::Errors::TokenForbidden
26
34
  end
27
35
  end
28
36
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  module Helpers
@@ -5,39 +7,42 @@ module Doorkeeper
5
7
  class Validator
6
8
  attr_reader :parsed_scopes, :scope_str
7
9
 
8
- def initialize(scope_str, server_scopes, application_scopes)
10
+ def initialize(scope_str, server_scopes, app_scopes, grant_type)
9
11
  @parsed_scopes = OAuth::Scopes.from_string(scope_str)
10
12
  @scope_str = scope_str
11
- @valid_scopes = valid_scopes(server_scopes, application_scopes)
13
+ @valid_scopes = valid_scopes(server_scopes, app_scopes)
14
+
15
+ @scopes_by_grant_type = Doorkeeper.config.scopes_by_grant_type[grant_type.to_sym] if grant_type
12
16
  end
13
17
 
14
18
  def valid?
15
19
  scope_str.present? &&
16
- scope_str !~ /[\n|\r|\t]/ &&
17
- @valid_scopes.has_scopes?(parsed_scopes)
18
- end
19
-
20
- def match?
21
- valid? && parsed_scopes.has_scopes?(@valid_scopes)
20
+ scope_str !~ /[\n\r\t]/ &&
21
+ @valid_scopes.has_scopes?(parsed_scopes) &&
22
+ permitted_to_grant_type?
22
23
  end
23
24
 
24
25
  private
25
26
 
26
- def valid_scopes(server_scopes, application_scopes)
27
- if application_scopes.present?
28
- application_scopes
29
- else
30
- server_scopes
31
- end
27
+ def valid_scopes(server_scopes, app_scopes)
28
+ app_scopes.presence || server_scopes
32
29
  end
33
- end
34
30
 
35
- def self.valid?(scope_str, server_scopes, application_scopes = nil)
36
- Validator.new(scope_str, server_scopes, application_scopes).valid?
31
+ def permitted_to_grant_type?
32
+ return true unless @scopes_by_grant_type
33
+
34
+ OAuth::Scopes.from_array(@scopes_by_grant_type)
35
+ .has_scopes?(parsed_scopes)
36
+ end
37
37
  end
38
38
 
39
- def self.match?(scope_str, server_scopes, application_scopes = nil)
40
- Validator.new(scope_str, server_scopes, application_scopes).match?
39
+ def self.valid?(scope_str:, server_scopes:, app_scopes: nil, grant_type: nil)
40
+ Validator.new(
41
+ scope_str,
42
+ server_scopes,
43
+ app_scopes,
44
+ grant_type,
45
+ ).valid?
41
46
  end
42
47
  end
43
48
  end
@@ -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,20 +1,49 @@
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
11
17
 
12
18
  def self.matches?(url, client_url)
13
- url, client_url = as_uri(url), as_uri(client_url)
19
+ url = as_uri(url)
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
+
14
37
  url.query = nil
15
38
  url == client_url
16
39
  end
17
40
 
41
+ def self.loopback_uri?(uri)
42
+ IPAddr.new(uri.host).loopback?
43
+ rescue IPAddr::Error
44
+ false
45
+ end
46
+
18
47
  def self.valid_for_authorization?(url, client_url)
19
48
  valid?(url) && client_url.split.any? { |other_url| matches?(url, other_url) }
20
49
  end
@@ -23,8 +52,30 @@ module Doorkeeper
23
52
  URI.parse(url)
24
53
  end
25
54
 
26
- def self.native_uri?(url)
27
- 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)
28
79
  end
29
80
  end
30
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,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class InvalidTokenResponse < ErrorResponse
6
+ attr_reader :reason
7
+
4
8
  def self.from_access_token(access_token, attributes = {})
5
- reason = case
6
- when access_token.try(:revoked?)
9
+ reason = if access_token&.revoked?
7
10
  :revoked
8
- when access_token.try(:expired?)
11
+ elsif access_token&.expired?
9
12
  :expired
10
13
  else
11
14
  :unknown
@@ -19,9 +22,32 @@ module Doorkeeper
19
22
  @reason = attributes[:reason] || :unknown
20
23
  end
21
24
 
25
+ def status
26
+ :unauthorized
27
+ end
28
+
22
29
  def description
23
- scope = { scope: [:doorkeeper, :errors, :messages, :invalid_token] }
24
- @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
+ }
25
51
  end
26
52
  end
27
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,46 +1,74 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
- class PasswordAccessTokenRequest
4
- include Validations
5
- include OAuth::RequestConcern
5
+ class PasswordAccessTokenRequest < BaseRequest
6
6
  include OAuth::Helpers
7
7
 
8
- validate :client, error: :invalid_client
8
+ validate :client, error: :invalid_client
9
+ validate :client_supports_grant_flow, error: :unauthorized_client
9
10
  validate :resource_owner, error: :invalid_grant
10
- validate :scopes, error: :invalid_scope
11
+ validate :scopes, error: :invalid_scope
11
12
 
12
- attr_accessor :server, :resource_owner, :credentials, :access_token
13
- attr_accessor :client
13
+ attr_reader :client, :credentials, :resource_owner, :parameters, :access_token
14
14
 
15
- def initialize(server, credentials, resource_owner, parameters = {})
15
+ def initialize(server, client, credentials, resource_owner, parameters = {})
16
16
  @server = server
17
17
  @resource_owner = resource_owner
18
+ @client = client
18
19
  @credentials = credentials
20
+ @parameters = parameters
19
21
  @original_scopes = parameters[:scope]
20
-
21
- if credentials
22
- @client = Application.by_uid_and_secret credentials.uid,
23
- credentials.secret
24
- end
22
+ @grant_type = Doorkeeper::OAuth::PASSWORD
25
23
  end
26
24
 
27
25
  private
28
26
 
29
27
  def before_successful_response
30
- find_or_create_access_token(client, resource_owner.id, scopes, server)
28
+ find_or_create_access_token(client, resource_owner, scopes, server)
29
+ super
31
30
  end
32
31
 
33
32
  def validate_scopes
34
- return true unless @original_scopes.present?
35
- 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
+ )
36
41
  end
37
42
 
38
43
  def validate_resource_owner
39
- !!resource_owner
44
+ resource_owner.present?
40
45
  end
41
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
+ #
42
62
  def validate_client
43
- !credentials || !!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)
44
72
  end
45
73
  end
46
74
  end