doorkeeper 5.1.2 → 5.2.2

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 (107) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +1 -1
  3. data/CHANGELOG.md +854 -0
  4. data/CONTRIBUTING.md +11 -9
  5. data/Dangerfile +2 -2
  6. data/Dockerfile +29 -0
  7. data/Gemfile +3 -2
  8. data/NEWS.md +1 -819
  9. data/README.md +11 -3
  10. data/RELEASING.md +6 -5
  11. data/app/controllers/doorkeeper/application_controller.rb +1 -1
  12. data/app/controllers/doorkeeper/application_metal_controller.rb +2 -1
  13. data/app/controllers/doorkeeper/applications_controller.rb +5 -3
  14. data/app/controllers/doorkeeper/authorizations_controller.rb +14 -7
  15. data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
  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 +1 -0
  22. data/gemfiles/rails_5_1.gemfile +1 -0
  23. data/gemfiles/rails_5_2.gemfile +1 -0
  24. data/gemfiles/rails_6_0.gemfile +2 -1
  25. data/gemfiles/rails_master.gemfile +1 -0
  26. data/lib/doorkeeper/config/option.rb +13 -7
  27. data/lib/doorkeeper/config.rb +88 -6
  28. data/lib/doorkeeper/errors.rb +13 -18
  29. data/lib/doorkeeper/grape/helpers.rb +5 -1
  30. data/lib/doorkeeper/helpers/controller.rb +23 -4
  31. data/lib/doorkeeper/models/access_token_mixin.rb +43 -2
  32. data/lib/doorkeeper/oauth/authorization/code.rb +11 -13
  33. data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
  34. data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -9
  35. data/lib/doorkeeper/oauth/base_request.rb +2 -0
  36. data/lib/doorkeeper/oauth/client_credentials/creator.rb +14 -0
  37. data/lib/doorkeeper/oauth/client_credentials/validation.rb +8 -0
  38. data/lib/doorkeeper/oauth/code_request.rb +5 -11
  39. data/lib/doorkeeper/oauth/code_response.rb +2 -2
  40. data/lib/doorkeeper/oauth/error_response.rb +1 -1
  41. data/lib/doorkeeper/oauth/helpers/uri_checker.rb +18 -4
  42. data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
  43. data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
  44. data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
  45. data/lib/doorkeeper/oauth/pre_authorization.rb +70 -37
  46. data/lib/doorkeeper/oauth/refresh_token_request.rb +13 -10
  47. data/lib/doorkeeper/oauth/token_introspection.rb +23 -13
  48. data/lib/doorkeeper/oauth/token_request.rb +4 -18
  49. data/lib/doorkeeper/orm/active_record/access_grant.rb +1 -1
  50. data/lib/doorkeeper/orm/active_record/access_token.rb +2 -2
  51. data/lib/doorkeeper/orm/active_record/application.rb +15 -69
  52. data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
  53. data/lib/doorkeeper/orm/active_record.rb +19 -3
  54. data/lib/doorkeeper/request/authorization_code.rb +2 -0
  55. data/lib/doorkeeper/request.rb +6 -11
  56. data/lib/doorkeeper/server.rb +2 -6
  57. data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
  58. data/lib/doorkeeper/version.rb +1 -1
  59. data/lib/doorkeeper.rb +4 -0
  60. data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +6 -6
  61. data/lib/generators/doorkeeper/templates/initializer.rb +110 -33
  62. data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
  63. data/spec/controllers/applications_controller_spec.rb +93 -0
  64. data/spec/controllers/authorizations_controller_spec.rb +143 -62
  65. data/spec/controllers/protected_resources_controller_spec.rb +3 -3
  66. data/spec/controllers/tokens_controller_spec.rb +205 -37
  67. data/spec/dummy/config/application.rb +3 -1
  68. data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
  69. data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +1 -1
  70. data/spec/lib/config_spec.rb +43 -1
  71. data/spec/lib/oauth/authorization_code_request_spec.rb +13 -1
  72. data/spec/lib/oauth/base_request_spec.rb +33 -16
  73. data/spec/lib/oauth/client_credentials/creator_spec.rb +3 -0
  74. data/spec/lib/oauth/code_request_spec.rb +27 -28
  75. data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
  76. data/spec/lib/oauth/invalid_request_response_spec.rb +75 -0
  77. data/spec/lib/oauth/pre_authorization_spec.rb +76 -66
  78. data/spec/lib/oauth/refresh_token_request_spec.rb +1 -0
  79. data/spec/lib/oauth/token_request_spec.rb +20 -17
  80. data/spec/lib/server_spec.rb +0 -12
  81. data/spec/models/doorkeeper/access_grant_spec.rb +21 -2
  82. data/spec/models/doorkeeper/access_token_spec.rb +35 -4
  83. data/spec/models/doorkeeper/application_spec.rb +275 -370
  84. data/spec/requests/endpoints/authorization_spec.rb +21 -5
  85. data/spec/requests/endpoints/token_spec.rb +1 -1
  86. data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
  87. data/spec/requests/flows/authorization_code_spec.rb +93 -27
  88. data/spec/requests/flows/client_credentials_spec.rb +38 -0
  89. data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
  90. data/spec/requests/flows/implicit_grant_spec.rb +9 -8
  91. data/spec/requests/flows/password_spec.rb +37 -0
  92. data/spec/requests/flows/refresh_token_spec.rb +1 -1
  93. data/spec/requests/flows/revoke_token_spec.rb +19 -11
  94. data/spec/support/doorkeeper_rspec.rb +1 -1
  95. data/spec/support/helpers/request_spec_helper.rb +14 -2
  96. data/spec/validators/redirect_uri_validator_spec.rb +40 -15
  97. metadata +16 -15
  98. data/.coveralls.yml +0 -1
  99. data/.github/ISSUE_TEMPLATE.md +0 -25
  100. data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  101. data/.gitignore +0 -20
  102. data/.gitlab-ci.yml +0 -16
  103. data/.hound.yml +0 -3
  104. data/.rspec +0 -1
  105. data/.rubocop.yml +0 -50
  106. data/.travis.yml +0 -35
  107. data/app/validators/redirect_uri_validator.rb +0 -50
