doorkeeper 5.1.0 → 5.3.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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +3 -3
  3. data/CHANGELOG.md +881 -0
  4. data/CONTRIBUTING.md +11 -9
  5. data/Dangerfile +2 -2
  6. data/Dockerfile +29 -0
  7. data/Gemfile +4 -3
  8. data/NEWS.md +1 -814
  9. data/README.md +11 -3
  10. data/RELEASING.md +6 -5
  11. data/app/controllers/doorkeeper/application_controller.rb +2 -2
  12. data/app/controllers/doorkeeper/application_metal_controller.rb +3 -2
  13. data/app/controllers/doorkeeper/applications_controller.rb +1 -0
  14. data/app/controllers/doorkeeper/authorizations_controller.rb +16 -9
  15. data/app/controllers/doorkeeper/authorized_applications_controller.rb +2 -2
  16. data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
  17. data/app/views/doorkeeper/applications/_form.html.erb +0 -6
  18. data/app/views/doorkeeper/applications/show.html.erb +1 -1
  19. data/config/locales/en.yml +8 -2
  20. data/doorkeeper.gemspec +9 -1
  21. data/gemfiles/rails_5_0.gemfile +3 -2
  22. data/gemfiles/rails_5_1.gemfile +3 -2
  23. data/gemfiles/rails_5_2.gemfile +3 -2
  24. data/gemfiles/rails_6_0.gemfile +4 -3
  25. data/gemfiles/rails_master.gemfile +3 -2
  26. data/lib/doorkeeper/config/option.rb +13 -7
  27. data/lib/doorkeeper/config.rb +157 -42
  28. data/lib/doorkeeper/errors.rb +13 -18
  29. data/lib/doorkeeper/grape/helpers.rb +6 -2
  30. data/lib/doorkeeper/helpers/controller.rb +28 -7
  31. data/lib/doorkeeper/models/access_grant_mixin.rb +7 -6
  32. data/lib/doorkeeper/models/access_token_mixin.rb +99 -20
  33. data/lib/doorkeeper/models/application_mixin.rb +3 -3
  34. data/lib/doorkeeper/models/concerns/ownership.rb +1 -1
  35. data/lib/doorkeeper/models/concerns/reusable.rb +1 -1
  36. data/lib/doorkeeper/models/concerns/revocable.rb +0 -27
  37. data/lib/doorkeeper/oauth/authorization/code.rb +12 -14
  38. data/lib/doorkeeper/oauth/authorization/token.rb +10 -7
  39. data/lib/doorkeeper/oauth/authorization_code_request.rb +31 -15
  40. data/lib/doorkeeper/oauth/base_request.rb +10 -4
  41. data/lib/doorkeeper/oauth/client.rb +7 -8
  42. data/lib/doorkeeper/oauth/client_credentials/creator.rb +23 -2
  43. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +7 -7
  44. data/lib/doorkeeper/oauth/client_credentials/{validation.rb → validator.rb} +10 -2
  45. data/lib/doorkeeper/oauth/client_credentials_request.rb +1 -1
  46. data/lib/doorkeeper/oauth/code_request.rb +5 -11
  47. data/lib/doorkeeper/oauth/code_response.rb +4 -4
  48. data/lib/doorkeeper/oauth/error.rb +1 -1
  49. data/lib/doorkeeper/oauth/error_response.rb +6 -6
  50. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +7 -5
  51. data/lib/doorkeeper/oauth/helpers/unique_token.rb +8 -5
  52. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +19 -5
  53. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  54. data/lib/doorkeeper/oauth/invalid_token_response.rb +5 -2
  55. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  56. data/lib/doorkeeper/oauth/password_access_token_request.rb +9 -4
  57. data/lib/doorkeeper/oauth/pre_authorization.rb +76 -38
  58. data/lib/doorkeeper/oauth/refresh_token_request.rb +17 -14
  59. data/lib/doorkeeper/oauth/token.rb +2 -2
  60. data/lib/doorkeeper/oauth/token_introspection.rb +26 -16
  61. data/lib/doorkeeper/oauth/token_request.rb +4 -18
  62. data/lib/doorkeeper/orm/active_record/access_grant.rb +4 -43
  63. data/lib/doorkeeper/orm/active_record/access_token.rb +4 -35
  64. data/lib/doorkeeper/orm/active_record/application.rb +5 -83
  65. data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +53 -0
  66. data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +47 -0
  67. data/lib/doorkeeper/orm/active_record/mixins/application.rb +128 -0
  68. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
  69. data/lib/doorkeeper/orm/active_record.rb +21 -6
  70. data/lib/doorkeeper/rails/helpers.rb +4 -4
  71. data/lib/doorkeeper/rails/routes.rb +5 -7
  72. data/lib/doorkeeper/rake/db.rake +3 -3
  73. data/lib/doorkeeper/request/authorization_code.rb +5 -3
  74. data/lib/doorkeeper/request/client_credentials.rb +2 -2
  75. data/lib/doorkeeper/request/password.rb +2 -2
  76. data/lib/doorkeeper/request/refresh_token.rb +3 -3
  77. data/lib/doorkeeper/request.rb +7 -12
  78. data/lib/doorkeeper/server.rb +3 -7
  79. data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
  80. data/lib/doorkeeper/version.rb +1 -1
  81. data/lib/doorkeeper.rb +6 -3
  82. data/lib/generators/doorkeeper/application_owner_generator.rb +1 -1
  83. data/lib/generators/doorkeeper/confidential_applications_generator.rb +1 -1
  84. data/lib/generators/doorkeeper/migration_generator.rb +1 -1
  85. data/lib/generators/doorkeeper/pkce_generator.rb +1 -1
  86. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +7 -7
  87. data/lib/generators/doorkeeper/templates/initializer.rb +149 -33
  88. data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
  89. data/spec/controllers/application_metal_controller_spec.rb +1 -1
  90. data/spec/controllers/applications_controller_spec.rb +95 -1
  91. data/spec/controllers/authorizations_controller_spec.rb +156 -75
  92. data/spec/controllers/protected_resources_controller_spec.rb +28 -20
  93. data/spec/controllers/token_info_controller_spec.rb +1 -1
  94. data/spec/controllers/tokens_controller_spec.rb +206 -38
  95. data/spec/dummy/config/application.rb +3 -1
  96. data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
  97. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -3
  98. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +1 -1
  99. data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +1 -1
  100. data/spec/generators/install_generator_spec.rb +1 -1
  101. data/spec/generators/previous_refresh_token_generator_spec.rb +2 -2
  102. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  103. data/spec/lib/config_spec.rb +104 -7
  104. data/spec/lib/doorkeeper_spec.rb +1 -1
  105. data/spec/lib/models/revocable_spec.rb +3 -3
  106. data/spec/lib/oauth/authorization_code_request_spec.rb +129 -115
  107. data/spec/lib/oauth/base_request_spec.rb +154 -135
  108. data/spec/lib/oauth/base_response_spec.rb +27 -29
  109. data/spec/lib/oauth/client/credentials_spec.rb +1 -1
  110. data/spec/lib/oauth/client_credentials/creator_spec.rb +43 -3
  111. data/spec/lib/oauth/client_credentials/issuer_spec.rb +12 -12
  112. data/spec/lib/oauth/client_credentials/validation_spec.rb +4 -4
  113. data/spec/lib/oauth/client_credentials_integration_spec.rb +16 -18
  114. data/spec/lib/oauth/client_credentials_request_spec.rb +78 -80
  115. data/spec/lib/oauth/client_spec.rb +26 -26
  116. data/spec/lib/oauth/code_request_spec.rb +34 -35
  117. data/spec/lib/oauth/code_response_spec.rb +21 -25
  118. data/spec/lib/oauth/error_response_spec.rb +42 -44
  119. data/spec/lib/oauth/error_spec.rb +12 -14
  120. data/spec/lib/oauth/forbidden_token_response_spec.rb +11 -13
  121. data/spec/lib/oauth/helpers/scope_checker_spec.rb +30 -18
  122. data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
  123. data/spec/lib/oauth/invalid_request_response_spec.rb +73 -0
  124. data/spec/lib/oauth/invalid_token_response_spec.rb +32 -34
  125. data/spec/lib/oauth/password_access_token_request_spec.rb +145 -147
  126. data/spec/lib/oauth/pre_authorization_spec.rb +161 -153
  127. data/spec/lib/oauth/refresh_token_request_spec.rb +138 -138
  128. data/spec/lib/oauth/scopes_spec.rb +104 -106
  129. data/spec/lib/oauth/token_request_spec.rb +117 -110
  130. data/spec/lib/oauth/token_response_spec.rb +71 -73
  131. data/spec/lib/oauth/token_spec.rb +121 -123
  132. data/spec/lib/server_spec.rb +0 -12
  133. data/spec/models/doorkeeper/access_grant_spec.rb +21 -4
  134. data/spec/models/doorkeeper/access_token_spec.rb +42 -11
  135. data/spec/models/doorkeeper/application_spec.rb +114 -77
  136. data/spec/requests/applications/applications_request_spec.rb +1 -1
  137. data/spec/requests/endpoints/authorization_spec.rb +26 -8
  138. data/spec/requests/endpoints/token_spec.rb +1 -1
  139. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
  140. data/spec/requests/flows/authorization_code_spec.rb +123 -45
  141. data/spec/requests/flows/client_credentials_spec.rb +38 -0
  142. data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
  143. data/spec/requests/flows/implicit_grant_spec.rb +9 -8
  144. data/spec/requests/flows/password_spec.rb +67 -10
  145. data/spec/requests/flows/refresh_token_spec.rb +20 -20
  146. data/spec/requests/flows/revoke_token_spec.rb +37 -23
  147. data/spec/spec_helper.rb +1 -4
  148. data/spec/support/doorkeeper_rspec.rb +1 -1
  149. data/spec/support/helpers/request_spec_helper.rb +14 -2
  150. data/spec/support/shared/controllers_shared_context.rb +33 -23
  151. data/spec/validators/redirect_uri_validator_spec.rb +41 -16
  152. metadata +19 -16
  153. data/.coveralls.yml +0 -1
  154. data/.github/ISSUE_TEMPLATE.md +0 -25
  155. data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  156. data/.gitignore +0 -20
  157. data/.gitlab-ci.yml +0 -16
  158. data/.hound.yml +0 -3
  159. data/.rspec +0 -1
  160. data/.rubocop.yml +0 -50
  161. data/.travis.yml +0 -35
  162. data/app/validators/redirect_uri_validator.rb +0 -50
  163. data/spec/support/http_method_shim.rb +0 -29
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Doorkeeper.configure do
4
- # Change the ORM that doorkeeper will use (needs plugins)
4
+ # Change the ORM that doorkeeper will use (requires ORM extensions installed).
5
+ # Check the list of supported ORMs here: https://github.com/doorkeeper-gem/doorkeeper#orms
5
6
  orm :active_record
