doorkeeper 5.0.0 → 5.1.0

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 (241) hide show
  1. checksums.yaml +5 -5
  2. data/.hound.yml +2 -1
  3. data/.rubocop.yml +37 -4
  4. data/.travis.yml +16 -21
  5. data/Appraisals +29 -7
  6. data/Dangerfile +67 -0
  7. data/Gemfile +19 -5
  8. data/NEWS.md +107 -10
  9. data/README.md +75 -470
  10. data/Rakefile +10 -8
  11. data/app/controllers/doorkeeper/application_controller.rb +3 -2
  12. data/app/controllers/doorkeeper/application_metal_controller.rb +4 -13
  13. data/app/controllers/doorkeeper/applications_controller.rb +21 -7
  14. data/app/controllers/doorkeeper/authorizations_controller.rb +3 -3
  15. data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -0
  16. data/app/controllers/doorkeeper/token_info_controller.rb +4 -2
  17. data/app/controllers/doorkeeper/tokens_controller.rb +13 -11
  18. data/app/helpers/doorkeeper/dashboard_helper.rb +3 -1
  19. data/app/validators/redirect_uri_validator.rb +7 -2
  20. data/app/views/doorkeeper/applications/_form.html.erb +6 -0
  21. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  22. data/app/views/layouts/doorkeeper/admin.html.erb +5 -3
  23. data/bin/console +16 -0
  24. data/config/locales/en.yml +1 -0
  25. data/doorkeeper.gemspec +28 -24
  26. data/gemfiles/rails_5_0.gemfile +9 -4
  27. data/gemfiles/rails_5_1.gemfile +9 -4
  28. data/gemfiles/rails_5_2.gemfile +10 -5
  29. data/gemfiles/rails_6_0.gemfile +17 -0
  30. data/gemfiles/rails_master.gemfile +9 -9
  31. data/lib/doorkeeper/config/option.rb +64 -0
  32. data/lib/doorkeeper/config.rb +197 -76
  33. data/lib/doorkeeper/engine.rb +3 -1
  34. data/lib/doorkeeper/errors.rb +17 -0
  35. data/lib/doorkeeper/grape/authorization_decorator.rb +6 -4
  36. data/lib/doorkeeper/grape/helpers.rb +5 -3
  37. data/lib/doorkeeper/helpers/controller.rb +5 -2
  38. data/lib/doorkeeper/models/access_grant_mixin.rb +25 -6
  39. data/lib/doorkeeper/models/access_token_mixin.rb +68 -22
  40. data/lib/doorkeeper/models/application_mixin.rb +45 -1
  41. data/lib/doorkeeper/models/concerns/accessible.rb +2 -0
  42. data/lib/doorkeeper/models/concerns/expirable.rb +6 -2
  43. data/lib/doorkeeper/models/concerns/orderable.rb +2 -0
  44. data/lib/doorkeeper/models/concerns/ownership.rb +3 -6
  45. data/lib/doorkeeper/models/concerns/reusable.rb +19 -0
  46. data/lib/doorkeeper/models/concerns/revocable.rb +4 -1
  47. data/lib/doorkeeper/models/concerns/scopes.rb +7 -1
  48. data/lib/doorkeeper/models/concerns/secret_storable.rb +108 -0
  49. data/lib/doorkeeper/oauth/authorization/code.rb +4 -2
  50. data/lib/doorkeeper/oauth/authorization/context.rb +2 -0
  51. data/lib/doorkeeper/oauth/authorization/token.rb +14 -7
  52. data/lib/doorkeeper/oauth/authorization/uri_builder.rb +3 -1
  53. data/lib/doorkeeper/oauth/authorization_code_request.rb +8 -4
  54. data/lib/doorkeeper/oauth/base_request.rb +2 -0
  55. data/lib/doorkeeper/oauth/base_response.rb +2 -0
  56. data/lib/doorkeeper/oauth/client/credentials.rb +2 -0
  57. data/lib/doorkeeper/oauth/client.rb +3 -1
  58. data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -0
  59. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +2 -0
  60. data/lib/doorkeeper/oauth/client_credentials/validation.rb +7 -4
  61. data/lib/doorkeeper/oauth/client_credentials_request.rb +3 -1
  62. data/lib/doorkeeper/oauth/code_request.rb +2 -0
  63. data/lib/doorkeeper/oauth/code_response.rb +4 -2
  64. data/lib/doorkeeper/oauth/error.rb +2 -0
  65. data/lib/doorkeeper/oauth/error_response.rb +20 -6
  66. data/lib/doorkeeper/oauth/forbidden_token_response.rb +10 -3
  67. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +25 -8
  68. data/lib/doorkeeper/oauth/helpers/unique_token.rb +15 -1
  69. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +42 -3
  70. data/lib/doorkeeper/oauth/invalid_token_response.rb +22 -0
  71. data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -2
  72. data/lib/doorkeeper/oauth/pre_authorization.rb +13 -5
  73. data/lib/doorkeeper/oauth/refresh_token_request.rb +15 -4
  74. data/lib/doorkeeper/oauth/scopes.rb +7 -3
  75. data/lib/doorkeeper/oauth/token.rb +4 -2
  76. data/lib/doorkeeper/oauth/token_introspection.rb +76 -8
  77. data/lib/doorkeeper/oauth/token_request.rb +2 -0
  78. data/lib/doorkeeper/oauth/token_response.rb +11 -9
  79. data/lib/doorkeeper/oauth.rb +5 -5
  80. data/lib/doorkeeper/orm/active_record/access_grant.rb +27 -13
  81. data/lib/doorkeeper/orm/active_record/access_token.rb +6 -13
  82. data/lib/doorkeeper/orm/active_record/application.rb +26 -7
  83. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +10 -3
  84. data/lib/doorkeeper/orm/active_record.rb +8 -6
  85. data/lib/doorkeeper/rails/helpers.rb +5 -1
  86. data/lib/doorkeeper/rails/routes/mapper.rb +2 -0
  87. data/lib/doorkeeper/rails/routes/mapping.rb +9 -7
  88. data/lib/doorkeeper/rails/routes.rb +19 -12
  89. data/lib/doorkeeper/rake/db.rake +17 -17
  90. data/lib/doorkeeper/rake.rb +1 -1
  91. data/lib/doorkeeper/request/authorization_code.rb +2 -0
  92. data/lib/doorkeeper/request/client_credentials.rb +2 -0
  93. data/lib/doorkeeper/request/code.rb +2 -0
  94. data/lib/doorkeeper/request/password.rb +2 -0
  95. data/lib/doorkeeper/request/refresh_token.rb +2 -0
  96. data/lib/doorkeeper/request/strategy.rb +2 -0
  97. data/lib/doorkeeper/request/token.rb +2 -0
  98. data/lib/doorkeeper/request.rb +3 -1
  99. data/lib/doorkeeper/secret_storing/base.rb +64 -0
  100. data/lib/doorkeeper/secret_storing/bcrypt.rb +60 -0
  101. data/lib/doorkeeper/secret_storing/plain.rb +33 -0
  102. data/lib/doorkeeper/secret_storing/sha256_hash.rb +26 -0
  103. data/lib/doorkeeper/server.rb +2 -0
  104. data/lib/doorkeeper/stale_records_cleaner.rb +20 -0
  105. data/lib/doorkeeper/validations.rb +2 -0
  106. data/lib/doorkeeper/version.rb +4 -2
  107. data/lib/doorkeeper.rb +69 -59
  108. data/lib/generators/doorkeeper/application_owner_generator.rb +10 -9
  109. data/lib/generators/doorkeeper/confidential_applications_generator.rb +10 -9
  110. data/lib/generators/doorkeeper/install_generator.rb +11 -9
  111. data/lib/generators/doorkeeper/migration_generator.rb +9 -9
  112. data/lib/generators/doorkeeper/pkce_generator.rb +10 -9
  113. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +10 -9
  114. data/lib/generators/doorkeeper/templates/initializer.rb +138 -13
  115. data/lib/generators/doorkeeper/templates/migration.rb.erb +17 -10
  116. data/lib/generators/doorkeeper/views_generator.rb +6 -4
  117. data/spec/controllers/application_metal_controller_spec.rb +24 -10
  118. data/spec/controllers/applications_controller_spec.rb +76 -78
  119. data/spec/controllers/authorizations_controller_spec.rb +189 -142
  120. data/spec/controllers/protected_resources_controller_spec.rb +111 -67
  121. data/spec/controllers/token_info_controller_spec.rb +14 -12
  122. data/spec/controllers/tokens_controller_spec.rb +170 -112
  123. data/spec/dummy/Rakefile +3 -1
  124. data/spec/dummy/app/controllers/application_controller.rb +3 -1
  125. data/spec/dummy/app/controllers/custom_authorizations_controller.rb +3 -1
  126. data/spec/dummy/app/controllers/full_protected_resources_controller.rb +4 -2
  127. data/spec/dummy/app/controllers/home_controller.rb +6 -5
  128. data/spec/dummy/app/controllers/metal_controller.rb +2 -0
  129. data/spec/dummy/app/controllers/semi_protected_resources_controller.rb +4 -2
  130. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  131. data/spec/dummy/app/models/user.rb +2 -0
  132. data/spec/dummy/config/application.rb +26 -2
  133. data/spec/dummy/config/boot.rb +2 -4
  134. data/spec/dummy/config/environment.rb +1 -1
  135. data/spec/dummy/config/environments/development.rb +2 -0
  136. data/spec/dummy/config/environments/production.rb +2 -0
  137. data/spec/dummy/config/environments/test.rb +4 -2
  138. data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
  139. data/spec/dummy/config/initializers/doorkeeper.rb +7 -3
  140. data/spec/dummy/config/initializers/secret_token.rb +3 -1
  141. data/spec/dummy/config/initializers/session_store.rb +3 -1
  142. data/spec/dummy/config/initializers/wrap_parameters.rb +2 -0
  143. data/spec/dummy/config.ru +3 -1
  144. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +20 -13
  145. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +1 -1
  146. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +2 -0
  147. data/spec/dummy/db/schema.rb +1 -1
  148. data/spec/dummy/script/rails +7 -4
  149. data/spec/factories.rb +11 -9
  150. data/spec/generators/application_owner_generator_spec.rb +13 -26
  151. data/spec/generators/confidential_applications_generator_spec.rb +12 -28
  152. data/spec/generators/install_generator_spec.rb +19 -14
  153. data/spec/generators/migration_generator_spec.rb +13 -26
  154. data/spec/generators/pkce_generator_spec.rb +11 -26
  155. data/spec/generators/previous_refresh_token_generator_spec.rb +16 -29
  156. data/spec/generators/templates/routes.rb +2 -1
  157. data/spec/generators/views_generator_spec.rb +14 -12
  158. data/spec/grape/grape_integration_spec.rb +35 -33
  159. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +9 -7
  160. data/spec/lib/config_spec.rb +290 -104
  161. data/spec/lib/doorkeeper_spec.rb +8 -6
  162. data/spec/lib/models/expirable_spec.rb +22 -8
  163. data/spec/lib/models/reusable_spec.rb +40 -0
  164. data/spec/lib/models/revocable_spec.rb +8 -6
  165. data/spec/lib/models/scopes_spec.rb +27 -13
  166. data/spec/lib/models/secret_storable_spec.rb +135 -0
  167. data/spec/lib/oauth/authorization/uri_builder_spec.rb +17 -15
  168. data/spec/lib/oauth/authorization_code_request_spec.rb +43 -17
  169. data/spec/lib/oauth/base_request_spec.rb +24 -12
  170. data/spec/lib/oauth/base_response_spec.rb +3 -1
  171. data/spec/lib/oauth/client/credentials_spec.rb +26 -24
  172. data/spec/lib/oauth/client_credentials/creator_spec.rb +56 -10
  173. data/spec/lib/oauth/client_credentials/issuer_spec.rb +27 -20
  174. data/spec/lib/oauth/client_credentials/validation_spec.rb +21 -15
  175. data/spec/lib/oauth/client_credentials_integration_spec.rb +8 -6
  176. data/spec/lib/oauth/client_credentials_request_spec.rb +27 -21
  177. data/spec/lib/oauth/client_spec.rb +15 -13
  178. data/spec/lib/oauth/code_request_spec.rb +10 -8
  179. data/spec/lib/oauth/code_response_spec.rb +9 -7
  180. data/spec/lib/oauth/error_response_spec.rb +19 -11
  181. data/spec/lib/oauth/error_spec.rb +4 -2
  182. data/spec/lib/oauth/forbidden_token_response_spec.rb +7 -5
  183. data/spec/lib/oauth/helpers/scope_checker_spec.rb +68 -31
  184. data/spec/lib/oauth/helpers/unique_token_spec.rb +8 -6
  185. data/spec/lib/oauth/helpers/uri_checker_spec.rb +124 -93
  186. data/spec/lib/oauth/invalid_token_response_spec.rb +3 -1
  187. data/spec/lib/oauth/password_access_token_request_spec.rb +95 -34
  188. data/spec/lib/oauth/pre_authorization_spec.rb +80 -54
  189. data/spec/lib/oauth/refresh_token_request_spec.rb +46 -39
  190. data/spec/lib/oauth/scopes_spec.rb +63 -61
  191. data/spec/lib/oauth/token_request_spec.rb +80 -25
  192. data/spec/lib/oauth/token_response_spec.rb +40 -38
  193. data/spec/lib/oauth/token_spec.rb +64 -45
  194. data/spec/lib/request/strategy_spec.rb +3 -1
  195. data/spec/lib/secret_storing/base_spec.rb +60 -0
  196. data/spec/lib/secret_storing/bcrypt_spec.rb +49 -0
  197. data/spec/lib/secret_storing/plain_spec.rb +44 -0
  198. data/spec/lib/secret_storing/sha256_hash_spec.rb +48 -0
  199. data/spec/lib/server_spec.rb +20 -18
  200. data/spec/lib/{orm/active_record/stale_records_cleaner_spec.rb → stale_records_cleaner_spec.rb} +28 -18
  201. data/spec/models/doorkeeper/access_grant_spec.rb +80 -15
  202. data/spec/models/doorkeeper/access_token_spec.rb +212 -83
  203. data/spec/models/doorkeeper/application_spec.rb +146 -49
  204. data/spec/requests/applications/applications_request_spec.rb +100 -68
  205. data/spec/requests/applications/authorized_applications_spec.rb +20 -18
  206. data/spec/requests/endpoints/authorization_spec.rb +27 -25
  207. data/spec/requests/endpoints/token_spec.rb +38 -36
  208. data/spec/requests/flows/authorization_code_errors_spec.rb +27 -25
  209. data/spec/requests/flows/authorization_code_spec.rb +260 -158
  210. data/spec/requests/flows/client_credentials_spec.rb +54 -52
  211. data/spec/requests/flows/implicit_grant_errors_spec.rb +11 -9
  212. data/spec/requests/flows/implicit_grant_spec.rb +27 -25
  213. data/spec/requests/flows/password_spec.rb +64 -56
  214. data/spec/requests/flows/refresh_token_spec.rb +91 -32
  215. data/spec/requests/flows/revoke_token_spec.rb +49 -63
  216. data/spec/requests/flows/skip_authorization_spec.rb +23 -21
  217. data/spec/requests/protected_resources/metal_spec.rb +7 -5
  218. data/spec/requests/protected_resources/private_api_spec.rb +37 -35
  219. data/spec/routing/custom_controller_routes_spec.rb +71 -65
  220. data/spec/routing/default_routes_spec.rb +22 -20
  221. data/spec/routing/scoped_routes_spec.rb +20 -18
  222. data/spec/spec_helper.rb +15 -13
  223. data/spec/spec_helper_integration.rb +3 -1
  224. data/spec/support/dependencies/factory_bot.rb +3 -1
  225. data/spec/support/doorkeeper_rspec.rb +5 -2
  226. data/spec/support/helpers/access_token_request_helper.rb +3 -1
  227. data/spec/support/helpers/authorization_request_helper.rb +4 -2
  228. data/spec/support/helpers/config_helper.rb +2 -0
  229. data/spec/support/helpers/model_helper.rb +11 -5
  230. data/spec/support/helpers/request_spec_helper.rb +5 -3
  231. data/spec/support/helpers/url_helper.rb +20 -18
  232. data/spec/support/http_method_shim.rb +4 -9
  233. data/spec/support/orm/active_record.rb +3 -1
  234. data/spec/support/shared/controllers_shared_context.rb +68 -10
  235. data/spec/support/shared/hashing_shared_context.rb +36 -0
  236. data/spec/support/shared/models_shared_examples.rb +12 -10
  237. data/spec/validators/redirect_uri_validator_spec.rb +76 -47
  238. data/spec/version/version_spec.rb +7 -5
  239. metadata +70 -21
  240. data/gemfiles/rails_4_2.gemfile +0 -13
  241. data/spec/dummy/config/initializers/new_framework_defaults.rb +0 -10
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  # RFC7662 OAuth 2.0 Token Introspection
@@ -18,7 +20,17 @@ module Doorkeeper
18
20
  @error.blank?
