doorkeeper 5.2.0.rc2 → 5.2.0.rc3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of doorkeeper might be problematic. Click here for more details.

Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +1 -1
  3. data/CHANGELOG.md +15 -2
  4. data/Gemfile +1 -1
  5. data/README.md +9 -1
  6. data/app/controllers/doorkeeper/application_metal_controller.rb +1 -1
  7. data/app/controllers/doorkeeper/authorizations_controller.rb +11 -9
  8. data/config/locales/en.yml +5 -1
  9. data/doorkeeper.gemspec +8 -0
  10. data/gemfiles/rails_6_0.gemfile +1 -1
  11. data/lib/doorkeeper.rb +1 -0
  12. data/lib/doorkeeper/config.rb +41 -2
  13. data/lib/doorkeeper/errors.rb +13 -18
  14. data/lib/doorkeeper/helpers/controller.rb +6 -2
  15. data/lib/doorkeeper/oauth/authorization/code.rb +1 -5
  16. data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -9
  17. data/lib/doorkeeper/oauth/base_request.rb +2 -0
  18. data/lib/doorkeeper/oauth/client_credentials/validation.rb +8 -0
  19. data/lib/doorkeeper/oauth/code_request.rb +5 -11
  20. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  21. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
  22. data/lib/doorkeeper/oauth/pre_authorization.rb +70 -37
  23. data/lib/doorkeeper/oauth/refresh_token_request.rb +5 -2
  24. data/lib/doorkeeper/oauth/token_introspection.rb +4 -1
  25. data/lib/doorkeeper/oauth/token_request.rb +4 -18
  26. data/lib/doorkeeper/orm/active_record.rb +2 -2
  27. data/lib/doorkeeper/orm/active_record/application.rb +1 -1
  28. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
  29. data/lib/doorkeeper/request.rb +6 -11
  30. data/lib/doorkeeper/request/authorization_code.rb +2 -0
  31. data/lib/doorkeeper/server.rb +2 -6
  32. data/lib/doorkeeper/version.rb +1 -1
  33. data/lib/generators/doorkeeper/templates/initializer.rb +33 -2
  34. data/lib/generators/doorkeeper/templates/migration.rb.erb +1 -1
  35. data/spec/controllers/authorizations_controller_spec.rb +127 -61
  36. data/spec/controllers/protected_resources_controller_spec.rb +3 -3
  37. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +1 -1
  38. data/spec/lib/config_spec.rb +17 -0
  39. data/spec/lib/oauth/authorization_code_request_spec.rb +11 -1
  40. data/spec/lib/oauth/base_request_spec.rb +33 -16
  41. data/spec/lib/oauth/code_request_spec.rb +27 -28
  42. data/spec/lib/oauth/invalid_request_response_spec.rb +75 -0
  43. data/spec/lib/oauth/pre_authorization_spec.rb +80 -55
  44. data/spec/lib/oauth/refresh_token_request_spec.rb +1 -0
  45. data/spec/lib/oauth/token_request_spec.rb +20 -17
  46. data/spec/lib/server_spec.rb +0 -12
  47. data/spec/requests/endpoints/authorization_spec.rb +21 -5
  48. data/spec/requests/endpoints/token_spec.rb +1 -1
  49. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
  50. data/spec/requests/flows/authorization_code_spec.rb +77 -23
  51. data/spec/requests/flows/client_credentials_spec.rb +38 -0
  52. data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
  53. data/spec/requests/flows/implicit_grant_spec.rb +9 -8
  54. data/spec/requests/flows/password_spec.rb +37 -0
  55. data/spec/requests/flows/refresh_token_spec.rb +1 -1
  56. data/spec/support/helpers/request_spec_helper.rb +14 -2
  57. data/spec/validators/redirect_uri_validator_spec.rb +1 -1
  58. metadata +12 -4
  59. data/app/validators/redirect_uri_validator.rb +0 -60
@@ -3,28 +3,22 @@
3
3
  module Doorkeeper
4
4
  module OAuth
5
5
  class CodeRequest
6
- attr_accessor :pre_auth, :resource_owner, :client
6
+ attr_accessor :pre_auth, :resource_owner
7
7
 
8
8
  def initialize(pre_auth, resource_owner)
9
9
  @pre_auth = pre_auth