@@ -4,6 +4,8 @@
4
4
  # Doorkeeper::ApplicationMetalController or Doorkeeper::ApplicationController
5
5
  module Doorkeeper
6
6
  module Helpers
7
+ # Rails controller helpers.
8
+ #
7
9
  module Controller
8
10
  private
9
11
 
@@ -14,7 +16,9 @@ module Doorkeeper
14
16
 
15
17
  # :doc:
16
18
  def current_resource_owner
17
- instance_eval(&Doorkeeper.configuration.authenticate_resource_owner)
19
+ @current_resource_owner ||= begin
20
+ instance_eval(&Doorkeeper.configuration.authenticate_resource_owner)
21
+ end
18
22
  end
19
23
 
20
24
  def resource_owner_from_credentials
@@ -40,7 +44,15 @@ module Doorkeeper
40
44
  end
41
45
 
42
46
  def get_error_response_from_exception(exception)
43
- OAuth::ErrorResponse.new name: exception.type, state: params[:state]
47
+ if exception.respond_to?(:response)
48
+ exception.response
49
+ elsif exception.type == :invalid_request
50
+ OAuth::InvalidRequestResponse.new(name: exception.type,
51
+ state: params[:state],
52
+ missing_param: exception.missing_param)
53
+ else
54
+ OAuth::ErrorResponse.new(name: exception.type, state: params[:state])
55
+ end
44
56
  end
45
57
 
46
58
  def handle_token_exception(exception)
@@ -51,14 +63,21 @@ module Doorkeeper
51
63
  end
52
64
 
53
65
  def skip_authorization?
54
- !!instance_exec([@server.current_resource_owner, @pre_auth.client], &Doorkeeper.configuration.skip_authorization)
66
+ !!instance_exec(
67
+ [server.current_resource_owner, @pre_auth.client],
68
+ &Doorkeeper.configuration.skip_authorization
69
+ )
55
70
  end
56
71
 
57
72
  def enforce_content_type
58
- if (request.put? || request.post? || request.patch?) && request.content_type != "application/x-www-form-urlencoded"
73
+ if (request.put? || request.post? || request.patch?) && !x_www_form_urlencoded?
59
74
  render json: {}, status: :unsupported_media_type
60
75
  end
61
76
  end
77
+
78
+ def x_www_form_urlencoded?
79
+ request.content_type == "application/x-www-form-urlencoded"
80
+ end
62
81
  end
63
82
  end