19
21
  end
20
22
 
21
- def to_json
23
+ def error_response
24
+ return if @error.blank?
25
+
26
+ if @error == :invalid_token
27
+ OAuth::InvalidTokenResponse.from_access_token(authorized_token)
28
+ else
29
+ OAuth::ErrorResponse.new(name: @error)
30
+ end
31
+ end
32
+
33
+ def to_json(*)
22
34
  active? ? success_response : failure_response
23
35
  end
24
36
 
@@ -35,13 +47,29 @@ module Doorkeeper
35
47
  #
36
48
  # @see https://www.oauth.com/oauth2-servers/token-introspection-endpoint/
37
49
  #
50
+ # To prevent token scanning attacks, the endpoint MUST also require
51
+ # some form of authorization to access this endpoint, such as client
52
+ # authentication as described in OAuth 2.0 [RFC6749] or a separate
53
+ # OAuth 2.0 access token such as the bearer token described in OAuth
54
+ # 2.0 Bearer Token Usage [RFC6750].
55
+ #
38
56
  def authorize!
39
57
  # Requested client authorization
40
58
  if server.credentials
41
59
  @error = :invalid_client unless authorized_client
42
- else
60
+ elsif authorized_token
43
61
  # Requested bearer token authorization
44
- @error = :invalid_request unless authorized_token
62
+ #
63
+ # If the protected resource uses an OAuth 2.0 bearer token to authorize
64
+ # its call to the introspection endpoint and the token used for
65
+ # authorization does not contain sufficient privileges or is otherwise
66
+ # invalid for this request, the authorization server responds with an
67
+ # HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
68
+ # Usage [RFC6750].
69
+ #
70
+ @error = :invalid_token if authorized_token_matches_introspected? || !authorized_token.accessible?
71
+ else
72
+ @error = :invalid_request
45
73
  end