10
- @client = pre_auth.client
11
10
  @resource_owner = resource_owner
12
11
  end
13
12
 
14
13
  def authorize
15
- if pre_auth.authorizable?
16
- auth = Authorization::Code.new(pre_auth, resource_owner)
17
- auth.issue_token
18
- @response = CodeResponse.new pre_auth, auth
19
- else
20
- @response = ErrorResponse.from_request pre_auth
21
- end
14
+ auth = Authorization::Code.new(pre_auth, resource_owner)
15
+ auth.issue_token
16
+ CodeResponse.new(pre_auth, auth)
22
17
  end
23
18
 
24
19
  def deny
25
20
  pre_auth.error = :access_denied
26
- ErrorResponse.from_request pre_auth,
27
- redirect_uri: pre_auth.redirect_uri
21
+ pre_auth.error_response
28
22
  end
29
23
  end
30
24
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ class InvalidRequestResponse < ErrorResponse
6
+ attr_reader :reason
7
+
8
+ def self.from_request(request, attributes = {})
9
+ new(
10
+ attributes.merge(
11
+ state: request.try(:state),
12
+ redirect_uri: request.try(:redirect_uri),
13
+ missing_param: request.try(:missing_param),
14
+ reason: request.try(:invalid_request_reason)
15
+ )
16
+ )
17
+ end
18
+
19
+ def initialize(attributes = {})
20
+ super(attributes.merge(name: :invalid_request))
21
+ @missing_param = attributes[:missing_param]
22
+ @reason = @missing_param.nil? ? attributes[:reason] : :missing_param
23
+ end
24
+
25
+ def status
26
+ :bad_request
27
+ end
28
+
29
+ def description
30
+ I18n.translate(
31
+ reason,
32
+ scope: %i[doorkeeper errors messages invalid_request],
33
+ default: :unknown,
34
+ value: @missing_param
35
+ )
36
+ end
37
+
38
+ def redirectable?
39
+ super && @missing_param != :client_id
40
+ end
41
+ end
42
+ end
43
+ end
@@ -5,9 +5,10 @@ module Doorkeeper
5
5
  class PasswordAccessTokenRequest < BaseRequest
6
6
  include OAuth::Helpers
7
7
 
8
- validate :client, error: :invalid_client
8
+ validate :client, error: :invalid_client
9
+ validate :client_supports_grant_flow, error: :unauthorized_client
9
10
  validate :resource_owner, error: :invalid_grant
10
- validate :scopes, error: :invalid_scope
11
+ validate :scopes, error: :invalid_scope
11
12
 
12
13
  attr_accessor :server, :client, :resource_owner, :parameters,
13
14
  :access_token
@@ -47,6 +48,10 @@ module Doorkeeper
47
48
  def validate_client
48
49
  !parameters[:client_id] || client.present?
49
50
  end
51
+
52
+ def validate_client_supports_grant_flow
53
+ Doorkeeper.configuration.allow_grant_flow_for_client?(grant_type, client)
54
+ end
50
55
  end
51
56
  end
52
57
  end
@@ -5,19 +5,21 @@ module Doorkeeper
5
5
  class PreAuthorization
6
6
  include Validations
7
7
 
8
- validate :response_type, error: :unsupported_response_type
9
- validate :client, error: :invalid_client
10
- validate :scopes, error: :invalid_scope
11
- validate :redirect_uri, error: :invalid_redirect_uri
8
+ validate :client_id, error: :invalid_request
9
+ validate :client, error: :invalid_client
10
+ validate :redirect_uri, error: :invalid_redirect_uri
11
+ validate :params, error: :invalid_request
12
+ validate :response_type, error: :unsupported_response_type
13
+ validate :scopes, error: :invalid_scope
12
14
  validate :code_challenge_method, error: :invalid_code_challenge_method
15
+ validate :client_supports_grant_flow, error: :unauthorized_client
13
16
 
14
- attr_accessor :server, :client, :response_type, :redirect_uri, :state,
15
- :code_challenge, :code_challenge_method
16
- attr_writer :scope
17
+ attr_reader :server, :client_id, :client, :redirect_uri, :response_type, :state,
18
+ :code_challenge, :code_challenge_method, :missing_param
17
19
 
18
- def initialize(server, client, attrs = {})
20
+ def initialize(server, attrs = {})
19
21
  @server = server