6
7
 
7
8
  # This block will be called to check whether the resource owner is authenticated or not.
@@ -9,7 +10,7 @@ Doorkeeper.configure do
9
10
  raise "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}"
10
11
  # Put your resource owner authentication logic here.
11
12
  # Example implementation:
12
- # User.find_by_id(session[:user_id]) || redirect_to(new_user_session_url)
13
+ # User.find_by(id: session[:user_id]) || redirect_to(new_user_session_url)
13
14
  end
14
15
 
15
16
  # If you didn't skip applications controller from Doorkeeper routes in your application routes.rb
@@ -28,6 +29,35 @@ Doorkeeper.configure do
28
29
  # end
29
30
  # end
30
31
 
32
+ # You can use your own model classes if you need to extend (or even override) default
33
+ # Doorkeeper models such as `Application`, `AccessToken` and `AccessGrant.
34
+ #
35
+ # Be default Doorkeeper ActiveRecord ORM uses it's own classes:
36
+ #
37
+ # access_token_class "Doorkeeper::AccessToken"
38
+ # access_grant_class "Doorkeeper::AccessGrant"
39
+ # application_class "Doorkeeper::Application"
40
+ #
41
+ # Don't forget to include Doorkeeper ORM mixins into your custom models:
42
+ #
43
+ # * ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken - for access token
44
+ # * ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessGrant - for access grant
45
+ # * ::Doorkeeper::Orm::ActiveRecord::Mixins::Application - for application (OAuth2 clients)
46
+ #
47
+ # For example:
48
+ #
49
+ # access_token_class "MyAccessToken"
50
+ #
51
+ # class MyAccessToken < ApplicationRecord
52
+ # include ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken
53
+ #
54
+ # self.table_name = "hey_i_wanna_my_name"
55
+ #
56
+ # def destroy_me!
57
+ # destroy
58
+ # end
59
+ # end
60
+
31
61
  # If you are planning to use Doorkeeper in Rails 5 API-only application, then you might
