doorkeeper 4.3.2 → 5.0.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 (188) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.gitlab-ci.yml +16 -0
  4. data/.rubocop.yml +4 -0
  5. data/.travis.yml +2 -0
  6. data/Appraisals +2 -2
  7. data/Gemfile +1 -1
  8. data/NEWS.md +70 -0
  9. data/README.md +92 -9
  10. data/Rakefile +6 -0
  11. data/UPGRADE.md +2 -0
  12. data/app/assets/stylesheets/doorkeeper/admin/application.css +2 -2
  13. data/app/controllers/doorkeeper/application_controller.rb +4 -3
  14. data/app/controllers/doorkeeper/application_metal_controller.rb +4 -0
  15. data/app/controllers/doorkeeper/applications_controller.rb +43 -22
  16. data/app/controllers/doorkeeper/authorizations_controller.rb +55 -12
  17. data/app/controllers/doorkeeper/authorized_applications_controller.rb +19 -2
  18. data/app/controllers/doorkeeper/tokens_controller.rb +11 -16
  19. data/app/helpers/doorkeeper/dashboard_helper.rb +7 -7
  20. data/app/validators/redirect_uri_validator.rb +3 -2
  21. data/app/views/doorkeeper/applications/_delete_form.html.erb +3 -1
  22. data/app/views/doorkeeper/applications/_form.html.erb +31 -19
  23. data/app/views/doorkeeper/applications/edit.html.erb +1 -1
  24. data/app/views/doorkeeper/applications/index.html.erb +18 -6
  25. data/app/views/doorkeeper/applications/new.html.erb +1 -1
  26. data/app/views/doorkeeper/applications/show.html.erb +8 -5
  27. data/app/views/doorkeeper/authorizations/error.html.erb +1 -1
  28. data/app/views/doorkeeper/authorizations/new.html.erb +4 -0
  29. data/app/views/layouts/doorkeeper/admin.html.erb +15 -15
  30. data/config/locales/en.yml +16 -1
  31. data/doorkeeper.gemspec +18 -18
  32. data/gemfiles/rails_5_2.gemfile +1 -1
  33. data/gemfiles/rails_master.gemfile +4 -1
  34. data/lib/doorkeeper/config.rb +77 -27
  35. data/lib/doorkeeper/engine.rb +4 -0
  36. data/lib/doorkeeper/errors.rb +2 -5
  37. data/lib/doorkeeper/grape/helpers.rb +1 -1
  38. data/lib/doorkeeper/helpers/controller.rb +7 -2
  39. data/lib/doorkeeper/models/access_grant_mixin.rb +71 -0
  40. data/lib/doorkeeper/models/access_token_mixin.rb +40 -23
  41. data/lib/doorkeeper/models/application_mixin.rb +8 -1
  42. data/lib/doorkeeper/models/concerns/scopes.rb +1 -1
  43. data/lib/doorkeeper/oauth/authorization/code.rb +31 -8
  44. data/lib/doorkeeper/oauth/authorization/context.rb +15 -0
  45. data/lib/doorkeeper/oauth/authorization/token.rb +36 -14
  46. data/lib/doorkeeper/oauth/authorization_code_request.rb +27 -2
  47. data/lib/doorkeeper/oauth/base_request.rb +20 -9
  48. data/lib/doorkeeper/oauth/client/credentials.rb +4 -2
  49. data/lib/doorkeeper/oauth/client.rb +0 -2
  50. data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -1
  51. data/lib/doorkeeper/oauth/client_credentials/issuer.rb +6 -3
  52. data/lib/doorkeeper/oauth/client_credentials/validation.rb +4 -6
  53. data/lib/doorkeeper/oauth/client_credentials_request.rb +0 -4
  54. data/lib/doorkeeper/oauth/error_response.rb +11 -3
  55. data/lib/doorkeeper/oauth/helpers/scope_checker.rb +0 -8
  56. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +1 -0
  57. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -4
  58. data/lib/doorkeeper/oauth/pre_authorization.rb +45 -13
  59. data/lib/doorkeeper/oauth/refresh_token_request.rb +6 -1
  60. data/lib/doorkeeper/oauth/scopes.rb +1 -1
  61. data/lib/doorkeeper/oauth/token.rb +5 -2
  62. data/lib/doorkeeper/oauth/token_introspection.rb +2 -2
  63. data/lib/doorkeeper/oauth/token_response.rb +4 -2
  64. data/lib/doorkeeper/oauth.rb +13 -0
  65. data/lib/doorkeeper/orm/active_record/application.rb +25 -0
  66. data/lib/doorkeeper/orm/active_record/stale_records_cleaner.rb +26 -0
  67. data/lib/doorkeeper/orm/active_record.rb +2 -0
  68. data/lib/doorkeeper/rails/helpers.rb +2 -4
  69. data/lib/doorkeeper/rails/routes.rb +13 -1
  70. data/lib/doorkeeper/rake/db.rake +40 -0
  71. data/lib/doorkeeper/rake/setup.rake +6 -0
  72. data/lib/doorkeeper/rake.rb +14 -0
  73. data/lib/doorkeeper/request/authorization_code.rb +0 -2
  74. data/lib/doorkeeper/request/client_credentials.rb +0 -2
  75. data/lib/doorkeeper/request/code.rb +0 -2
  76. data/lib/doorkeeper/request/password.rb +1 -13
  77. data/lib/doorkeeper/request/refresh_token.rb +0 -2
  78. data/lib/doorkeeper/request/token.rb +0 -2
  79. data/lib/doorkeeper/request.rb +28 -35
  80. data/lib/doorkeeper/version.rb +5 -4
  81. data/lib/doorkeeper.rb +19 -17
  82. data/lib/generators/doorkeeper/application_owner_generator.rb +23 -18
  83. data/lib/generators/doorkeeper/confidential_applications_generator.rb +32 -0
  84. data/lib/generators/doorkeeper/install_generator.rb +17 -9
  85. data/lib/generators/doorkeeper/migration_generator.rb +23 -18
  86. data/lib/generators/doorkeeper/pkce_generator.rb +32 -0
  87. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +29 -24
  88. data/lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb +13 -0
  89. data/lib/generators/doorkeeper/templates/enable_pkce_migration.rb.erb +6 -0
  90. data/lib/generators/doorkeeper/templates/initializer.rb +76 -11
  91. data/lib/generators/doorkeeper/templates/migration.rb.erb +1 -0
  92. data/lib/generators/doorkeeper/views_generator.rb +3 -1
  93. data/spec/controllers/application_metal_controller_spec.rb +50 -0
  94. data/spec/controllers/applications_controller_spec.rb +126 -13
  95. data/spec/controllers/authorizations_controller_spec.rb +279 -17
  96. data/spec/controllers/protected_resources_controller_spec.rb +16 -16
  97. data/spec/controllers/token_info_controller_spec.rb +4 -12
  98. data/spec/controllers/tokens_controller_spec.rb +72 -22
  99. data/spec/dummy/app/assets/config/manifest.js +2 -0
  100. data/spec/dummy/config/environments/test.rb +4 -5
  101. data/spec/dummy/config/initializers/doorkeeper.rb +11 -1
  102. data/spec/dummy/config/initializers/new_framework_defaults.rb +4 -0
  103. data/spec/dummy/config/routes.rb +3 -42
  104. data/spec/dummy/db/migrate/20111122132257_create_users.rb +3 -1
  105. data/spec/dummy/db/migrate/20120312140401_add_password_to_users.rb +3 -1
  106. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +3 -1
  107. data/spec/dummy/db/migrate/20151223200000_add_owner_to_application.rb +3 -1
  108. data/spec/dummy/db/migrate/20160320211015_add_previous_refresh_token_to_access_tokens.rb +3 -1
  109. data/spec/dummy/db/migrate/20170822064514_enable_pkce.rb +6 -0
  110. data/spec/dummy/db/migrate/20180210183654_add_confidential_to_applications.rb +13 -0
  111. data/spec/dummy/db/schema.rb +38 -37
  112. data/spec/generators/application_owner_generator_spec.rb +1 -1
  113. data/spec/generators/confidential_applications_generator_spec.rb +45 -0
  114. data/spec/generators/install_generator_spec.rb +1 -1
  115. data/spec/generators/migration_generator_spec.rb +1 -1
  116. data/spec/generators/pkce_generator_spec.rb +43 -0
  117. data/spec/generators/previous_refresh_token_generator_spec.rb +1 -1
  118. data/spec/generators/views_generator_spec.rb +1 -1
  119. data/spec/grape/grape_integration_spec.rb +1 -1
  120. data/spec/helpers/doorkeeper/dashboard_helper_spec.rb +1 -1
  121. data/spec/lib/config_spec.rb +88 -14
  122. data/spec/lib/doorkeeper_spec.rb +1 -126
  123. data/spec/lib/models/expirable_spec.rb +0 -3
  124. data/spec/lib/models/revocable_spec.rb +0 -2
  125. data/spec/lib/models/scopes_spec.rb +0 -4
  126. data/spec/lib/oauth/authorization/uri_builder_spec.rb +0 -4
  127. data/spec/lib/oauth/authorization_code_request_spec.rb +24 -2
  128. data/spec/lib/oauth/base_request_spec.rb +40 -2
  129. data/spec/lib/oauth/base_response_spec.rb +1 -1
  130. data/spec/lib/oauth/client/credentials_spec.rb +5 -5
  131. data/spec/lib/oauth/client_credentials/creator_spec.rb +5 -1
  132. data/spec/lib/oauth/client_credentials/issuer_spec.rb +26 -7
  133. data/spec/lib/oauth/client_credentials/validation_spec.rb +2 -3
  134. data/spec/lib/oauth/client_credentials_integration_spec.rb +1 -1
  135. data/spec/lib/oauth/client_credentials_request_spec.rb +3 -5
  136. data/spec/lib/oauth/client_spec.rb +0 -3
  137. data/spec/lib/oauth/code_request_spec.rb +4 -2
  138. data/spec/lib/oauth/error_response_spec.rb +0 -3
  139. data/spec/lib/oauth/error_spec.rb +0 -2
  140. data/spec/lib/oauth/forbidden_token_response_spec.rb +1 -4
  141. data/spec/lib/oauth/helpers/scope_checker_spec.rb +0 -3
  142. data/spec/lib/oauth/helpers/unique_token_spec.rb +0 -1
  143. data/spec/lib/oauth/helpers/uri_checker_spec.rb +5 -2
  144. data/spec/lib/oauth/invalid_token_response_spec.rb +1 -4
  145. data/spec/lib/oauth/password_access_token_request_spec.rb +37 -2
  146. data/spec/lib/oauth/pre_authorization_spec.rb +40 -6
  147. data/spec/lib/oauth/refresh_token_request_spec.rb +11 -7
  148. data/spec/lib/oauth/scopes_spec.rb +0 -3
  149. data/spec/lib/oauth/token_request_spec.rb +4 -5
  150. data/spec/lib/oauth/token_response_spec.rb +0 -1
  151. data/spec/lib/oauth/token_spec.rb +37 -14
  152. data/spec/lib/orm/active_record/stale_records_cleaner_spec.rb +79 -0
  153. data/spec/lib/request/strategy_spec.rb +0 -1
  154. data/spec/lib/server_spec.rb +1 -1
  155. data/spec/models/doorkeeper/access_grant_spec.rb +44 -1
  156. data/spec/models/doorkeeper/access_token_spec.rb +66 -22
  157. data/spec/models/doorkeeper/application_spec.rb +64 -6
  158. data/spec/requests/applications/applications_request_spec.rb +134 -1
  159. data/spec/requests/applications/authorized_applications_spec.rb +1 -1
  160. data/spec/requests/endpoints/authorization_spec.rb +1 -1
  161. data/spec/requests/endpoints/token_spec.rb +7 -5
  162. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -1
  163. data/spec/requests/flows/authorization_code_spec.rb +198 -2
  164. data/spec/requests/flows/client_credentials_spec.rb +46 -6
  165. data/spec/requests/flows/implicit_grant_errors_spec.rb +1 -1
  166. data/spec/requests/flows/implicit_grant_spec.rb +38 -11
  167. data/spec/requests/flows/password_spec.rb +120 -23
  168. data/spec/requests/flows/refresh_token_spec.rb +2 -2
  169. data/spec/requests/flows/revoke_token_spec.rb +11 -11
  170. data/spec/requests/flows/skip_authorization_spec.rb +16 -11
  171. data/spec/requests/protected_resources/metal_spec.rb +1 -1
  172. data/spec/requests/protected_resources/private_api_spec.rb +1 -1
  173. data/spec/routing/custom_controller_routes_spec.rb +59 -7
  174. data/spec/routing/default_routes_spec.rb +2 -2
  175. data/spec/routing/scoped_routes_spec.rb +16 -2
  176. data/spec/spec_helper.rb +54 -3
  177. data/spec/spec_helper_integration.rb +2 -74
  178. data/spec/support/doorkeeper_rspec.rb +19 -0
  179. data/spec/support/helpers/authorization_request_helper.rb +4 -4
  180. data/spec/support/helpers/request_spec_helper.rb +10 -2
  181. data/spec/support/helpers/url_helper.rb +7 -3
  182. data/spec/support/http_method_shim.rb +12 -16
  183. data/spec/validators/redirect_uri_validator_spec.rb +7 -1
  184. data/spec/version/version_spec.rb +3 -3
  185. data/vendor/assets/stylesheets/doorkeeper/bootstrap.min.css +4 -5
  186. metadata +36 -10
  187. data/spec/controllers/application_metal_controller.rb +0 -10
  188. /data/spec/support/dependencies/{factory_girl.rb → factory_bot.rb} +0 -0