20
- @client = client
22
+ @client_id = attrs[:client_id]
21
23
  @response_type = attrs[:response_type]
22
24
  @redirect_uri = attrs[:redirect_uri]
23
25
  @scope = attrs[:scope]
@@ -30,34 +32,38 @@ module Doorkeeper
30
32
  valid?
31
33
  end
32
34
 
35
+ def validate_client_supports_grant_flow
36
+ Doorkeeper.configuration.allow_grant_flow_for_client?(grant_type, client.application)
37
+ end
38
+
33
39
  def scopes
34
40
  Scopes.from_string scope
35
41
  end
36
42
 
37
43
  def scope
38
- @scope.presence || build_scopes
44
+ @scope.presence || (server.default_scopes.presence && build_scopes)
39
45
  end
40
46
 
41
47
  def error_response
42
- OAuth::ErrorResponse.from_request(self)
48
+ is_implicit_flow = response_type == "token"
49
+
50
+ if error == :invalid_request
51
+ OAuth::InvalidRequestResponse.from_request(self, response_on_fragment: is_implicit_flow)
52
+ else
53
+ OAuth::ErrorResponse.from_request(self, response_on_fragment: is_implicit_flow)
54
+ end
43
55
  end
44
56
 
45
- def as_json(_options)
46
- {
47
- client_id: client.uid,
48
- redirect_uri: redirect_uri,
49
- state: state,
50
- response_type: response_type,
51
- scope: scope,
52
- client_name: client.name,
53
- status: I18n.t("doorkeeper.pre_authorization.status"),
54
- }
57
+ def as_json(attributes = {})
58
+ return pre_auth_hash.merge(attributes.to_h) if attributes.respond_to?(:to_h)
59
+
60
+ pre_auth_hash
55
61
  end
56
62
 
57
63
  private
58
64
 
59
65
  def build_scopes
60
- client_scopes = client.application.scopes
66
+ client_scopes = client.scopes
61
67
  if client_scopes.blank?
62
68
  server.default_scopes.to_s
63
69
  else
@@ -65,21 +71,45 @@ module Doorkeeper
65
71
  end
66
72
  end
67
73
 
68
- def validate_response_type
69
- server.authorization_response_types.include? response_type
74
+ def validate_client_id
75
+ @missing_param = :client_id if client_id.blank?
76
+
77
+ @missing_param.nil?
70
78
  end
71
79
 
72
80
  def validate_client
73
- client.present?
81
+ @client = OAuth::Client.find(client_id)
82
+ @client.present?
74
83
  end
75
84
 
76
- def validate_scopes
77
- return true if scope.blank?
85
+ def validate_redirect_uri
86
+ return false if redirect_uri.blank?
87
+
88
+ Helpers::URIChecker.valid_for_authorization?(
89
+ redirect_uri,
90
+ client.redirect_uri
91
+ )
92
+ end
93
+
94
+ def validate_params
95
+ @missing_param = if response_type.blank?
96
+ :response_type
97
+ elsif @scope.blank? && server.default_scopes.blank?
98
+ :scope
99
+ end
78
100
 
101
+ @missing_param.nil?
102
+ end
103
+
104
+ def validate_response_type
105
+ server.authorization_response_types.include?(response_type)
106
+ end
107
+
108
+ def validate_scopes
79
109
  Helpers::ScopeChecker.valid?(
80
110
  scope_str: scope,
81
111
  server_scopes: server.scopes,
82
- app_scopes: client.application.scopes,
112
+ app_scopes: client.scopes,
83
113
  grant_type: grant_type
84
114
  )
85
115
  end
@@ -88,19 +118,22 @@ module Doorkeeper
88
118
  response_type == "code" ? AUTHORIZATION_CODE : IMPLICIT
89
119
  end
90
120
 
91
- def validate_redirect_uri
92
- return false if redirect_uri.blank?
93
-
94
- Helpers::URIChecker.valid_for_authorization?(
95
- redirect_uri,
96
- client.redirect_uri
97
- )
98
- end
99
-
100
121
  def validate_code_challenge_method
101
122
  code_challenge.blank? ||