32
62
  # want to use API mode that will skip all the views management and change the way how
33
63
  # Doorkeeper responds to a requests.
@@ -39,18 +69,18 @@ Doorkeeper.configure do
39
69
  #
40
70
  # enforce_content_type
41
71
 
42
- # Authorization Code expiration time (default 10 minutes).
72
+ # Authorization Code expiration time (default: 10 minutes).
43
73
  #
44
74
  # authorization_code_expires_in 10.minutes
45
75
 
46
- # Access token expiration time (default 2 hours).
47
- # If you want to disable expiration, set this to nil.
76
+ # Access token expiration time (default: 2 hours).
77
+ # If you want to disable expiration, set this to `nil`.
48
78
  #
49
79
  # access_token_expires_in 2.hours
50
80
 
51
81
  # Assign custom TTL for access tokens. Will be used instead of access_token_expires_in
52
82
  # option if defined. In case the block returns `nil` value Doorkeeper fallbacks to
53
- # `access_token_expires_in` configuration option value. If you really need to issue a
83
+ # +access_token_expires_in+ configuration option value. If you really need to issue a
54
84
  # non-expiring access token (which is not recommended) then you need to return
55
85
  # Float::INFINITY from this block.
56
86
  #
@@ -65,12 +95,13 @@ Doorkeeper.configure do
65
95
  # end
