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,54 +1,99 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  class ApplicationsController < Doorkeeper::ApplicationController
3
- layout 'doorkeeper/admin'
5
+ layout "doorkeeper/admin" unless Doorkeeper.configuration.api_only
4
6
 
5
7
  before_action :authenticate_admin!
6
- before_action :set_application, only: [:show, :edit, :update, :destroy]
8
+ before_action :set_application, only: %i[show edit update destroy]
7
9
 
8
10
  def index
9
- @applications = Application.all
11
+ @applications = Doorkeeper.config.application_model.ordered_by(:created_at)
12
+
13
+ respond_to do |format|
14
+ format.html
15
+ format.json { head :no_content }
16
+ end
17
+ end
18
+
19
+ def show
20
+ respond_to do |format|
21
+ format.html
22
+ format.json { render json: @application, as_owner: true }
23
+ end
10
24
  end
11
25
 
12
26
  def new
13
- @application = Application.new
27
+ @application = Doorkeeper.config.application_model.new
14
28
  end
15
29
 
16
30
  def create
17
- @application = Application.new(application_params)
31
+ @application = Doorkeeper.config.application_model.new(application_params)
32
+
18
33
  if @application.save
19
- flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
20
- redirect_to oauth_application_url(@application)
34
+ flash[:notice] = I18n.t(:notice, scope: %i[doorkeeper flash applications create])
35
+ flash[:application_secret] = @application.plaintext_secret
36
+
37
+ respond_to do |format|
38
+ format.html { redirect_to oauth_application_url(@application) }
39
+ format.json { render json: @application, as_owner: true }
40
+ end
21
41
  else
22
- render :new
42
+ respond_to do |format|
43
+ format.html { render :new }
44
+ format.json do
45
+ errors = @application.errors.full_messages
46
+
47
+ render json: { errors: errors }, status: :unprocessable_entity
48
+ end
49
+ end
23
50
  end
24
51
  end
25
52
 
53
+ def edit; end
54
+
26
55
  def update
27
- if @application.update_attributes(application_params)
28
- flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :update])
29
- redirect_to oauth_application_url(@application)
56
+ if @application.update(application_params)
57
+ flash[:notice] = I18n.t(:notice, scope: i18n_scope(:update))
58
+
59
+ respond_to do |format|
60
+ format.html { redirect_to oauth_application_url(@application) }
61
+ format.json { render json: @application, as_owner: true }
62
+ end
30
63
  else
31
- render :edit
64
+ respond_to do |format|
65
+ format.html { render :edit }
66
+ format.json do
67
+ errors = @application.errors.full_messages
68
+
69
+ render json: { errors: errors }, status: :unprocessable_entity
70
+ end
71
+ end
32
72
  end
33
73
  end
34
74
 
35
75
  def destroy
36
- flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :destroy]) if @application.destroy
37
- redirect_to oauth_applications_url
76
+ flash[:notice] = I18n.t(:notice, scope: i18n_scope(:destroy)) if @application.destroy
77
+
78
+ respond_to do |format|
79
+ format.html { redirect_to oauth_applications_url }
80
+ format.json { head :no_content }
81
+ end
38
82
  end
39
83
 
40
84
  private
41
85
 
42
86
  def set_application
43
- @application = Application.find(params[:id])
87
+ @application = Doorkeeper.config.application_model.find(params[:id])
44
88
  end
45
89
 
46
90
  def application_params
47
- if params.respond_to?(:permit)
48
- params.require(:doorkeeper_application).permit(:name, :redirect_uri, :scopes)
49
- else
50
- params[:doorkeeper_application].slice(:name, :redirect_uri, :scopes) rescue nil
51
- end
91
+ params.require(:doorkeeper_application)
92
+ .permit(:name, :redirect_uri, :scopes, :confidential)
93
+ end
94
+
95
+ def i18n_scope(action)
96
+ %i[doorkeeper flash applications] << action
52
97
  end
53
98
  end
54
99
  end
@@ -1,49 +1,118 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  class AuthorizationsController < Doorkeeper::ApplicationController
3
5
  before_action :authenticate_resource_owner!
4
6
 
5
7
  def new
6
8
  if pre_auth.authorizable?
7
- if skip_authorization? || matching_token?
8
- auth = authorization.authorize
9
- redirect_to auth.redirect_uri
10
- else
11
- render :new
12
- end
9
+ render_success
13
10
  else