@@ -6,18 +6,26 @@ module Doorkeeper
6
6
  validate :grant, error: :invalid_grant
7
7
  # @see https://tools.ietf.org/html/rfc6749#section-5.2
8
8
  validate :redirect_uri, error: :invalid_grant
9
+ validate :code_verifier, error: :invalid_grant
9
10
 
10
- attr_accessor :server, :grant, :client, :redirect_uri, :access_token
11
+ attr_accessor :server, :grant, :client, :redirect_uri, :access_token,
12
+ :code_verifier
11
13
 
12
14
  def initialize(server, grant, client, parameters = {})
13
15
  @server = server
14
16
  @client = client
15
17
  @grant = grant
18
+ @grant_type = Doorkeeper::OAuth::AUTHORIZATION_CODE
16
19
  @redirect_uri = parameters[:redirect_uri]
20
+ @code_verifier = parameters[:code_verifier]
17
21
  end
18
22
 
19
23
  private
20
24
 
25
+ def client_by_uid(parameters)
26
+ Doorkeeper::Application.by_uid(parameters[:client_id])
27
+ end
28
+
21
29
  def before_successful_response
22
30
  grant.transaction do
23
31
  grant.lock!
@@ -33,11 +41,13 @@ module Doorkeeper
33
41
  end
34
42
 