66
96
 
67
97
  # Use a custom class for generating the access token.
68
- # See https://github.com/doorkeeper-gem/doorkeeper#custom-access-token-generator
98
+ # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-access-token-generator
69
99
  #
70
100
  # access_token_generator '::Doorkeeper::JWT'
71
101
 
72
- # The controller Doorkeeper::ApplicationController inherits from.
73
- # Defaults to ActionController::Base.
102
+ # The controller +Doorkeeper::ApplicationController+ inherits from.
103
+ # Defaults to +ActionController::Base+ unless +api_only+ is set, which changes the default to
104
+ # +ActionController::API+. The return value of this option must be a stringified class name.
74
105
  # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-base-controller
75
106
  #
76
107
  # base_controller 'ApplicationController'
@@ -96,6 +127,16 @@ Doorkeeper.configure do
96
127
  #
97
128
  # token_reuse_limit 100
98
129
 
130
+ # Only allow one valid access token obtained via client credentials
131
+ # per client. If a new access token is obtained before the old one
132
+ # expired, the old one gets revoked (disabled by default)
133
+ #
134
+ # When enabling this option, make sure that you do not expect multiple processes
135
+ # using the same credentials at the same time (e.g. web servers spanning
136
+ # multiple machines and/or processes).
137
+ #
138
+ # revoke_previous_client_credentials_token
139
+
99
140
  # Hash access and refresh tokens before persisting them.
100
141
  # This will disable the possibility to use +reuse_access_token+
101
142
  # since plain values can no longer be retrieved.
@@ -128,11 +169,10 @@ Doorkeeper.configure do
128
169
  #