14
- render :error
11
+ render_error
15
12
  end
16
13
  end
17
14
 
18
- # TODO: Handle raise invalid authorization
19
15
  def create
20
- redirect_or_render authorization.authorize
16
+ redirect_or_render(authorize_response)
21
17
  end
22
18
 
23
19
  def destroy
24
- redirect_or_render authorization.deny
20
+ redirect_or_render(authorization.deny)
21
+ rescue Doorkeeper::Errors::InvalidTokenStrategy => e
22
+ error_response = get_error_response_from_exception(e)
23
+
24
+ if Doorkeeper.configuration.api_only
25
+ render json: error_response.body, status: :bad_request
26
+ else
27
+ render :error, locals: { error_response: error_response }
28
+ end
25
29
  end
26
30
 
27
31
  private
28
32
 
33
+ def render_success
34
+ if skip_authorization? || (matching_token? && pre_auth.client.application.confidential?)
35
+ redirect_or_render(authorize_response)
36
+ elsif Doorkeeper.configuration.api_only
37
+ render json: pre_auth
38
+ else
39
+ render :new
40
+ end
41
+ end
42
+
43
+ def render_error
44
+ pre_auth.error_response.raise_exception! if Doorkeeper.config.raise_on_errors?
45
+
46
+ if Doorkeeper.configuration.redirect_on_errors? && pre_auth.error_response.redirectable?
47
+ redirect_or_render(pre_auth.error_response)
48
+ elsif Doorkeeper.configuration.api_only
49
+ render json: pre_auth.error_response.body, status: pre_auth.error_response.status
50
+ else
51
+ render :error, locals: { error_response: pre_auth.error_response }, status: pre_auth.error_response.status
52
+ end
53
+ end
54
+
55
+ # Active access token issued for the same client and resource owner with
56
+ # the same set of the scopes exists?
29
57
  def matching_token?
30
- AccessToken.matching_token_for pre_auth.client,
31
- current_resource_owner.id,
32
- pre_auth.scopes
58
+ Doorkeeper.config.access_token_model.matching_token_for(
59
+ pre_auth.client,
60
+ current_resource_owner,
61
+ pre_auth.scopes,
62
+ )
33
63
  end
34
64
 
35
65
  def redirect_or_render(auth)
36
66
  if auth.redirectable?
37
- redirect_to auth.redirect_uri
67
+ if Doorkeeper.configuration.api_only
68
+ if pre_auth.form_post_response?
69
+ render(
70
+ json: { status: :post, redirect_uri: pre_auth.redirect_uri, body: auth.body },
71
+ status: auth.status,
72
+ )
73
+ else
74
+ render(
75
+ json: { status: :redirect, redirect_uri: auth.redirect_uri },
76
+ status: auth.status,
77
+ )
78
+ end
79
+ elsif pre_auth.form_post_response?
80
+ render :form_post
81
+ else
82
+ redirect_to auth.redirect_uri, allow_other_host: true
83
+ end
38
84
  else
39
85
  render json: auth.body, status: auth.status
40
86
  end
41
87
  end
42
88
 
43
89
  def pre_auth
44
- @pre_auth ||= OAuth::PreAuthorization.new(Doorkeeper.configuration,
45
- server.client_via_uid,
46
- params)
90
+ @pre_auth ||= OAuth::PreAuthorization.new(
91
+ Doorkeeper.configuration,
92
+ pre_auth_params,
93
+ current_resource_owner,
94
+ )
95
+ end
96
+
97
+ def pre_auth_params
98
+ params.slice(*pre_auth_param_fields).permit(*pre_auth_param_fields)
99
+ end
100
+
101
+ def pre_auth_param_fields
102
+ custom_access_token_attributes + %i[
103
+ client_id
104
+ code_challenge
105
+ code_challenge_method
106
+ response_type
107
+ response_mode
108
+ redirect_uri
109
+ scope
110
+ state
111
+ ]
112
+ end
113
+
114
+ def custom_access_token_attributes
115
+ Doorkeeper.config.custom_access_token_attributes.map(&:to_sym)
47
116
  end
48
117
 
49
118
  def authorization
@@ -51,7 +120,35 @@ module Doorkeeper
51
120
  end
52
121
 
53
122
  def strategy