35
43
  def validate_attributes
44
+ return false if grant && grant.uses_pkce? && code_verifier.blank?
45
+ return false if grant && !grant.pkce_supported? && !code_verifier.blank?
36
46
  redirect_uri.present?
37
47
  end
38
48
 
39
49
  def validate_client
40
- !!client
50
+ !client.nil?
41
51
  end
42
52
 
43
53
  def validate_grant
@@ -51,6 +61,21 @@ module Doorkeeper
51
61
  grant.redirect_uri
52
62
  )
53
63
  end
64
+
65
+ # if either side (server or client) request pkce, check the verifier
66
+ # against the DB - if pkce is supported
67
+ def validate_code_verifier
68
+ return true unless grant.uses_pkce? || code_verifier
69
+ return false unless grant.pkce_supported?
70
+
71
+ if grant.code_challenge_method == 'S256'
72
+ grant.code_challenge == AccessGrant.generate_code_challenge(code_verifier)
73
+ elsif grant.code_challenge_method == 'plain'
74
+ grant.code_challenge == code_verifier
75
+ else
76
+ false
77
+ end
78
+ end
54
79
  end
55
80
  end
56
81
  end
@@ -3,6 +3,8 @@ module Doorkeeper
3
3
  class BaseRequest
4
4
  include Validations