46
74
  end
47
75
 
@@ -58,14 +86,14 @@ module Doorkeeper
58
86
 
59
87
  # 2.2. Introspection Response
60
88
  def success_response
61
- {
89
+ customize_response(
62
90
  active: true,
63
91
  scope: @token.scopes_string,
64
92
  client_id: @token.try(:application).try(:uid),
65
93
  token_type: @token.token_type,
66
94
  exp: @token.expires_at.to_i,
67
95
  iat: @token.created_at.to_i
68
- }
96
+ )
69
97
  end
70
98
 
71
99
  # If the introspection call is properly authorized but the token is not
@@ -81,7 +109,7 @@ module Doorkeeper
81
109
  #
82
110
  def failure_response
83
111
  {
84
- active: false
112
+ active: false,
85
113
  }
86
114
  end
87
115
 
@@ -101,6 +129,25 @@ module Doorkeeper
101
129
  # * The token expired
102
130
  # * The token was issued to a different client than is making this request
103
131
  #
132
+ # Since resource servers using token introspection rely on the
133
+ # authorization server to determine the state of a token, the
134
+ # authorization server MUST perform all applicable checks against a
135
+ # token's state. For instance, these tests include the following:
136
+ #
137
+ # o If the token can expire, the authorization server MUST determine
138
+ # whether or not the token has expired.
139
+ # o If the token can be issued before it is able to be used, the
140
+ # authorization server MUST determine whether or not a token's valid
141
+ # period has started yet.
142
+ # o If the token can be revoked after it was issued, the authorization
143
+ # server MUST determine whether or not such a revocation has taken
144
+ # place.
145
+ # o If the token has been signed, the authorization server MUST
146
+ # validate the signature.
147
+ # o If the token can be used only at certain resource servers, the
148
+ # authorization server MUST determine whether or not the token can
149
+ # be used at the resource server making the introspection call.
150
+ #
104
151
  def active?