54
- @strategy ||= server.authorization_request pre_auth.response_type
123
+ @strategy ||= server.authorization_request(pre_auth.response_type)
124
+ end
125
+
126
+ def authorize_response
127
+ @authorize_response ||= begin
128
+ return pre_auth.error_response unless pre_auth.authorizable?
129
+
130
+ context = build_context(pre_auth: pre_auth)
131
+ before_successful_authorization(context)
132
+
133
+ auth = strategy.authorize
134
+
135
+ context = build_context(auth: auth)
136
+ after_successful_authorization(context)
137
+
138
+ auth
139
+ end
140
+ end
141
+
142
+ def build_context(**attributes)
143
+ Doorkeeper::OAuth::Hooks::Context.new(**attributes)
144
+ end
145
+
146
+ def before_successful_authorization(context = nil)
147
+ Doorkeeper.config.before_successful_authorization.call(self, context)
148
+ end
149
+
150
+ def after_successful_authorization(context)
151
+ Doorkeeper.config.after_successful_authorization.call(self, context)
55
152
  end
56
153
  end
57
154
  end
@@ -1,14 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  class AuthorizedApplicationsController < Doorkeeper::ApplicationController
3
5
  before_action :authenticate_resource_owner!
4
6
 
5
7
  def index
6
- @applications = Application.authorized_for(current_resource_owner)
8
+ @applications = Doorkeeper.config.application_model.authorized_for(current_resource_owner)
9
+
10
+ respond_to do |format|
11
+ format.html
12
+ format.json { render json: @applications, current_resource_owner: current_resource_owner }
13
+ end
7
14
  end
8
15
 
9
16
  def destroy
10
- AccessToken.revoke_all_for params[:id], current_resource_owner
11
- redirect_to oauth_authorized_applications_url, notice: I18n.t(:notice, scope: [:doorkeeper, :flash, :authorized_applications, :destroy])
17
+ Doorkeeper.config.application_model.revoke_tokens_and_grants_for(
18
+ params[:id],
19
+ current_resource_owner,
20
+ )
21
+
22
+ respond_to do |format|
23
+ format.html do
24
+ redirect_to oauth_authorized_applications_url, notice: I18n.t(
25
+ :notice, scope: %i[doorkeeper flash authorized_applications destroy],
26
+ )
27
+ end
28
+
29
+ format.json { head :no_content }
30
+ end
12
31
  end
13
32
  end
14
33
  end
@@ -1,13 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  class TokenInfoController < Doorkeeper::ApplicationMetalController
3
5
  def show
4
- if doorkeeper_token && doorkeeper_token.accessible?
5
- render json: doorkeeper_token, status: :ok
6
+ if doorkeeper_token&.accessible?
7
+ render json: doorkeeper_token_to_json, status: :ok
6
8
  else
7
- error = OAuth::ErrorResponse.new(name: :invalid_request)
9
+ error = OAuth::InvalidTokenResponse.new
8
10
  response.headers.merge!(error.headers)
9
- render json: error.body, status: error.status
11
+ render json: error_to_json(error), status: error.status
10
12
  end
11
13
  end
14
+
15
+ protected
16
+
17
+ def doorkeeper_token_to_json
18
+ doorkeeper_token
19
+ end
20
+
21
+ def error_to_json(error)
22
+ error.body
23
+ end
12
24
  end
13
25
  end
@@ -1,37 +1,89 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  class TokensController < Doorkeeper::ApplicationMetalController
5
+ before_action :validate_presence_of_client, only: [:revoke]
6
+
3
7
  def create
4
- response = authorize_response
5
- headers.merge! response.headers
6
- self.response_body = response.body.to_json
7
- self.status = response.status
8
+ headers.merge!(authorize_response.headers)
9
+ render json: authorize_response.body,
10
+ status: authorize_response.status
8
11
  rescue Errors::DoorkeeperError => e
9
- handle_token_exception e
12
+ handle_token_exception(e)
10
13
  end
11
14
 
12
- # OAuth 2.0 Token Revocation - http://tools.ietf.org/html/rfc7009
15
+ # OAuth 2.0 Token Revocation - https://datatracker.ietf.org/doc/html/rfc7009
13
16
  def revoke