64
83
  end
@@ -76,9 +76,50 @@ module Doorkeeper
76
76
  end
77
77
 
78
78
  tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
79
- tokens.detect do |token|
80
- scopes_match?(token.scopes, scopes, application.try(:scopes))
79
+ find_matching_token(tokens, application, scopes)
80
+ end
81
+
82
+ # Interface to enumerate access token records in batches in order not
83
+ # to bloat the memory. Could be overloaded in any ORM extension.
84
+ #
85
+ def find_access_token_in_batches(relation, *args, &block)
86
+ relation.find_in_batches(*args, &block)
87
+ end
88
+
89
+ # Enumerates AccessToken records in batches to find a matching token.
90
+ # Batching is required in order not to pollute the memory if Application
91
+ # has huge amount of associated records.
92
+ #
93
+ # ActiveRecord 5.x - 6.x ignores custom ordering so we can't perform a
94
+ # database sort by created_at, so we need to load all the matching records,
95
+ # sort them and find latest one. Probably it would be better to rewrite this
96
+ # query using Time math if possible, but we n eed to consider ORM and
97
+ # different databases support.
98
+ #
99
+ # @param relation [ActiveRecord::Relation]
100
+ # Access tokens relation
101
+ # @param application [Doorkeeper::Application]
102
+ # Application instance
103
+ # @param scopes [String, Doorkeeper::OAuth::Scopes]
104
+ # set of scopes
105
+ #
106
+ # @return [Doorkeeper::AccessToken, nil] Access Token instance or
107
+ # nil if matching record was not found
108
+ #
109
+ def find_matching_token(relation, application, scopes)
110
+ return nil unless relation
111
+
112
+ matching_tokens = []
113
+
114
+ find_access_token_in_batches(relation) do |batch|
115
+ tokens = batch.select do |token|
116
+ scopes_match?(token.scopes, scopes, application.try(:scopes))
117
+ end
118
+
119
+ matching_tokens.concat(tokens)
81
120
  end
121
+
122
+ matching_tokens.max_by(&:created_at)
82
123
  end
83
124
 
84
125
  # Checks whether the token scopes match the scopes from the parameters
@@ -12,29 +12,27 @@ module Doorkeeper
12
12
  end
13
13
 
14
14
  def issue_token
15
- @token ||= AccessGrant.create! access_grant_attributes
15
+ @token ||= AccessGrant.create!(access_grant_attributes)
16
16
  end
17
17
 
18
- def native_redirect
18
+ def oob_redirect
19
19
  { action: :show, code: token.plaintext_token }
20
20
  end
21
21
 
22
- def configuration
23
- Doorkeeper.configuration
24
- end
25
-
26
22
  private
27
23
 
28
24
  def authorization_code_expires_in
29
- configuration.authorization_code_expires_in
25
+ Doorkeeper.configuration.authorization_code_expires_in
30
26
  end
31
27
 
32
28
  def access_grant_attributes
33
- pkce_attributes.merge application_id: pre_auth.client.id,
34
- resource_owner_id: resource_owner.id,
35
- expires_in: authorization_code_expires_in,
36
- redirect_uri: pre_auth.redirect_uri,
37
- scopes: pre_auth.scopes.to_s
29
+ pkce_attributes.merge(
30
+ application_id: pre_auth.client.id,
31
+ resource_owner_id: resource_owner.id,
32
+ expires_in: authorization_code_expires_in,
33
+ redirect_uri: pre_auth.redirect_uri,
34
+ scopes: pre_auth.scopes.to_s
35
+ )
38
36
  end
39
37
 
40
38
  def pkce_attributes
@@ -46,7 +44,7 @@ module Doorkeeper
46
44
  }
47
45
  end
48
46
 
49
- # ensures firstly, if migration with additional pcke columns was
47
+ # Ensures firstly, if migration with additional PKCE columns was
50
48
  # generated and migrated
51
49
  def pkce_supported?
52
50
  Doorkeeper::AccessGrant.pkce_supported?
@@ -63,7 +63,7 @@ module Doorkeeper
63
63
  )
64
64
  end
65
65
 