105
152
  if authorized_client
106
153
  valid_token? && authorized_for_client?
@@ -111,18 +158,39 @@ module Doorkeeper
111
158
 
112
159
  # Token can be valid only if it is not expired or revoked.
113
160
  def valid_token?
114
- @token.present? && @token.accessible?
161
+ @token&.accessible?
162
+ end
163
+
164
+ # RFC7662 Section 2.1
165
+ def authorized_token_matches_introspected?
166
+ authorized_token.token == @token&.token
115
167
  end
116
168
 
117
169
  # If token doesn't belong to some client, then it is public.
118
170
  # Otherwise in it required for token to be connected to the same client.
119
171
  def authorized_for_client?
120
- if @token.application.present?
172
+ if @token.application
121
173
  @token.application == authorized_client.application
122
174
  else
123
175
  true
124
176
  end
125
177
  end
178
+
179
+ # Allows to customize introspection response.
180
+ # Provides context (controller) and token for generating developer-specific
181
+ # response.
182
+ #
183
+ # @see https://tools.ietf.org/html/rfc7662#section-2.2
184
+ #
185
+ def customize_response(response)
186
+ customized_response = Doorkeeper.configuration.custom_introspection_response.call(
187
+ token,
188
+ server.context
189
+ )
190
+ return response if customized_response.blank?
191
+
192
+ response.merge(customized_response)
193
+ end
126
194
  end