14
- # The authorization server, if applicable, first authenticates the client
15
- # and checks its ownership of the provided token.
16
- #
17
- # Doorkeeper does not use the token_type_hint logic described in the
18
- # RFC 7009 due to the refresh token implementation that is a field in
19
- # the access token model.
20
- if authorized?
17
+ # The authorization server responds with HTTP status code 200 if the client
18
+ # submitted an invalid token or the token has been revoked successfully.
19
+ if token.blank?
20
+ render json: {}, status: 200
21
+ # The authorization server validates [...] and whether the token
22
+ # was issued to the client making the revocation request. If this
23
+ # validation fails, the request is refused and the client is informed
24
+ # of the error by the authorization server as described below.
25
+ elsif authorized?
21
26
  revoke_token
27
+ render json: {}, status: 200
28
+ else
29
+ render json: revocation_error_response, status: :forbidden
22
30
  end
31
+ end
23
32
 
24
- # The authorization server responds with HTTP status code 200 if the token
25
- # has been revoked successfully or if the client submitted an invalid
26
- # token
27
- render json: {}, status: 200
33
+ # OAuth 2.0 Token Introspection - https://datatracker.ietf.org/doc/html/rfc7662
34
+ def introspect
35
+ introspection = OAuth::TokenIntrospection.new(server, token)
36
+
37
+ if introspection.authorized?
38
+ render json: introspection.to_json, status: 200
39
+ else
40
+ error = introspection.error_response
41
+ headers.merge!(error.headers)
42
+ render json: error.body, status: error.status
43
+ end
28
44
  end
29
45
 
30
46
  private
31
47
 
48
+ def validate_presence_of_client
49
+ return if Doorkeeper.config.skip_client_authentication_for_password_grant
50
+
51
+ # @see 2.1. Revocation Request
52
+ #
53
+ # The client constructs the request by including the following
54
+ # parameters using the "application/x-www-form-urlencoded" format in
55
+ # the HTTP request entity-body:
56
+ # token REQUIRED.
57
+ # token_type_hint OPTIONAL.
58
+ #
59
+ # The client also includes its authentication credentials as described
60
+ # in Section 2.3. of [RFC6749].
61
+ #
62
+ # The authorization server first validates the client credentials (in
63
+ # case of a confidential client) and then verifies whether the token
64
+ # was issued to the client making the revocation request.
65
+ return if server.client
66
+
67
+ # If this validation [client credentials / token ownership] fails, the request is
68
+ # refused and the client is informed of the error by the authorization server as
69
+ # described below.
70
+ #
71
+ # @see 2.2.1. Error Response
72
+ #
73
+ # The error presentation conforms to the definition in Section 5.2 of [RFC6749].
74
+ render json: revocation_error_response, status: :forbidden
75
+ end
76
+
32
77
  # OAuth 2.0 Section 2.1 defines two client types, "public" & "confidential".
33
- # Public clients (as per RFC 7009) do not require authentication whereas
34
- # confidential clients must be authenticated for their token revocation.
78
+ #
79
+ # RFC7009
80
+ # Section 5. Security Considerations
81
+ # A malicious client may attempt to guess valid tokens on this endpoint
82
+ # by making revocation requests against potential token strings.
83
+ # According to this specification, a client's request must contain a
84
+ # valid client_id, in the case of a public client, or valid client
85
+ # credentials, in the case of a confidential client. The token being
86
+ # revoked must also belong to the requesting client.
35
87
  #
36
88
  # Once a confidential client is authenticated, it must be authorized to
37
89
  # revoke the provided access or refresh token. This ensures one client
@@ -43,39 +95,67 @@ module Doorkeeper
43
95
  # types, they set the application_id as null (since the claim cannot be
44
96
  # verified).
45
97
  #
46
- # https://tools.ietf.org/html/rfc6749#section-2.1
47
- # https://tools.ietf.org/html/rfc7009
98
+ # https://datatracker.ietf.org/doc/html/rfc6749#section-2.1
99
+ # https://datatracker.ietf.org/doc/html/rfc7009
48
100
  def authorized?
49
- if token.present?
50
- # Client is confidential, therefore client authentication & authorization
51
- # is required
52
- if token.application_id?
53
- # We authorize client by checking token's application
54
- server.client && server.client.application == token.application
55
- else
56
- # Client is public, authentication unnecessary
57
- true
58
- end
101
+ # Token belongs to specific client, so we need to check if
102
+ # authenticated client could access it.
103
+ if token.application_id? && token.application.confidential?
104
+ # We authorize client by checking token's application
105
+ server.client && server.client.application == token.application
106
+ else
107
+ # Token was issued without client, authorization unnecessary
108
+ true
59
109
  end