66
- def native_redirect
66
+ def oob_redirect
67
67
  {
68
68
  controller: controller,
69
69
  action: :show,
@@ -3,7 +3,8 @@
3
3
  module Doorkeeper
4
4
  module OAuth
5
5
  class AuthorizationCodeRequest < BaseRequest
6
- validate :attributes, error: :invalid_request
6
+ validate :pkce_support, error: :invalid_request
7
+ validate :params, error: :invalid_request
7
8
  validate :client, error: :invalid_client
8
9
  validate :grant, error: :invalid_grant
9
10
  # @see https://tools.ietf.org/html/rfc6749#section-5.2
@@ -12,6 +13,7 @@ module Doorkeeper
12
13
 
13
14
  attr_accessor :server, :grant, :client, :redirect_uri, :access_token,
14
15
  :code_verifier
16
+ attr_reader :invalid_request_reason, :missing_param
15
17
 
16
18
  def initialize(server, grant, client, parameters = {})
17
19
  @server = server
@@ -24,10 +26,6 @@ module Doorkeeper
24
26
 
25
27
  private
26
28
 
27
- def client_by_uid(parameters)
28
- Doorkeeper::Application.by_uid(parameters[:client_id])
29
- end
30
-
31
29
  def before_successful_response
32
30
  grant.transaction do
33
31
  grant.lock!
@@ -42,11 +40,22 @@ module Doorkeeper
42
40
  super
43
41
  end
44
42
 
45
- def validate_attributes
46
- return false if grant&.uses_pkce? && code_verifier.blank?
47
- return false if grant && !grant.pkce_supported? && !code_verifier.blank?
43
+ def validate_pkce_support
44
+ @invalid_request_reason = :not_support_pkce if grant &&
45
+ !grant.pkce_supported? &&
46
+ code_verifier.present?
47
+
48
+ @invalid_request_reason.nil?
49
+ end
50
+
51
+ def validate_params
52
+ @missing_param = if grant&.uses_pkce? && code_verifier.blank?
53
+ :code_verifier
54
+ elsif redirect_uri.blank?
55
+ :redirect_uri
56
+ end
48
57
 
49
- redirect_uri.present?
58
+ @missing_param.nil?
50
59
  end
51
60
 
52
61
  def validate_client
@@ -15,6 +15,8 @@ module Doorkeeper
15
15
  @response = TokenResponse.new(access_token)
16
16
  after_successful_response
17
17
  @response
18
+ elsif error == :invalid_request
19
+ @response = InvalidRequestResponse.from_request(self)
18
20
  else
19
21
  @response = ErrorResponse.from_request(self)
20
22
  end
@@ -5,11 +5,25 @@ module Doorkeeper
5
5
  class ClientCredentialsRequest < BaseRequest
6
6
  class Creator
7
7
  def call(client, scopes, attributes = {})
8
+ existing_token = existing_token_for(client, scopes)
9
+
10
+ if Doorkeeper.configuration.reuse_access_token && existing_token&.reusable?
11
+ return existing_token
12
+ end
13
+
14
+ existing_token&.revoke
15
+
8
16
  AccessToken.find_or_create_for(
9
17
  client, nil, scopes, attributes[:expires_in],
10
18
  attributes[:use_refresh_token]
11
19
  )
12
20
  end
21
+
22
+ private
23
+
24
+ def existing_token_for(client, scopes)
25
+ Doorkeeper::AccessToken.matching_token_for client, nil, scopes
26
+ end
13
27
  end
14
28
  end
15
29
  end
@@ -8,6 +8,7 @@ module Doorkeeper
8
8
  include OAuth::Helpers
9
9
 
10
10
  validate :client, error: :invalid_client
11
+ validate :client_supports_grant_flow, error: :unauthorized_client
11
12
  validate :scopes, error: :invalid_scope
12
13
 
13
14
  def initialize(server, request)
@@ -24,6 +25,13 @@ module Doorkeeper
24
25
  @client.present?
25
26
  end
26
27
 
28
+ def validate_client_supports_grant_flow
29
+ Doorkeeper.configuration.allow_grant_flow_for_client?(
30
+ Doorkeeper::OAuth::CLIENT_CREDENTIALS,
31
+ @client
32
+ )
33
+ end
34
+
27
35
  def validate_scopes
28
36
  return true if @request.scopes.blank?
29
37
 
@@ -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
@@ -18,8 +18,8 @@ module Doorkeeper
18
18
  end
19
19
 
20
20
  def redirect_uri
21
- if URIChecker.native_uri? pre_auth.redirect_uri
22
- auth.native_redirect
21
+ if URIChecker.oob_uri? pre_auth.redirect_uri
22
+ auth.oob_redirect
23
23
  elsif response_on_fragment
24
24
  Authorization::URIBuilder.uri_with_fragment(
25
25
  pre_auth.redirect_uri,
@@ -41,7 +41,7 @@ module Doorkeeper
41
41
 
42
42
  def redirectable?
43
43
  name != :invalid_redirect_uri && name != :invalid_client &&
44
- !URIChecker.native_uri?(@redirect_uri)
44
+ !URIChecker.oob_uri?(@redirect_uri)
45
45
  end
46
46
 
47
47
  def redirect_uri
@@ -25,10 +25,10 @@ module Doorkeeper
25
25
  module Helpers
26
26
  module URIChecker
27
27
  def self.valid?(url)
28
- return true if native_uri?(url)
28
+ return true if oob_uri?(url)
29
29
 
30
30
  uri = as_uri(url)
31
- uri.fragment.nil? && !uri.host.nil? && !uri.scheme.nil?
31
+ valid_scheme?(uri) && iff_host?(uri) && uri.fragment.nil? && uri.opaque.nil?
32
32
  rescue URI::InvalidURIError
33
33
  false
34
34
  end
@@ -78,8 +78,22 @@ module Doorkeeper
78
78
  client_query.split("&").sort == query.split("&").sort
79
79
  end
80
80
 
81
- def self.native_uri?(url)
82
- url == Doorkeeper.configuration.native_redirect_uri
81
+ def self.valid_scheme?(uri)
82
+ return false if uri.scheme.nil?
83
+
84
+ %w[localhost].include?(uri.scheme) == false
85
+ end
86
+
87
+ def self.hypertext_scheme?(uri)
88
+ %w[http https].include?(uri.scheme)
89
+ end
90
+
91
+ def self.iff_host?(uri)
92
+ !(hypertext_scheme?(uri) && uri.host.nil?)
93
+ end
94
+
95
+ def self.oob_uri?(uri)
96
+ NonStandard::IETF_WG_OAUTH2_OOB_METHODS.include?(uri)
83
97
  end
84
98
  end
85
99
  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
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doorkeeper
4
+ module OAuth
5
+ class NonStandard
6
+ # These are not part of the OAuth 2 specification but are still in use by Google
7
+ # and in some other implementations. Native applications should use one of the
8
+ # approaches discussed in RFC8252. OOB is 'Out of Band'
9
+
10
+ # This value signals to the Google Authorization Server that the authorization
11
+ # code should be returned in the title bar of the browser, with the page text
12
+ # prompting the user to copy the code and paste it in the application.
13
+ # This is useful when the client (such as a Windows application) cannot listen
14
+ # on an HTTP port without significant client configuration.
15
+
16
+ # When you use this value, your application can then detect that the page has loaded, and can
17
+ # read the title of the HTML page to obtain the authorization code. It is then up to your
18
+ # application to close the browser window if you want to ensure that the user never sees the
19
+ # page that contains the authorization code. The mechanism for doing this varies from platform
20
+ # to platform.
21
+ #
22
+ # If your platform doesn't allow you to detect that the page has loaded or read the title of
23
+ # the page, you can have the user paste the code back to your application, as prompted by the
24
+ # text in the confirmation page that the OAuth 2.0 server generates.
25
+ IETF_WG_OAUTH2_OOB = "urn:ietf:wg:oauth:2.0:oob"
26
+
27
+ # This is identical to urn:ietf:wg:oauth:2.0:oob, but the text in the confirmation page that
28
+ # the OAuth 2.0 server generates won't instruct the user to copy the authorization code, but
29
+ # instead will simply ask the user to close the window.
30
+ #
31
+ # This is useful when your application reads the title of the HTML page (by checking window
32
+ # titles on the desktop, for example) to obtain the authorization code, but can't close the
33
+ # page on its own.
34
+ IETF_WG_OAUTH2_OOB_AUTO = "urn:ietf:wg:oauth:2.0:oob:auto"
35
+
36
+ IETF_WG_OAUTH2_OOB_METHODS = [IETF_WG_OAUTH2_OOB, IETF_WG_OAUTH2_OOB_AUTO].freeze
37
+ end
38
+ end
39
+ 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