129
170
  # hash_application_secrets using: '::Doorkeeper::SecretStoring::BCrypt'
130
171
 
131
- # When the above option is enabled,
132
- # and a hashed token or secret is not found,
133
- # you can allow to fall back to another strategy.
134
- # For users upgrading doorkeeper and wishing to enable hashing,
135
- # you will probably want to enable the fallback to plain tokens.
172
+ # When the above option is enabled, and a hashed token or secret is not found,
173
+ # you can allow to fall back to another strategy. For users upgrading
174
+ # doorkeeper and wishing to enable hashing, you will probably want to enable
175
+ # the fallback to plain tokens.
136
176
  #
137
177
  # This will ensure that old access tokens and secrets
138
178
  # will remain valid even if the hashing above is enabled.
@@ -141,8 +181,8 @@ Doorkeeper.configure do
141
181
 
142
182
  # Issue access tokens with refresh token (disabled by default), you may also
143
183
  # pass a block which accepts `context` to customize when to give a refresh
144
- # token or not. Similar to `custom_access_token_expires_in`, `context` has
145
- # the properties:
184
+ # token or not. Similar to +custom_access_token_expires_in+, `context` has
185
+ # the following properties:
146
186
  #
147
187
  # `client` - the OAuth client application (see Doorkeeper::OAuth::Client)
148
188
  # `grant_type` - the grant type of the request (see Doorkeeper::OAuth)
@@ -151,7 +191,7 @@ Doorkeeper.configure do
151
191
  # use_refresh_token
152
192
 
153
193
  # Provide support for an owner to be assigned to each registered application (disabled by default)
154
- # Optional parameter confirmation: true (default false) if you want to enforce ownership of
194
+ # Optional parameter confirmation: true (default: false) if you want to enforce ownership of
155
195
  # a registered application
156
196
  # NOTE: you must also run the rails g doorkeeper:application_owner generator
157
197
  # to provide the necessary support
@@ -160,22 +200,22 @@ Doorkeeper.configure do
160
200
 
161
201
  # Define access token scopes for your provider
162
202
  # For more information go to
163
- # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
203
+ # https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes
164
204
  #
165
205
  # default_scopes :public
166
206
  # optional_scopes :write, :update
167
207
 
168
- # Define scopes_by_grant_type to restrict only certain scopes for grant_type
208
+ # Allows to restrict only certain scopes for grant_type.
169
209
  # By default, all the scopes will be available for all the grant types.
170
210
  #
171
211
  # Keys to this hash should be the name of grant_type and
172
212
  # values should be the array of scopes for that grant type.
173
- # Note: scopes should be from configured_scopes(i.e. deafult or optional)
213
+ # Note: scopes should be from configured_scopes (i.e. default or optional)
174
214
  #
175
215
  # scopes_by_grant_type password: [:write], client_credentials: [:update]
176
216
 
177
217
  # Forbids creating/updating applications with arbitrary scopes that are
178
- # not in configuration, i.e. `default_scopes` or `optional_scopes`.
218
+ # not in configuration, i.e. +default_scopes+ or +optional_scopes+.
179
219
  # (disabled by default)
180
220
  #
181
221
  # enforce_configured_scopes
@@ -196,15 +236,6 @@ Doorkeeper.configure do
196
236
  #
197
237
  # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
198
238
 
199
- # Change the native redirect uri for client apps
200
- # When clients register with the following redirect uri, they won't be redirected to
201
- # any server and the authorizationcode will be displayed within the provider
202
- # The value can be any string. Use nil to disable this feature. When disabled, clients
203
- # must providea valid URL
204
- # (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
205
- #
206
- # native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
207
-
208
239
  # Forces the usage of the HTTPS protocol in non-native redirect uris (enabled
209
240
  # by default in non-development environments). OAuth2 delegates security in
210
241
  # communication to the HTTPS protocol so it is wise to keep this enabled.
@@ -246,7 +277,7 @@ Doorkeeper.configure do
246
277
  # is invalid, expired, revoked or has invalid scopes.
247
278
  #