60
110
  end
61
111
 
62
112
  def revoke_token
63
- if token.accessible?
64
- token.revoke
65
- end
113
+ # The authorization server responds with HTTP status code 200 if the token
114
+ # has been revoked successfully or if the client submitted an invalid
115
+ # token
116
+ token.revoke if token&.accessible?
66
117
  end
67
118
 
68
119
  def token
69
- @token ||= AccessToken.by_token(request.POST['token']) ||
70
- AccessToken.by_refresh_token(request.POST['token'])
120
+ @token ||=
121
+ if params[:token_type_hint] == "refresh_token"
122
+ Doorkeeper.config.access_token_model.by_refresh_token(params["token"])
123
+ else
124
+ Doorkeeper.config.access_token_model.by_token(params["token"]) ||
125
+ Doorkeeper.config.access_token_model.by_refresh_token(params["token"])
126
+ end
71
127
  end
72
128
 
73
129
  def strategy
74
- @strategy ||= server.token_request params[:grant_type]
130
+ @strategy ||= server.token_request(params[:grant_type])
75
131
  end
76
132
 
77
133
  def authorize_response
78
- @authorize_response ||= strategy.authorize
134
+ @authorize_response ||= begin
135
+ before_successful_authorization
136
+ auth = strategy.authorize
137
+ context = build_context(auth: auth)
138
+ after_successful_authorization(context) unless auth.is_a?(Doorkeeper::OAuth::ErrorResponse)
139
+ auth
140
+ end
141
+ end
142
+
143
+ def build_context(**attributes)
144
+ Doorkeeper::OAuth::Hooks::Context.new(**attributes)
145
+ end
146
+
147
+ def before_successful_authorization(context = nil)
148
+ Doorkeeper.config.before_successful_authorization.call(self, context)
149
+ end
150
+
151
+ def after_successful_authorization(context)
152
+ Doorkeeper.config.after_successful_authorization.call(self, context)
153
+ end
154
+
155
+ def revocation_error_response
156
+ error_description = I18n.t(:unauthorized, scope: %i[doorkeeper errors messages revoke])
157
+
158
+ { error: :unauthorized_client, error_description: error_description }
79
159
  end
80
160
  end
81
161
  end
@@ -1,13 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module DashboardHelper
3
5
  def doorkeeper_errors_for(object, method)
4
- if object.errors[method].present?
5
- object.errors[method].map do |msg|
6
- content_tag(:span, class: 'help-block') do
7
- msg.capitalize
8
- end
9
- end.join.html_safe
6
+ return if object.errors[method].blank?
7
+
8
+ output = object.errors[method].map do |msg|
9
+ content_tag(:span, class: "invalid-feedback") do
10
+ msg.capitalize
11
+ end
10
12
  end
13
+
14
+ safe_join(output)
11
15
  end
12
16
 
13
17
  def doorkeeper_submit_path(application)
@@ -1,5 +1,6 @@
1
1
  <%- submit_btn_css ||= 'btn btn-link' %>
2
- <%= form_tag oauth_application_path(application) do %>
3
- <input type="hidden" name="_method" value="delete">
4
- <%= submit_tag t('doorkeeper.applications.buttons.destroy'), onclick: "return confirm('#{ t('doorkeeper.applications.confirmations.destroy') }')", class: submit_btn_css %>
2
+ <%= form_tag oauth_application_path(application), method: :delete do %>
3
+ <%= submit_tag t('doorkeeper.applications.buttons.destroy'),
4
+ onclick: "return confirm('#{ t('doorkeeper.applications.confirmations.destroy') }')",
5
+ class: submit_btn_css %>
5
6
  <% end %>
@@ -1,47 +1,59 @@
1
- <%= form_for application, url: doorkeeper_submit_path(application), html: {class: 'form-horizontal', role: 'form'} do |f| %>
1
+ <%= form_for application, url: doorkeeper_submit_path(application), as: :doorkeeper_application, html: { role: 'form' } do |f| %>
2
2
  <% if application.errors.any? %>
3
3
  <div class="alert alert-danger" data-alert><p><%= t('doorkeeper.applications.form.error') %></p></div>
4
4
  <% end %>
5
5
 