127
195
  end
128
196
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class TokenRequest
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OAuth
3
5
  class TokenResponse
@@ -9,12 +11,12 @@ module Doorkeeper
9
11
 
10
12
  def body
11
13
  {
12
- 'access_token' => token.token,
13
- 'token_type' => token.token_type,
14
- 'expires_in' => token.expires_in_seconds,
15
- 'refresh_token' => token.refresh_token,
16
- 'scope' => token.scopes_string,
17
- 'created_at' => token.created_at.to_i
14
+ "access_token" => token.plaintext_token,
15
+ "token_type" => token.token_type,
16
+ "expires_in" => token.expires_in_seconds,
17
+ "refresh_token" => token.plaintext_refresh_token,
18
+ "scope" => token.scopes_string,
19
+ "created_at" => token.created_at.to_i,
18
20
  }.reject { |_, value| value.blank? }
19
21
  end
20
22
 
@@ -24,9 +26,9 @@ module Doorkeeper
24
26
 
25
27
  def headers
26
28
  {
27
- 'Cache-Control' => 'no-store',
28
- 'Pragma' => 'no-cache',
29
- 'Content-Type' => 'application/json; charset=utf-8'
29
+ "Cache-Control" => "no-store",
30
+ "Pragma" => "no-cache",
31
+ "Content-Type" => "application/json; charset=utf-8",
30
32
  }
31
33
  end
32
34
  end
@@ -3,11 +3,11 @@
3
3
  module Doorkeeper
4
4
  module OAuth
5
5
  GRANT_TYPES = [
6
- AUTHORIZATION_CODE = 'authorization_code'.freeze,
7
- IMPLICIT = 'implicit'.freeze,
8
- PASSWORD = 'password'.freeze,
9
- CLIENT_CREDENTIALS = 'client_credentials'.freeze,
10
- REFRESH_TOKEN = 'refresh_token'.freeze
6
+ AUTHORIZATION_CODE = "authorization_code",
7
+ IMPLICIT = "implicit",
8
+ PASSWORD = "password",
9
+ CLIENT_CREDENTIALS = "client_credentials",
10
+ REFRESH_TOKEN = "refresh_token",
11
11
  ].freeze
12
12
  end
13
13
  end
@@ -1,26 +1,39 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  class AccessGrant < ActiveRecord::Base
3
- self.table_name = "#{table_name_prefix}oauth_access_grants#{table_name_suffix}".to_sym
5
+ self.table_name = "#{table_name_prefix}oauth_access_grants#{table_name_suffix}"
4
6
 
5
7
  include AccessGrantMixin
6
- include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
7
-
8
- belongs_to_options = {
9
- class_name: 'Doorkeeper::Application',
10
- inverse_of: :access_grants
11
- }
12
8
 
13
- if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
14
- belongs_to_options[:optional] = true
15
- end
9
+ belongs_to :application, class_name: "Doorkeeper::Application",
10
+ optional: true, inverse_of: :access_grants
16
11
 
17
- belongs_to :application, belongs_to_options
12
+ validates :resource_owner_id,
13
+ :application_id,
14
+ :token,
15
+ :expires_in,
16
+ :redirect_uri,
17
+ presence: true
18
18
 
19
- validates :resource_owner_id, :application_id, :token, :expires_in, :redirect_uri, presence: true
20
19
  validates :token, uniqueness: true
21
20
 
22
21
  before_validation :generate_token, on: :create
23
22
 
23
+ # We keep a volatile copy of the raw token for initial communication
24
+ # The stored refresh_token may be mapped and not available in cleartext.
25
+ #
26
+ # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
27
+ # while hashing strategies do not, so you cannot rely on this value
28
+ # returning a present value for persisted tokens.
29
+ def plaintext_token
30
+ if secret_strategy.allows_restoring_secrets?
31
+ secret_strategy.restore_secret(self, :token)
32
+ else
33
+ @raw_token
34
+ end
35
+ end
36
+
24
37
  private
25
38
 
26
39
  # Generates token value with UniqueToken class.
@@ -28,7 +41,8 @@ module Doorkeeper
28
41
  # @return [String] token value
29
42
  #
30
43
  def generate_token
31
- self.token = UniqueToken.generate
44
+ @raw_token = UniqueToken.generate
45
+ secret_strategy.store_secret(self, :token, @raw_token)
32
46
  end
33
47
  end
34
48
  end