102
123
  (code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
103
124
  end
125
+
126
+ def pre_auth_hash
127
+ {
128
+ client_id: client.uid,
129
+ redirect_uri: redirect_uri,
130
+ state: state,
131
+ response_type: response_type,
132
+ scope: scope,
133
+ client_name: client.name,
134
+ status: I18n.t("doorkeeper.pre_authorization.status"),
135
+ }
136
+ end
104
137
  end
105
138
  end
106
139
  end
@@ -13,6 +13,7 @@ module Doorkeeper
13
13
 
14
14
  attr_accessor :access_token, :client, :credentials, :refresh_token,
15
15
  :server
16
+ attr_reader :missing_param
16
17
 
17
18
  def initialize(server, refresh_token, credentials, parameters = {})
18
19
  @server = server
@@ -32,7 +33,7 @@ module Doorkeeper
32
33
  def before_successful_response
33
34
  refresh_token.transaction do
34
35
  refresh_token.lock!
35
- raise Errors::InvalidTokenReuse if refresh_token.revoked?
36
+ raise Errors::InvalidGrantReuse if refresh_token.revoked?
36
37
 
37
38
  refresh_token.revoke unless refresh_token_revoked_on_use?
38
39
  create_access_token
@@ -76,7 +77,9 @@ module Doorkeeper
76
77
  end
77
78
 
78
79
  def validate_token_presence
79
- refresh_token.present? || @refresh_token_parameter.present?
80
+ @missing_param = :refresh_token if refresh_token.blank? && @refresh_token_parameter.blank?
81
+
82
+ @missing_param.nil?
80
83
  end
81
84
 
82
85
  def validate_token
@@ -7,7 +7,7 @@ module Doorkeeper
7
7
  # @see https://tools.ietf.org/html/rfc7662
8
8
  class TokenIntrospection
9
9
  attr_reader :server, :token
10
- attr_reader :error
10
+ attr_reader :error, :invalid_request_reason
11
11
 
12
12
  def initialize(server, token)
13
13
  @server = server
@@ -25,6 +25,8 @@ module Doorkeeper
25
25
 
26
26
  if @error == :invalid_token
27
27
  OAuth::InvalidTokenResponse.from_access_token(authorized_token)
28
+ elsif @error == :invalid_request
29
+ OAuth::InvalidRequestResponse.from_request(self)
28
30
  else
29
31
  OAuth::ErrorResponse.new(name: @error)
30
32
  end
@@ -70,6 +72,7 @@ module Doorkeeper
70
72
  @error = :invalid_token unless valid_authorized_token?
71
73
  else
72
74
  @error = :invalid_request
75
+ @invalid_request_reason = :request_not_authorized
73
76
  end
74
77
  end
75
78
 
@@ -11,28 +11,14 @@ module Doorkeeper
11
11
  end
12
12
 
13
13
  def authorize
14
- if pre_auth.authorizable?
15
- auth = Authorization::Token.new(pre_auth, resource_owner)
16
- auth.issue_token
17
- @response = CodeResponse.new pre_auth,
18
- auth,
19
- response_on_fragment: true
20
- else
21
- @response = error_response
22
- end
14
+ auth = Authorization::Token.new(pre_auth, resource_owner)
15
+ auth.issue_token
16
+ CodeResponse.new(pre_auth, auth, response_on_fragment: true)
23
17
  end
24
18
 
25
19
  def deny
26
20
  pre_auth.error = :access_denied
27
- error_response
28
- end
29
-
30
- private
31
-
32
- def error_response
33
- ErrorResponse.from_request pre_auth,
34
- redirect_uri: pre_auth.redirect_uri,
35
- response_on_fragment: true
21
+ pre_auth.error_response
36
22
  end
37
23
  end
38
24
  end
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "active_support/lazy_load_hooks"
4
4
 
5
- require "doorkeeper/orm/active_record/stale_records_cleaner"
6
-
7
5
  module Doorkeeper
8
6
  module Orm
9
7
  # ActiveRecord ORM for Doorkeeper entity models.
@@ -17,6 +15,8 @@ module Doorkeeper
17
15
  module ActiveRecord
18
16
  def self.initialize_models!
19
17
  lazy_load do
18
+ require "doorkeeper/orm/active_record/stale_records_cleaner"
19
+ require "doorkeeper/orm/active_record/redirect_uri_validator"
20
20
  require "doorkeeper/orm/active_record/access_grant"
21
21
  require "doorkeeper/orm/active_record/access_token"
22
22
  require "doorkeeper/orm/active_record/application"
@@ -11,7 +11,7 @@ module Doorkeeper
11
11
 
12
12
  validates :name, :secret, :uid, presence: true
13
13
  validates :uid, uniqueness: { case_sensitive: true }
14
- validates :redirect_uri, redirect_uri: true
14
+ validates :redirect_uri, "doorkeeper/redirect_uri": true
15
15
  validates :confidential, inclusion: { in: [true, false] }
16
16
 
17
17
  validate :scopes_match_configured, if: :enforce_scopes?
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+
5
+ module Doorkeeper
6
+ # ActiveModel validator for redirect URI validation in according
7
+ # to OAuth standards and Doorkeeper configuration.
8
+ class RedirectUriValidator < ActiveModel::EachValidator
9
+ def validate_each(record, attribute, value)
10
+ if value.blank?
11
+ return if Doorkeeper.configuration.allow_blank_redirect_uri?(record)
12
+
13
+ record.errors.add(attribute, :blank)
14
+ else
15
+ value.split.each do |val|
16
+ next if oob_redirect_uri?(val)
17
+
18
+ uri = ::URI.parse(val)
19
+ record.errors.add(attribute, :forbidden_uri) if forbidden_uri?(uri)
20
+ record.errors.add(attribute, :fragment_present) unless uri.fragment.nil?
21
+ record.errors.add(attribute, :unspecified_scheme) if unspecified_scheme?(uri)
22
+ record.errors.add(attribute, :relative_uri) if relative_uri?(uri)
23
+ record.errors.add(attribute, :secured_uri) if invalid_ssl_uri?(uri)
24
+ end
25
+ end
26
+ rescue URI::InvalidURIError
27
+ record.errors.add(attribute, :invalid_uri)
28
+ end
29
+
30
+ private
31
+
32
+ def oob_redirect_uri?(uri)
33
+ Doorkeeper::OAuth::NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
34
+ end
35
+
36
+ def forbidden_uri?(uri)
37
+ Doorkeeper.configuration.forbid_redirect_uri.call(uri)
38
+ end
39
+
40
+ def unspecified_scheme?(uri)
41
+ return true if uri.opaque.present?
42
+
43
+ %w[localhost].include?(uri.try(:scheme))
44
+ end
45
+
46
+ def relative_uri?(uri)
47
+ uri.scheme.nil? && uri.host.nil?
48
+ end
49
+
50
+ def invalid_ssl_uri?(uri)
51
+ forces_ssl = Doorkeeper.configuration.force_ssl_in_redirect_uri
52
+ non_https = uri.try(:scheme) == "http"
53
+
54
+ if forces_ssl.respond_to?(:call)
55
+ forces_ssl.call(uri) && non_https
56
+ else
57
+ forces_ssl && non_https
58
+ end
59
+ end
60
+ end
61
+ end
@@ -4,30 +4,25 @@ module Doorkeeper
4
4
  module Request
5
5
  class << self
6
6
  def authorization_strategy(response_type)
7
- get_strategy(response_type, authorization_response_types)
8
- rescue NameError
9
- raise Errors::InvalidAuthorizationStrategy
7
+ build_strategy_class(response_type)
10
8
  end
11
9
 
12
10
  def token_strategy(grant_type)
11
+ raise Errors::MissingRequiredParameter, :grant_type if grant_type.blank?
12
+
13
13
  get_strategy(grant_type, token_grant_types)
14
14
  rescue NameError
15
15
  raise Errors::InvalidTokenStrategy
16
16
  end
17
17
 
18
- def get_strategy(grant_or_request_type, available)
19
- raise Errors::MissingRequestStrategy if grant_or_request_type.blank?
20
- raise NameError unless available.include?(grant_or_request_type.to_s)
18
+ def get_strategy(grant_type, available)
19
+ raise NameError unless available.include?(grant_type.to_s)
21
20
 
22
- build_strategy_class(grant_or_request_type)
21
+ build_strategy_class(grant_type)
23
22
  end
24
23
 
25
24
  private
26
25
 
27
- def authorization_response_types
28
- Doorkeeper.configuration.authorization_response_types
29
- end
30
-
31
26
  def token_grant_types
32
27
  Doorkeeper.configuration.token_grant_types
33
28
  end