248
279
  # If you want to render error response yourself (i.e. rescue exceptions),
249
- # set handle_auth_errors to `:raise` and rescue Doorkeeper::Errors::InvalidToken
280
+ # set +handle_auth_errors+ to `:raise` and rescue Doorkeeper::Errors::InvalidToken
250
281
  # or following specific errors:
251
282
  #
252
283
  # Doorkeeper::Errors::TokenForbidden, Doorkeeper::Errors::TokenExpired,
@@ -290,6 +321,37 @@ Doorkeeper.configure do
290
321
  #
291
322
  # grant_flows %w[authorization_code client_credentials]
292
323
 
324
+ # Allows to customize OAuth grant flows that +each+ application support.
325
+ # You can configure a custom block (or use a class respond to `#call`) that must
326
+ # return `true` in case Application instance supports requested OAuth grant flow
327
+ # during the authorization request to the server. This configuration +doesn't+
328
+ # set flows per application, it only allows to check if application supports
329
+ # specific grant flow.
330
+ #
331
+ # For example you can add an additional database column to `oauth_applications` table,
332
+ # say `t.array :grant_flows, default: []`, and store allowed grant flows that can
333
+ # be used with this application there. Then when authorization requested Doorkeeper
334
+ # will call this block to check if specific Application (passed with client_id and/or
335
+ # client_secret) is allowed to perform the request for the specific grant type
336
+ # (authorization, password, client_credentials, etc).
337
+ #
338
+ # Example of the block:
339
+ #
340
+ # ->(flow, client) { client.grant_flows.include?(flow) }
341
+ #
342
+ # In case this option invocation result is `false`, Doorkeeper server returns
343
+ # :unauthorized_client error and stops the request.
344
+ #
345
+ # @param allow_grant_flow_for_client [Proc] Block or any object respond to #call
346
+ # @return [Boolean] `true` if allow or `false` if forbid the request
347
+ #
348
+ # allow_grant_flow_for_client do |grant_flow, client|
349
+ # # `grant_flows` is an Array column with grant
350
+ # # flows that application supports
351
+ #
352
+ # client.grant_flows.include?(grant_flow)
353
+ # end
354
+
293
355
  # Hook into the strategies' request & response life-cycle in case your
294
356
  # application needs advanced customization or logging:
295
357
  #
@@ -305,7 +367,7 @@ Doorkeeper.configure do
305
367
  # or add any other functionality.
306
368
  #
307
369
  # before_successful_authorization do |controller|
308
- # Rails.logger.info(params.inspect)
370
+ # Rails.logger.info(controller.request.params.inspect)
309
371
  # end
310
372
  #
311
373
  # after_successful_authorization do |controller|
@@ -323,7 +385,61 @@ Doorkeeper.configure do
323
385
  # client.superapp? or resource_owner.admin?
324
386
  # end
325
387
 