@@ -1,20 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  class AccessToken < ActiveRecord::Base
3
- self.table_name = "#{table_name_prefix}oauth_access_tokens#{table_name_suffix}".to_sym
5
+ self.table_name = "#{table_name_prefix}oauth_access_tokens#{table_name_suffix}"
4
6
 
5
7
  include AccessTokenMixin
6
- include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
7
-
8
- belongs_to_options = {
9
- class_name: 'Doorkeeper::Application',
10
- inverse_of: :access_tokens
11
- }
12
-
13
- if defined?(ActiveRecord::Base) && ActiveRecord::VERSION::MAJOR >= 5
14
- belongs_to_options[:optional] = true
15
- end
16
8
 
17
- belongs_to :application, belongs_to_options
9
+ belongs_to :application, class_name: "Doorkeeper::Application",
10
+ inverse_of: :access_tokens, optional: true
18
11
 
19
12
  validates :token, presence: true, uniqueness: true
20
13
  validates :refresh_token, uniqueness: true, if: :use_refresh_token?
@@ -41,7 +34,7 @@ module Doorkeeper
41
34
  end
42
35
 
43
36
  def self.refresh_token_revoked_on_use?
44
- column_names.include?('previous_refresh_token')
37
+ column_names.include?("previous_refresh_token")
45
38
  end
46
39
  end
47
40
  end
@@ -1,12 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  class Application < ActiveRecord::Base
3
- self.table_name = "#{table_name_prefix}oauth_applications#{table_name_suffix}".to_sym
5
+ self.table_name = "#{table_name_prefix}oauth_applications#{table_name_suffix}"
4
6
 
5
7
  include ApplicationMixin
6
- include ActiveModel::MassAssignmentSecurity if defined?(::ProtectedAttributes)
7
8
 
8
- has_many :access_grants, dependent: :delete_all, class_name: 'Doorkeeper::AccessGrant'
9
- has_many :access_tokens, dependent: :delete_all, class_name: 'Doorkeeper::AccessToken'
9
+ has_many :access_grants, dependent: :delete_all, class_name: "Doorkeeper::AccessGrant"
10
+ has_many :access_tokens, dependent: :delete_all, class_name: "Doorkeeper::AccessToken"
10
11
 
11
12
  validates :name, :secret, :uid, presence: true
12
13
  validates :uid, uniqueness: true
@@ -17,7 +18,7 @@ module Doorkeeper
17
18
 
18
19
  before_validation :generate_uid, :generate_secret, on: :create
19
20
 
20
- has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name: 'AccessToken'
21
+ has_many :authorized_tokens, -> { where(revoked_at: nil) }, class_name: "AccessToken"
21
22
  has_many :authorized_applications, through: :authorized_tokens, source: :application
22
23
 
23
24
  # Returns Applications associated with active (not revoked) Access Tokens
@@ -45,6 +46,20 @@ module Doorkeeper
45
46
  AccessGrant.revoke_all_for(id, resource_owner)
46
47
  end
47
48
 
49
+ # We keep a volatile copy of the raw secret for initial communication
50
+ # The stored refresh_token may be mapped and not available in cleartext.
51
+ #
52
+ # Some strategies allow restoring stored secrets (e.g. symmetric encryption)
53
+ # while hashing strategies do not, so you cannot rely on this value
54
+ # returning a present value for persisted tokens.
55
+ def plaintext_secret
56
+ if secret_strategy.allows_restoring_secrets?
57
+ secret_strategy.restore_secret(self, :secret)
58
+ else
59
+ @raw_secret
60
+ end
61
+ end
62
+
48
63
  private
49
64
 
50
65
  def generate_uid
@@ -52,12 +67,16 @@ module Doorkeeper
52
67
  end
53
68
 
54
69
  def generate_secret
55
- self.secret = UniqueToken.generate if secret.blank?
70
+ return unless secret.blank?
71
+
72
+ @raw_secret = UniqueToken.generate
73
+ secret_strategy.store_secret(self, :secret, @raw_secret)
56
74
  end
57
75
 
58
76
  def scopes_match_configured
59
77
  if scopes.present? &&
60
- !ScopeChecker.valid?(scopes.to_s, Doorkeeper.configuration.scopes)
78
+ !ScopeChecker.valid?(scope_str: scopes.to_s,
79
+ server_scopes: Doorkeeper.configuration.scopes)
61
80
  errors.add(:scopes, :not_match_configured)
62
81
  end
63
82
  end
@@ -3,22 +3,29 @@
3
3
  module Doorkeeper
4
4
  module Orm
5
5
  module ActiveRecord
6
+ # Helper class to clear stale and non-active tokens and grants.
7
+ # Used by Doorkeeper Rake tasks.
8
+ #
6
9
  class StaleRecordsCleaner
7
10
  def initialize(base_scope)
8
11
  @base_scope = base_scope
9
12
  end
10
13
 
14
+ # Clears revoked records
11
15
  def clean_revoked
12
16
  table = @base_scope.arel_table
17
+
13
18
  @base_scope.where.not(revoked_at: nil)
14
- .where(table[:revoked_at].lt(Time.current))
15
- .delete_all
19
+ .where(table[:revoked_at].lt(Time.current))
20
+ .in_batches(&:delete_all)
16
21
  end
17
22
 
23
+ # Clears expired records
18
24
  def clean_expired(ttl)
19
25
  table = @base_scope.arel_table