5
5
 
6
+ attr_reader :grant_type
7
+
6
8
  def authorize
7
9
  validate
8
10
 
@@ -17,11 +19,7 @@ module Doorkeeper
17
19
  end
18
20
 
19
21
  def scopes
20
- @scopes ||= if @original_scopes.present?
21
- OAuth::Scopes.from_string(@original_scopes)
22
- else
23
- default_scopes
24
- end
22
+ @scopes ||= build_scopes
25
23
  end
26
24
 
27
25
  def default_scopes
@@ -33,12 +31,13 @@ module Doorkeeper
33
31
  end
34
32
 
35
33
  def find_or_create_access_token(client, resource_owner_id, scopes, server)
34
+ context = Authorization::Token.build_context(client, grant_type, scopes)
36
35
  @access_token = AccessToken.find_or_create_for(
37
36
  client,
38
37
  resource_owner_id,
39
38
  scopes,
40
- Authorization::Token.access_token_expires_in(server, client),
41
- server.refresh_token_enabled?
39
+ Authorization::Token.access_token_expires_in(server, context),
40
+ Authorization::Token.refresh_token_enabled?(server, context)
42
41
  )
43
42
  end
44
43
 
@@ -47,8 +46,20 @@ module Doorkeeper
47
46
  end
48
47
 
49
48
  def after_successful_response
50
- Doorkeeper.configuration.after_successful_strategy_response.
51
- call(self, @response)
49
+ Doorkeeper.configuration.after_successful_strategy_response.call(self, @response)
50
+ end
51
+
52
+ private
53
+
54
+ def build_scopes
55
+ if @original_scopes.present?
56
+ OAuth::Scopes.from_string(@original_scopes)
57
+ else
58
+ client_scopes = @client.try(:scopes)
59
+ return default_scopes if client_scopes.blank?
60
+
61
+ default_scopes & @client.scopes
62
+ end
52
63
  end