326
- # WWW-Authenticate Realm (default "Doorkeeper").
388
+ # Configure custom constraints for the Token Introspection request.
389
+ # By default this configuration option allows to introspect a token by another
390
+ # token of the same application, OR to introspect the token that belongs to
391
+ # authorized client (from authenticated client) OR when token doesn't
392
+ # belong to any client (public token). Otherwise requester has no access to the
393
+ # introspection and it will return response as stated in the RFC.
394
+ #
395
+ # Block arguments:
396
+ #
397
+ # @param token [Doorkeeper::AccessToken]
398
+ # token to be introspected
399
+ #
400
+ # @param authorized_client [Doorkeeper::Application]
401
+ # authorized client (if request is authorized using Basic auth with
402
+ # Client Credentials for example)
403
+ #
404
+ # @param authorized_token [Doorkeeper::AccessToken]
405
+ # Bearer token used to authorize the request
406
+ #
407
+ # In case the block returns `nil` or `false` introspection responses with 401 status code
408
+ # when using authorized token to introspect, or you'll get 200 with { "active": false } body
409
+ # when using authorized client to introspect as stated in the
410
+ # RFC 7662 section 2.2. Introspection Response.
411
+ #
412
+ # Using with caution:
413
+ # Keep in mind that these three parameters pass to block can be nil as following case:
414
+ # `authorized_client` is nil if and only if `authorized_token` is present, and vice versa.
415
+ # `token` will be nil if and only if `authorized_token` is present.
416
+ # So remember to use `&` or check if it is present before calling method on
417
+ # them to make sure you doesn't get NoMethodError exception.
418
+ #
419
+ # You can define your custom check:
420
+ #
421
+ # allow_token_introspection do |token, authorized_client, authorized_token|
422
+ # if authorized_token
423
+ # # customize: require `introspection` scope
424
+ # authorized_token.application == token&.application ||
425
+ # authorized_token.scopes.include?("introspection")
426
+ # elsif token.application
427
+ # # `protected_resource` is a new database boolean column, for example
428
+ # authorized_client == token.application || authorized_client.protected_resource?
429
+ # else
430
+ # # public token (when token.application is nil, token doesn't belong to any application)
431
+ # true
432
+ # end
433
+ # end
434
+ #
435
+ # Or you can completely disable any token introspection:
436
+ #
437
+ # allow_token_introspection false
438
+ #
439
+ # If you need to block the request at all, then configure your routes.rb or web-server
440
+ # like nginx to forbid the request.
441
+
442
+ # WWW-Authenticate Realm (default: "Doorkeeper").
327
443
  #
328
444
  # realm "Doorkeeper"
329
445
  end
@@ -24,7 +24,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration<%= migration_version %>
24
24
  t.text :redirect_uri, null: false
25
25
  t.datetime :created_at, null: false
26
26
  t.datetime :revoked_at
27
- t.string :scopes
27
+ t.string :scopes, null: false, default: ''
28
28
  end
29
29
 
30
30
  add_index :oauth_access_grants, :token, unique: true
@@ -36,6 +36,9 @@ class CreateDoorkeeperTables < ActiveRecord::Migration<%= migration_version %>
36
36
 
37
37
  create_table :oauth_access_tokens do |t|
38
38
  t.references :resource_owner, index: true
39
+
40
+ # Remove `null: false` if you are planning to use Password
41
+ # Credentials Grant flow that doesn't require an application.
39
42
  t.references :application, null: false
40
43
 
41
44
  # If you use a custom token generator you may need to change this column
@@ -21,7 +21,7 @@ describe Doorkeeper::ApplicationMetalController do
21
21
  end
22
22
 
23
23
  describe "enforce_content_type" do
24
- before { allow(Doorkeeper.configuration).to receive(:enforce_content_type).and_return(flag) }
24
+ before { allow(Doorkeeper.config).to receive(:enforce_content_type).and_return(flag) }
25
25
 
26
26
  context "enabled" do
27
27
  let(:flag) { true }
@@ -26,6 +26,10 @@ module Doorkeeper
26
26
 
27
27
  expect(json_response).to include("id", "name", "uid", "secret", "redirect_uri", "scopes")
28
28
 
29
+ application = Application.last
30
+ secret_from_response = json_response["secret"]
31
+ expect(application.secret_matches?(secret_from_response)).to be_truthy
32
+
29
33
  expect(json_response["name"]).to eq("Example")
30
34
  expect(json_response["redirect_uri"]).to eq("https://example.com")
31
35
  end
@@ -44,6 +48,21 @@ module Doorkeeper
44
48
  expect(json_response).to include("errors")
45
49
  end
46
50
 
51
+ it "returns validations on wrong create params (unspecified scheme)" do
52
+ expect do
53
+ post :create, params: {
54
+ doorkeeper_application: {
55
+ name: "Example",
56
+ redirect_uri: "app.com:80",
57
+ }, format: :json,
58
+ }
59
+ end.not_to(change { Doorkeeper::Application.count })
60
+
61
+ expect(response).to have_http_status(422)
62
+
63
+ expect(json_response).to include("errors")
64
+ end
65
+
47
66
  it "returns application info" do
48
67
  application = FactoryBot.create(:application, name: "Change me")