26
+
20
27
  @base_scope.where(table[:created_at].lt(Time.current - ttl))
21
- .delete_all
28
+ .in_batches(&:delete_all)
22
29
  end
23
30
  end
24
31
  end
@@ -1,15 +1,17 @@
1
- require 'active_support/lazy_load_hooks'
1
+ # frozen_string_literal: true
2
2
 
3
- require 'doorkeeper/orm/active_record/stale_records_cleaner'
3
+ require "active_support/lazy_load_hooks"
4
+
5
+ require "doorkeeper/orm/active_record/stale_records_cleaner"
4
6
 
5
7
  module Doorkeeper
6
8
  module Orm
7
9
  module ActiveRecord
8
10
  def self.initialize_models!
9
11
  lazy_load do
10
- require 'doorkeeper/orm/active_record/access_grant'
11
- require 'doorkeeper/orm/active_record/access_token'
12
- require 'doorkeeper/orm/active_record/application'
12
+ require "doorkeeper/orm/active_record/access_grant"
13
+ require "doorkeeper/orm/active_record/access_token"
14
+ require "doorkeeper/orm/active_record/application"
13
15
 
14
16
  if Doorkeeper.configuration.active_record_options[:establish_connection]
15
17
  [Doorkeeper::AccessGrant, Doorkeeper::AccessToken, Doorkeeper::Application].each do |model|
@@ -22,7 +24,7 @@ module Doorkeeper
22
24
 
23
25
  def self.initialize_application_owner!
24
26
  lazy_load do
25
- require 'doorkeeper/models/concerns/ownership'
27
+ require "doorkeeper/models/concerns/ownership"
26
28
 
27
29
  Doorkeeper::Application.send :include, Doorkeeper::Models::Ownership
28
30
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Rails
3
5
  module Helpers
@@ -12,13 +14,15 @@ module Doorkeeper
12
14
  def doorkeeper_forbidden_render_options(**); end
13
15
 
14
16
  def valid_doorkeeper_token?
15
- doorkeeper_token && doorkeeper_token.acceptable?(@_doorkeeper_scopes)
17
+ doorkeeper_token&.acceptable?(@_doorkeeper_scopes)
16
18
  end
17
19
 
18
20
  private
19
21
 
20
22
  def doorkeeper_render_error
21
23
  error = doorkeeper_error
24
+ error.raise_exception! if Doorkeeper.configuration.raise_on_errors?
25
+
22
26
  headers.merge!(error.headers.reject { |k| k == "Content-Type" })
23
27
  doorkeeper_render_error_with(error)
24
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Rails
3
5
  class Routes # :nodoc:
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Rails
3
5
  class Routes # :nodoc:
@@ -6,17 +8,17 @@ module Doorkeeper
6
8
 
7
9
  def initialize
8
10
  @controllers = {
9
- authorizations: 'doorkeeper/authorizations',
10
- applications: 'doorkeeper/applications',
11
- authorized_applications: 'doorkeeper/authorized_applications',
12
- tokens: 'doorkeeper/tokens',
13
- token_info: 'doorkeeper/token_info'
11
+ authorizations: "doorkeeper/authorizations",
12
+ applications: "doorkeeper/applications",
13
+ authorized_applications: "doorkeeper/authorized_applications",
14
+ tokens: "doorkeeper/tokens",
15
+ token_info: "doorkeeper/token_info",
14
16
  }
15
17
 
16
18
  @as = {
17
19
  authorizations: :authorization,
18
20
  tokens: :token,
19
- token_info: :token_info
21
+ token_info: :token_info,
20
22
  }
21
23
 
22
24
  @skips = []
@@ -25,7 +27,7 @@ module Doorkeeper
25
27
  def [](routes)
26
28
  {
27
29
  controllers: @controllers[routes],
28
- as: @as[routes]
30
+ as: @as[routes],
29
31
  }
30
32
  end
31
33
 
@@ -1,5 +1,7 @@
1
- require 'doorkeeper/rails/routes/mapping'
2
- require 'doorkeeper/rails/routes/mapper'
1
+ # frozen_string_literal: true
2
+
3
+ require "doorkeeper/rails/routes/mapping"
4
+ require "doorkeeper/rails/routes/mapper"
3
5
 
4
6
  module Doorkeeper
5
7
  module Rails
@@ -30,7 +32,7 @@ module Doorkeeper
30
32
  end
31
33
 
32
34
  def generate_routes!(options)
33
- routes.scope options[:scope] || 'oauth', as: 'oauth' do
35
+ routes.scope options[:scope] || "oauth", as: "oauth" do
34
36
  map_route(:authorizations, :authorization_routes)
35
37
  map_route(:tokens, :token_routes)
36
38
  map_route(:tokens, :revoke_routes)
@@ -54,48 +56,53 @@ module Doorkeeper
54
56
  def authorization_routes(mapping)
55
57
  routes.resource(
56
58
  :authorization,
57
- path: 'authorize',
59
+ path: "authorize",
58
60
  only: %i[create destroy],
59
61
  as: mapping[:as],
60
62
  controller: mapping[:controllers]
61
63
  ) do
62
- routes.get '/native', action: :show, on: :member
63
- routes.get '/', action: :new, on: :member
64
+ routes.get "/native", action: :show, on: :member
65
+ routes.get "/", action: :new, on: :member
64
66
  end
65
67
  end
66
68
 