53
64
  end
54
65
  end
@@ -4,7 +4,7 @@ module Doorkeeper
4
4
  Credentials = Struct.new(:uid, :secret) do
5
5
  class << self
6
6
  def from_request(request, *credentials_methods)
7
- credentials_methods.inject(nil) do |credentials, method|
7
+ credentials_methods.inject(nil) do |_, method|
8
8
  method = self.method(method) if method.is_a?(Symbol)
9
9
  credentials = Credentials.new(*method.call(request))
10
10
  break credentials unless credentials.blank?
@@ -23,8 +23,10 @@ module Doorkeeper
23
23
  end
24
24
  end
25
25
 
26
+ # Public clients may have their secret blank, but "credentials" are
27
+ # still present
26
28
  def blank?
27
- uid.blank? || secret.blank?
29
+ uid.blank?
28
30
  end
29
31
  end
30
32
  end
@@ -1,5 +1,3 @@
1
- require 'doorkeeper/oauth/client/credentials'
2
-
3
1
  module Doorkeeper
4
2
  module OAuth
5
3
  class Client
@@ -5,7 +5,8 @@ module Doorkeeper
5
5
  def call(client, scopes, attributes = {})
6
6
  AccessToken.find_or_create_for(
7
7
  client, nil, scopes, attributes[:expires_in],
8
- attributes[:use_refresh_token])
8
+ attributes[:use_refresh_token]
9
+ )
9
10
  end
10
11
  end
11
12
  end
@@ -1,5 +1,3 @@
1
- require 'doorkeeper/oauth/client_credentials/validation'
2
-
3
1
  module Doorkeeper
4
2
  module OAuth
5
3
  class ClientCredentialsRequest < BaseRequest
@@ -25,7 +23,12 @@ module Doorkeeper
25
23
  private
26
24
 
27
25
  def create_token(client, scopes, creator)
28
- ttl = Authorization::Token.access_token_expires_in(@server, client)
26
+ context = Authorization::Token.build_context(
27
+ client,
28
+ Doorkeeper::OAuth::CLIENT_CREDENTIALS,
29
+ scopes
30
+ )
31
+ ttl = Authorization::Token.access_token_expires_in(@server, context)
29
32
 