6
- <%= content_tag :div, class: "form-group#{' has-error' if application.errors[:name].present?}" do %>
7
- <%= f.label :name, class: 'col-sm-2 control-label' %>
6
+ <div class="form-group row">
7
+ <%= f.label :name, class: 'col-sm-2 col-form-label font-weight-bold' %>
8
8
  <div class="col-sm-10">
9
- <%= f.text_field :name, class: 'form-control' %>
9
+ <%= f.text_field :name, class: "form-control #{ 'is-invalid' if application.errors[:name].present? }", required: true %>
10
10
  <%= doorkeeper_errors_for application, :name %>
11
11
  </div>
12
- <% end %>
12
+ </div>
13
13
 
14
- <%= content_tag :div, class: "form-group#{' has-error' if application.errors[:redirect_uri].present?}" do %>
15
- <%= f.label :redirect_uri, class: 'col-sm-2 control-label' %>
14
+ <div class="form-group row">
15
+ <%= f.label :redirect_uri, class: 'col-sm-2 col-form-label font-weight-bold' %>
16
16
  <div class="col-sm-10">
17
- <%= f.text_area :redirect_uri, class: 'form-control' %>
17
+ <%= f.text_area :redirect_uri, class: "form-control #{ 'is-invalid' if application.errors[:redirect_uri].present? }" %>
18
18
  <%= doorkeeper_errors_for application, :redirect_uri %>
19
- <span class="help-block">
19
+ <span class="form-text text-secondary">
20
20
  <%= t('doorkeeper.applications.help.redirect_uri') %>
21
21
  </span>
22
- <% if Doorkeeper.configuration.native_redirect_uri %>
23
- <span class="help-block">
24
- <%= raw t('doorkeeper.applications.help.native_redirect_uri', native_redirect_uri: "<code>#{ Doorkeeper.configuration.native_redirect_uri }</code>") %>
25
- </span>
22
+
23
+ <% if Doorkeeper.configuration.allow_blank_redirect_uri?(application) %>
24
+ <span class="form-text text-secondary">
25
+ <%= t('doorkeeper.applications.help.blank_redirect_uri') %>
26
+ </span>
26
27
  <% end %>
27
28
  </div>
28
- <% end %>
29
+ </div>
30
+
31
+ <div class="form-group row">
32
+ <%= f.label :confidential, class: 'col-sm-2 form-check-label font-weight-bold' %>
33
+ <div class="col-sm-10">
34
+ <%= f.check_box :confidential, class: "checkbox #{ 'is-invalid' if application.errors[:confidential].present? }" %>
35
+ <%= doorkeeper_errors_for application, :confidential %>
36
+ <span class="form-text text-secondary">
37
+ <%= t('doorkeeper.applications.help.confidential') %>
38
+ </span>
39
+ </div>
40
+ </div>
29
41
 
30
- <%= content_tag :div, class: "form-group#{' has-error' if application.errors[:scopes].present?}" do %>
31
- <%= f.label :scopes, class: 'col-sm-2 control-label' %>
42
+ <div class="form-group row">
43
+ <%= f.label :scopes, class: 'col-sm-2 col-form-label font-weight-bold' %>
32
44
  <div class="col-sm-10">
33
- <%= f.text_field :scopes, class: 'form-control' %>
45
+ <%= f.text_field :scopes, class: "form-control #{ 'has-error' if application.errors[:scopes].present? }" %>
34
46
  <%= doorkeeper_errors_for application, :scopes %>
35
- <span class="help-block">
47
+ <span class="form-text text-secondary">
36
48
  <%= t('doorkeeper.applications.help.scopes') %>
37
49
  </span>
38
50
  </div>
39
- <% end %>
51
+ </div>
40
52
 
41
53
  <div class="form-group">
42
54
  <div class="col-sm-offset-2 col-sm-10">
43
- <%= f.submit t('doorkeeper.applications.buttons.submit'), class: "btn btn-primary" %>
44
- <%= link_to t('doorkeeper.applications.buttons.cancel'), oauth_applications_path, :class => "btn btn-default" %>
55
+ <%= f.submit t('doorkeeper.applications.buttons.submit'), class: 'btn btn-primary' %>
56
+ <%= link_to t('doorkeeper.applications.buttons.cancel'), oauth_applications_path, class: 'btn btn-secondary' %>
45
57
  </div>
46
58
  </div>
47
59
  <% end %>