67
69
  def token_routes(mapping)
68
70
  routes.resource(
69
71
  :token,
70
- path: 'token',
72
+ path: "token",
71
73
  only: [:create], as: mapping[:as],
72
74
  controller: mapping[:controllers]
73
75
  )
74
76
  end
75
77
 
76
78
  def revoke_routes(mapping)
77
- routes.post 'revoke', controller: mapping[:controllers], action: :revoke
79
+ routes.post "revoke", controller: mapping[:controllers], action: :revoke
78
80
  end
79
81
 
80
82
  def introspect_routes(mapping)
81
- routes.post 'introspect', controller: mapping[:controllers], action: :introspect
83
+ routes.post "introspect", controller: mapping[:controllers], action: :introspect
82
84
  end
83
85
 
84
86
  def token_info_routes(mapping)
85
87
  routes.resource(
86
88
  :token_info,
87
- path: 'token/info',
89
+ path: "token/info",
88
90
  only: [:show], as: mapping[:as],
89
91
  controller: mapping[:controllers]
90
92
  )
91
93
  end
92
94
 
93
95
  def application_routes(mapping)
94
- routes.resources :doorkeeper_applications, controller: mapping[:controllers], as: :applications, path: 'applications'
96
+ routes.resources :doorkeeper_applications,
97
+ controller: mapping[:controllers],
98
+ as: :applications,
99
+ path: "applications"
95
100
  end
96
101
 
97
102
  def authorized_applications_routes(mapping)
98
- routes.resources :authorized_applications, only: %i[index destroy], controller: mapping[:controllers]
103
+ routes.resources :authorized_applications,
104
+ only: %i[index destroy],
105
+ controller: mapping[:controllers]
99
106
  end
100
107
  end
101
108
  end
@@ -2,37 +2,37 @@
2
2
 
3
3
  namespace :doorkeeper do
4
4
  namespace :db do
5
- desc 'Removes stale data from doorkeeper related database tables'
5
+ desc "Removes stale data from doorkeeper related database tables"
6
6
  task cleanup: [
7
- 'doorkeeper:db:cleanup:revoked_tokens',
8
- 'doorkeeper:db:cleanup:expired_tokens',
9
- 'doorkeeper:db:cleanup:revoked_grants',
10
- 'doorkeeper:db:cleanup:expired_grants'
7
+ "doorkeeper:db:cleanup:revoked_tokens",
8
+ "doorkeeper:db:cleanup:expired_tokens",
9
+ "doorkeeper:db:cleanup:revoked_grants",
10
+ "doorkeeper:db:cleanup:expired_grants",
11
11
  ]
12
12
 
13
13
  namespace :cleanup do
14
- desc 'Removes stale access tokens'
15
- task revoked_tokens: 'doorkeeper:setup' do
16
- cleaner = Doorkeeper::Orm::ActiveRecord::StaleRecordsCleaner.new(Doorkeeper::AccessToken)
14
+ desc "Removes stale access tokens"
15
+ task revoked_tokens: "doorkeeper:setup" do
16
+ cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessToken)
17
17
  cleaner.clean_revoked
18
18
  end
19
19
 
20
- desc 'Removes expired (TTL passed) access tokens'
21
- task expired_tokens: 'doorkeeper:setup' do
20
+ desc "Removes expired (TTL passed) access tokens"
21
+ task expired_tokens: "doorkeeper:setup" do
22
22
  expirable_tokens = Doorkeeper::AccessToken.where(refresh_token: nil)
23
- cleaner = Doorkeeper::Orm::ActiveRecord::StaleRecordsCleaner.new(expirable_tokens)
23
+ cleaner = Doorkeeper::StaleRecordsCleaner.new(expirable_tokens)
24
24
  cleaner.clean_expired(Doorkeeper.configuration.access_token_expires_in)
25
25
  end
26
26
 
27
- desc 'Removes stale access grants'
28
- task revoked_grants: 'doorkeeper:setup' do
29
- cleaner = Doorkeeper::Orm::ActiveRecord::StaleRecordsCleaner.new(Doorkeeper::AccessGrant)
27
+ desc "Removes stale access grants"
28
+ task revoked_grants: "doorkeeper:setup" do
29
+ cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessGrant)
30
30
  cleaner.clean_revoked
31
31
  end
32
32
 
33
- desc 'Removes expired (TTL passed) access grants'
34
- task expired_grants: 'doorkeeper:setup' do
35
- cleaner = Doorkeeper::Orm::ActiveRecord::StaleRecordsCleaner.new(Doorkeeper::AccessGrant)
33
+ desc "Removes expired (TTL passed) access grants"
34
+ task expired_grants: "doorkeeper:setup" do
35
+ cleaner = Doorkeeper::StaleRecordsCleaner.new(Doorkeeper::AccessGrant)
36
36
  cleaner.clean_expired(Doorkeeper.configuration.authorization_code_expires_in)
37
37
  end
38
38
  end
@@ -4,7 +4,7 @@ module Doorkeeper
4
4
  module Rake
5
5
  class << self
6
6
  def load_tasks
7
- glob = File.join(File.absolute_path(__dir__), 'rake', '*.rake')
7
+ glob = File.join(File.absolute_path(__dir__), "rake", "*.rake")
8
8
  Dir[glob].each do |rake_file|
9
9
  load rake_file
10
10
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module Request
3
5
  class AuthorizationCode < Strategy