30
33
  creator.call(
31
34
  client,
@@ -1,7 +1,3 @@
1
- require 'doorkeeper/validations'
2
- require 'doorkeeper/oauth/scopes'
3
- require 'doorkeeper/oauth/helpers/scope_checker'
4
-
5
1
  module Doorkeeper
6
2
  module OAuth
7
3
  class ClientCredentialsRequest < BaseRequest
@@ -13,7 +9,9 @@ module Doorkeeper
13
9
  validate :scopes, error: :invalid_scope
14
10
 
15
11
  def initialize(server, request)
16
- @server, @request, @client = server, request, request.client
12
+ @server = server
13
+ @request = request
14
+ @client = request.client
17
15
 
18
16
  validate
19
17
  end
@@ -25,7 +23,7 @@ module Doorkeeper
25
23
  end
26
24
 
27
25
  def validate_scopes
28
- return true unless @request.scopes.present?
26
+ return true if @request.scopes.blank?
29
27
 
30
28
  application_scopes = if @client.present?
31
29
  @client.application.scopes
@@ -1,7 +1,3 @@
1
- require 'doorkeeper/oauth/client_credentials/creator'
2
- require 'doorkeeper/oauth/client_credentials/issuer'
3
- require 'doorkeeper/oauth/client_credentials/validation'
4
-
5
1
  module Doorkeeper
6
2
  module OAuth
7
3
  class ClientCredentialsRequest < BaseRequest
@@ -4,7 +4,13 @@ module Doorkeeper
4
4
  include OAuth::Helpers
5
5
 
6
6
  def self.from_request(request, attributes = {})
7
- new(attributes.merge(name: request.error, state: request.try(:state)))
7
+ new(
8
+ attributes.merge(
9
+ name: request.error,
10
+ state: request.try(:state),
11
+ redirect_uri: request.try(:redirect_uri)
12
+ )
13
+ )
8
14
  end
9
15
 
10
16
  delegate :name, :description, :state, to: :@error
@@ -41,10 +47,12 @@ module Doorkeeper
41
47
  end
42
48
 
43
49
  def headers
44
- { 'Cache-Control' => 'no-store',
50
+ {
51
+ 'Cache-Control' => 'no-store',
45
52
  'Pragma' => 'no-cache',
46
53
  'Content-Type' => 'application/json; charset=utf-8',
47
- 'WWW-Authenticate' => authenticate_info }
54
+ 'WWW-Authenticate' => authenticate_info
55
+ }
48
56
  end
49
57
 
50
58
  protected
@@ -17,10 +17,6 @@ module Doorkeeper
17
17
  @valid_scopes.has_scopes?(parsed_scopes)
18
18
  end
19
19
 
20
- def match?
21
- valid? && parsed_scopes.has_scopes?(@valid_scopes)
22
- end
23
-
24
20
  private
25
21
 
26
22
  def valid_scopes(server_scopes, application_scopes)
@@ -35,10 +31,6 @@ module Doorkeeper
35
31
  def self.valid?(scope_str, server_scopes, application_scopes = nil)
36
32
  Validator.new(scope_str, server_scopes, application_scopes).valid?
37
33
  end
38
-
39
- def self.match?(scope_str, server_scopes, application_scopes = nil)
40
- Validator.new(scope_str, server_scopes, application_scopes).match?
41
- end
42
34
  end
43
35
  end
44
36
  end
@@ -3,6 +3,7 @@ module Doorkeeper
3
3
  module Helpers
4
4
  module URIChecker
5
5
  def self.valid?(url)
6
+ return true if native_uri?(url)
6
7
  uri = as_uri(url)
7
8
  uri.fragment.nil? && !uri.host.nil? && !uri.scheme.nil?
8
9
  rescue URI::InvalidURIError
@@ -16,6 +16,7 @@ module Doorkeeper
16
16
  @client = client
17
17
  @parameters = parameters
18
18
  @original_scopes = parameters[:scope]
19
+ @grant_type = Doorkeeper::OAuth::PASSWORD
19
20
  end
20
21
 
21
22
  private
@@ -26,16 +27,18 @@ module Doorkeeper
26
27
  end
27
28
 
28
29
  def validate_scopes
29
- return true unless @original_scopes.present?
30
- ScopeChecker.valid? @original_scopes, server.scopes, client.try(:scopes)
30
+ client_scopes = client.try(:scopes)
31
+ return true if scopes.blank?
32
+
33
+ ScopeChecker.valid?(scopes.to_s, server.scopes, client_scopes)
31
34
  end
32
35
 
33
36
  def validate_resource_owner
34
- !!resource_owner
37
+ !resource_owner.nil?
35
38
  end
36
39
 
37
40
  def validate_client
38
- !parameters[:client_id] || !!client
41
+ !parameters[:client_id] || !client.nil?
39
42
  end
40
43
  end
41
44
  end
@@ -7,17 +7,21 @@ module Doorkeeper
7
7
  validate :client, error: :invalid_client
8
8
  validate :scopes, error: :invalid_scope
9
9
  validate :redirect_uri, error: :invalid_redirect_uri
10
+ validate :code_challenge_method, error: :invalid_code_challenge_method
10
11
 
11
- attr_accessor :server, :client, :response_type, :redirect_uri, :state
12
+ attr_accessor :server, :client, :response_type, :redirect_uri, :state,
13
+ :code_challenge, :code_challenge_method
12
14
  attr_writer :scope
13
15
 
14
16
  def initialize(server, client, attrs = {})
15
- @server = server
16
- @client = client
17
- @response_type = attrs[:response_type]
18
- @redirect_uri = attrs[:redirect_uri]
19
- @scope = attrs[:scope]
20
- @state = attrs[:state]
17
+ @server = server
18
+ @client = client
19
+ @response_type = attrs[:response_type]
20
+ @redirect_uri = attrs[:redirect_uri]
21
+ @scope = attrs[:scope]
22
+ @state = attrs[:state]
23
+ @code_challenge = attrs[:code_challenge]
24
+ @code_challenge_method = attrs[:code_challenge_method]
21
25
  end
22
26
 
23
27
  def authorizable?
@@ -29,15 +33,36 @@ module Doorkeeper
29
33
  end
30
34
 
31
35
  def scope
32
- @scope.presence || server.default_scopes.to_s
36
+ @scope.presence || build_scopes
33
37
  end
34
38
 
35
39
  def error_response
36
40
  OAuth::ErrorResponse.from_request(self)
37
41
  end
38
42
 
43
+ def as_json(_options)
44
+ {
45
+ client_id: client.uid,
46
+ redirect_uri: redirect_uri,
47
+ state: state,
48
+ response_type: response_type,
49
+ scope: scope,
50
+ client_name: client.name,
51
+ status: I18n.t('doorkeeper.pre_authorization.status')
52
+ }
53
+ end
54
+
39
55
  private
40
56
 
57
+ def build_scopes
58
+ client_scopes = client.application.scopes
59
+ if client_scopes.blank?
60
+ server.default_scopes.to_s
61
+ else
62
+ (server.default_scopes & client_scopes).to_s
63
+ end
64
+ end
65
+
41
66
  def validate_response_type
42
67
  server.authorization_response_types.include? response_type
43
68
  end
@@ -47,7 +72,8 @@ module Doorkeeper
47
72
  end
48
73
 
49
74
  def validate_scopes
50
- return true unless scope.present?
75
+ return true if scope.blank?
76
+
51
77
  Helpers::ScopeChecker.valid?(
52
78
  scope,
53
79
  server.scopes,
@@ -55,11 +81,17 @@ module Doorkeeper
55
81
  )
56
82
  end
57
83
 
58
- # TODO: test uri should be matched against the client's one
59
84
  def validate_redirect_uri
60
- return false unless redirect_uri.present?
61
- Helpers::URIChecker.native_uri?(redirect_uri) ||
62
- Helpers::URIChecker.valid_for_authorization?(redirect_uri, client.redirect_uri)
85
+ return false if redirect_uri.blank?
86
+
87
+ Helpers::URIChecker.valid_for_authorization?(
88
+ redirect_uri,
89
+ client.redirect_uri
90
+ )
91
+ end
92
+
93
+ def validate_code_challenge_method
94
+ !code_challenge.present? || (code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
63
95
  end
64
96
  end
65
97
  end
@@ -65,7 +65,12 @@ module Doorkeeper
65
65
  end
66
66
 
67
67
  def access_token_expires_in
68
- Authorization::Token.access_token_expires_in(server, client)
68
+ context = Authorization::Token.build_context(
69
+ client,
70
+ Doorkeeper::OAuth::REFRESH_TOKEN,
71
+ scopes
72
+ )
73
+ Authorization::Token.access_token_expires_in(server, context)
69
74
  end
70
75
 
71
76
  def validate_token_presence
@@ -41,7 +41,7 @@ module Doorkeeper
41
41
  end
42
42
 
43
43
  def has_scopes?(scopes)
44
- scopes.all? { |s| exists?(s) }
44
+ scopes.all? { |scope| exists?(scope) }
45
45
  end
46
46
 
47
47
  def +(other)
@@ -3,7 +3,7 @@ module Doorkeeper
3
3
  class Token
4
4
  class << self
5
5
  def from_request(request, *methods)
6
- methods.inject(nil) do |credentials, method|
6
+ methods.inject(nil) do |_, method|
7
7
  method = self.method(method) if method.is_a?(Symbol)
8
8
  credentials = method.call(request)
9
9
  break credentials unless credentials.blank?
@@ -13,7 +13,10 @@ module Doorkeeper
13
13
  def authenticate(request, *methods)
14
14
  if (token = from_request(request, *methods))
15
15
  access_token = AccessToken.by_token(token)
16
- access_token.revoke_previous_refresh_token! if access_token
16
+ refresh_token_enabled = Doorkeeper.configuration.refresh_token_enabled?
17
+ if access_token.present? && refresh_token_enabled
18
+ access_token.revoke_previous_refresh_token!
19
+ end
17
20
  access_token
18
21
  end
19
22
  end
@@ -47,12 +47,12 @@ module Doorkeeper
47
47
 
48
48
  # Client Authentication
49
49
  def authorized_client
50
- @_authorized_client ||= server.credentials && server.client
50
+ @authorized_client ||= server.credentials && server.client
51
51
  end
52
52
 
53
53
  # Bearer Token Authentication
54
54
  def authorized_token
55
- @_authorized_token ||=
55
+ @authorized_token ||=
56
56
  OAuth::Token.authenticate(server.context.request, :from_bearer_authorization)
57
57
  end
58
58
 
@@ -23,9 +23,11 @@ module Doorkeeper
23
23
  end
24
24
 
25
25
  def headers
26
- { 'Cache-Control' => 'no-store',
26
+ {
27
+ 'Cache-Control' => 'no-store',
27
28
  'Pragma' => 'no-cache',
28
- 'Content-Type' => 'application/json; charset=utf-8' }
29
+ 'Content-Type' => 'application/json; charset=utf-8'
30
+ }
29
31
  end
30
32
  end
31
33
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
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
11
+ ].freeze
12
+ end
13
+ end
@@ -11,6 +11,9 @@ module Doorkeeper
11
11
  validates :name, :secret, :uid, presence: true
12
12
  validates :uid, uniqueness: true
13
13
  validates :redirect_uri, redirect_uri: true
14
+ validates :confidential, inclusion: { in: [true, false] }
15
+
16
+ validate :scopes_match_configured, if: :enforce_scopes?
14
17
 
15
18
  before_validation :generate_uid, :generate_secret, on: :create
16
19
 
@@ -31,6 +34,17 @@ module Doorkeeper
31
34
  where(id: resource_access_tokens.select(:application_id).distinct)
32
35
  end
33
36
 
37
+ # Revokes AccessToken and AccessGrant records that have not been revoked and
38
+ # associated with the specific Application and Resource Owner.
39
+ #
40
+ # @param resource_owner [ActiveRecord::Base]
41
+ # instance of the Resource Owner model
42
+ #
43
+ def self.revoke_tokens_and_grants_for(id, resource_owner)
44
+ AccessToken.revoke_all_for(id, resource_owner)
45
+ AccessGrant.revoke_all_for(id, resource_owner)
46
+ end
47
+
34
48
  private
35
49
 
36
50
  def generate_uid
@@ -40,5 +54,16 @@ module Doorkeeper
40
54
  def generate_secret
41
55
  self.secret = UniqueToken.generate if secret.blank?
42
56
  end
57
+
58
+ def scopes_match_configured
59
+ if scopes.present? &&
60
+ !ScopeChecker.valid?(scopes.to_s, Doorkeeper.configuration.scopes)
61
+ errors.add(:scopes, :not_match_configured)
62
+ end
63
+ end
64
+
65
+ def enforce_scopes?
66
+ Doorkeeper.configuration.enforce_configured_scopes?
67
+ end
43
68
  end
44
69
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module Orm
5
+ module ActiveRecord
6
+ class StaleRecordsCleaner
7
+ def initialize(base_scope)
8
+ @base_scope = base_scope
9
+ end
10
+
11
+ def clean_revoked
12
+ table = @base_scope.arel_table
13
+ @base_scope.where.not(revoked_at: nil)
14
+ .where(table[:revoked_at].lt(Time.current))
15
+ .delete_all
16
+ end
17
+
18
+ def clean_expired(ttl)
19
+ table = @base_scope.arel_table
20
+ @base_scope.where(table[:created_at].lt(Time.current - ttl))
21
+ .delete_all
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,7 @@
1
1
  require 'active_support/lazy_load_hooks'
2
2
 
3
+ require 'doorkeeper/orm/active_record/stale_records_cleaner'
4
+
3
5
  module Doorkeeper
4
6
  module Orm
5
7
  module ActiveRecord
@@ -4,9 +4,7 @@ module Doorkeeper
4
4
  def doorkeeper_authorize!(*scopes)
5
5
  @_doorkeeper_scopes = scopes.presence || Doorkeeper.configuration.default_scopes
6
6
 
7
- unless valid_doorkeeper_token?
8
- doorkeeper_render_error
9
- end
7
+ doorkeeper_render_error unless valid_doorkeeper_token?
10
8
  end
11
9
 
12
10
  def doorkeeper_unauthorized_render_options(**); end
@@ -68,7 +66,7 @@ module Doorkeeper
68
66
  end
69
67
 
70
68
  def doorkeeper_token
71
- @_doorkeeper_token ||= OAuth::Token.authenticate(
69
+ @doorkeeper_token ||= OAuth::Token.authenticate(
72
70
  request,
73
71
  *Doorkeeper.configuration.access_token_methods
74
72
  )
@@ -4,6 +4,10 @@ require 'doorkeeper/rails/routes/mapper'
4
4
  module Doorkeeper
5
5
  module Rails
6
6
  class Routes # :nodoc:
7
+ mattr_reader :mapping do
8
+ {}
9
+ end
10
+
7
11
  module Helper
8
12
  def use_doorkeeper(options = {}, &block)
9
13
  Doorkeeper::Rails::Routes.new(self, &block).generate_routes!(options)
@@ -19,6 +23,10 @@ module Doorkeeper
19
23
  def initialize(routes, &block)
20
24
  @routes = routes
21
25
  @mapping = Mapper.new.map(&block)
26
+
27
+ if Doorkeeper.configuration.api_only
28
+ @mapping.skips.push(:applications, :authorized_applications)
29
+ end
22
30
  end
23
31
 
24
32
  def generate_routes!(options)
@@ -36,7 +44,11 @@ module Doorkeeper
36
44
  private
37
45
 
38
46
  def map_route(name, method)
39
- send(method, @mapping[name]) unless @mapping.skipped?(name)
47
+ return if @mapping.skipped?(name)
48
+
49
+ send(method, @mapping[name])
50
+
51
+ mapping[name] = @mapping[name]
40
52
  end
41
53
 
42
54
  def authorization_routes(mapping)