49
68
 
@@ -98,7 +117,7 @@ module Doorkeeper
98
117
 
99
118
  context "when admin is not authenticated" do
100
119
  before do
101
- allow(Doorkeeper.configuration).to receive(:authenticate_admin).and_return(proc do
120
+ allow(Doorkeeper.config).to receive(:authenticate_admin).and_return(proc do
102
121
  redirect_to main_app.root_url
103
122
  end)
104
123
  end
@@ -121,6 +140,73 @@ module Doorkeeper
121
140
  end
122
141
 
123
142
  context "when admin is authenticated" do
143
+ context "when application secrets are hashed" do
144
+ before do
145
+ allow(Doorkeeper.configuration)
146
+ .to receive(:application_secret_strategy).and_return(Doorkeeper::SecretStoring::Sha256Hash)
147
+ end
148
+
149
+ it "shows the application secret after creating a new application" do
150
+ expect do
151
+ post :create, params: {
152
+ doorkeeper_application: {
153
+ name: "Example",
154
+ redirect_uri: "https://example.com",
155
+ },
156
+ }
157
+ end.to change { Doorkeeper::Application.count }.by(1)
158
+
159
+ application = Application.last
160
+
161
+ secret_from_flash = flash[:application_secret]
162
+ expect(secret_from_flash).not_to be_empty
163
+ expect(application.secret_matches?(secret_from_flash)).to be_truthy
164
+ expect(response).to redirect_to(controller.main_app.oauth_application_url(application.id))
165
+
166
+ get :show, params: { id: application.id, format: :html }
167
+
168
+ # We don't know the application secret here (because its hashed) so we can not assert its text on the page
169
+ # Instead, we read it from the page and then check if it matches the application secret
170
+ code_element = %r{<code.*id="secret".*>(.*)<\/code>}.match(response.body)
171
+ secret_from_page = code_element[1]
172
+
173
+ expect(response.body).to have_selector("code#application_id", text: application.uid)
174
+ expect(response.body).to have_selector("code#secret")
175
+ expect(secret_from_page).not_to be_empty
176
+ expect(application.secret_matches?(secret_from_page)).to be_truthy
177
+ end
178
+
179
+ it "does not show an application secret when application did already exist" do
180
+ application = FactoryBot.create(:application)
181
+ get :show, params: { id: application.id, format: :html }
182
+
183
+ expect(response.body).to have_selector("code#application_id", text: application.uid)
184
+ expect(response.body).to have_selector("code#secret", text: "")
185
+ end
186
+
187
+ it "returns the application details in a json response" do
188
+ expect do
189
+ post :create, params: {
190
+ doorkeeper_application: {
191
+ name: "Example",
192
+ redirect_uri: "https://example.com",
193
+ }, format: :json,
194
+ }
195
+ end.to(change { Doorkeeper::Application.count })
196
+
197
+ expect(response).to be_successful
198
+
199
+ expect(json_response).to include("id", "name", "uid", "secret", "redirect_uri", "scopes")
200
+
201
+ application = Application.last
202
+ secret_from_response = json_response["secret"]
203
+ expect(application.secret_matches?(secret_from_response)).to be_truthy
204
+
205
+ expect(json_response["name"]).to eq("Example")
206
+ expect(json_response["redirect_uri"]).to eq("https://example.com")
207
+ end
208
+ end
209
+
124
210
  render_views
125
211
 
126
212
  before do
@@ -151,6 +237,14 @@ module Doorkeeper
151
237
  expect(response).to be_redirect
152
238
  end
153
239
 
240
+ it "shows application details" do
241
+ application = FactoryBot.create(:application)
242
+ get :show, params: { id: application.id, format: :html }
243
+
244
+ expect(response.body).to have_selector("code#application_id", text: application.uid)
245
+ expect(response.body).to have_selector("code#secret", text: application.plaintext_secret)
246
+ end
247
+
154
248
  it "does not allow mass assignment of uid or secret" do
155
249
  application = FactoryBot.create(:application)
156
250
  put :update, params: {