doorkeeper 5.1.2 → 5.2.6
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.
- checksums.yaml +4 -4
- data/Appraisals +1 -1
- data/CHANGELOG.md +880 -0
- data/CONTRIBUTING.md +11 -9
- data/Dangerfile +2 -2
- data/Dockerfile +29 -0
- data/Gemfile +3 -2
- data/NEWS.md +1 -819
- data/README.md +11 -3
- data/RELEASING.md +6 -5
- data/app/controllers/doorkeeper/application_controller.rb +1 -1
- data/app/controllers/doorkeeper/application_metal_controller.rb +2 -1
- data/app/controllers/doorkeeper/applications_controller.rb +1 -0
- data/app/controllers/doorkeeper/authorizations_controller.rb +14 -7
- data/app/controllers/doorkeeper/tokens_controller.rb +32 -9
- data/app/views/doorkeeper/applications/_form.html.erb +0 -6
- data/app/views/doorkeeper/applications/show.html.erb +1 -1
- data/config/locales/en.yml +8 -2
- data/doorkeeper.gemspec +9 -1
- data/gemfiles/rails_5_0.gemfile +1 -0
- data/gemfiles/rails_5_1.gemfile +1 -0
- data/gemfiles/rails_5_2.gemfile +1 -0
- data/gemfiles/rails_6_0.gemfile +2 -1
- data/gemfiles/rails_master.gemfile +1 -0
- data/lib/doorkeeper/config/option.rb +13 -7
- data/lib/doorkeeper/config.rb +89 -6
- data/lib/doorkeeper/errors.rb +13 -18
- data/lib/doorkeeper/grape/helpers.rb +5 -1
- data/lib/doorkeeper/helpers/controller.rb +23 -4
- data/lib/doorkeeper/models/access_token_mixin.rb +49 -7
- data/lib/doorkeeper/oauth/authorization/code.rb +11 -13
- data/lib/doorkeeper/oauth/authorization/token.rb +1 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +18 -9
- data/lib/doorkeeper/oauth/base_request.rb +2 -0
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +14 -0
- data/lib/doorkeeper/oauth/client_credentials/validation.rb +8 -0
- data/lib/doorkeeper/oauth/code_request.rb +5 -11
- data/lib/doorkeeper/oauth/code_response.rb +2 -2
- data/lib/doorkeeper/oauth/error_response.rb +1 -1
- data/lib/doorkeeper/oauth/helpers/uri_checker.rb +18 -4
- data/lib/doorkeeper/oauth/invalid_request_response.rb +43 -0
- data/lib/doorkeeper/oauth/nonstandard.rb +39 -0
- data/lib/doorkeeper/oauth/password_access_token_request.rb +7 -2
- data/lib/doorkeeper/oauth/pre_authorization.rb +73 -37
- data/lib/doorkeeper/oauth/refresh_token_request.rb +13 -10
- data/lib/doorkeeper/oauth/token_introspection.rb +23 -13
- data/lib/doorkeeper/oauth/token_request.rb +4 -18
- data/lib/doorkeeper/orm/active_record/access_grant.rb +1 -1
- data/lib/doorkeeper/orm/active_record/access_token.rb +2 -2
- data/lib/doorkeeper/orm/active_record/application.rb +13 -5
- data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +61 -0
- data/lib/doorkeeper/orm/active_record.rb +18 -3
- data/lib/doorkeeper/request/authorization_code.rb +2 -0
- data/lib/doorkeeper/request.rb +6 -11
- data/lib/doorkeeper/server.rb +2 -6
- data/lib/doorkeeper/stale_records_cleaner.rb +6 -2
- data/lib/doorkeeper/version.rb +2 -2
- data/lib/doorkeeper.rb +4 -0
- data/lib/generators/doorkeeper/previous_refresh_token_generator.rb +6 -6
- data/lib/generators/doorkeeper/templates/initializer.rb +118 -33
- data/lib/generators/doorkeeper/templates/migration.rb.erb +4 -1
- data/spec/controllers/applications_controller_spec.rb +93 -0
- data/spec/controllers/authorizations_controller_spec.rb +143 -62
- data/spec/controllers/protected_resources_controller_spec.rb +3 -3
- data/spec/controllers/tokens_controller_spec.rb +205 -37
- data/spec/dummy/config/application.rb +3 -1
- data/spec/dummy/config/initializers/doorkeeper.rb +54 -9
- data/spec/dummy/db/migrate/20151223192035_create_doorkeeper_tables.rb +1 -1
- data/spec/lib/config_spec.rb +58 -1
- data/spec/lib/oauth/authorization_code_request_spec.rb +13 -1
- data/spec/lib/oauth/base_request_spec.rb +33 -16
- data/spec/lib/oauth/client_credentials/creator_spec.rb +3 -0
- data/spec/lib/oauth/code_request_spec.rb +27 -28
- data/spec/lib/oauth/helpers/uri_checker_spec.rb +17 -2
- data/spec/lib/oauth/invalid_request_response_spec.rb +75 -0
- data/spec/lib/oauth/pre_authorization_spec.rb +76 -66
- data/spec/lib/oauth/refresh_token_request_spec.rb +1 -0
- data/spec/lib/oauth/token_request_spec.rb +20 -17
- data/spec/lib/server_spec.rb +0 -12
- data/spec/models/doorkeeper/access_grant_spec.rb +21 -2
- data/spec/models/doorkeeper/access_token_spec.rb +35 -4
- data/spec/models/doorkeeper/application_spec.rb +10 -0
- data/spec/requests/endpoints/authorization_spec.rb +21 -5
- data/spec/requests/endpoints/token_spec.rb +1 -1
- data/spec/requests/flows/authorization_code_errors_spec.rb +1 -0
- data/spec/requests/flows/authorization_code_spec.rb +93 -27
- data/spec/requests/flows/client_credentials_spec.rb +38 -0
- data/spec/requests/flows/implicit_grant_errors_spec.rb +22 -10
- data/spec/requests/flows/implicit_grant_spec.rb +9 -8
- data/spec/requests/flows/password_spec.rb +37 -0
- data/spec/requests/flows/refresh_token_spec.rb +1 -1
- data/spec/requests/flows/revoke_token_spec.rb +19 -11
- data/spec/support/doorkeeper_rspec.rb +1 -1
- data/spec/support/helpers/request_spec_helper.rb +14 -2
- data/spec/validators/redirect_uri_validator_spec.rb +40 -15
- metadata +16 -15
- data/.coveralls.yml +0 -1
- data/.github/ISSUE_TEMPLATE.md +0 -25
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -17
- data/.gitignore +0 -20
- data/.gitlab-ci.yml +0 -16
- data/.hound.yml +0 -3
- data/.rspec +0 -1
- data/.rubocop.yml +0 -50
- data/.travis.yml +0 -35
- data/app/validators/redirect_uri_validator.rb +0 -50
@@ -76,9 +76,51 @@ module Doorkeeper
|
|
76
76
|
end
|
77
77
|
|
78
78
|
tokens = authorized_tokens_for(application.try(:id), resource_owner_id)
|
79
|
-
tokens
|
80
|
-
|
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
|
+
batch_size = Doorkeeper.configuration.token_lookup_batch_size
|
114
|
+
|
115
|
+
find_access_token_in_batches(relation, batch_size: batch_size) do |batch|
|
116
|
+
tokens = batch.select do |token|
|
117
|
+
scopes_match?(token.scopes, scopes, application.try(:scopes))
|
118
|
+
end
|
119
|
+
|
120
|
+
matching_tokens.concat(tokens)
|
81
121
|
end
|
122
|
+
|
123
|
+
matching_tokens.max_by(&:created_at)
|
82
124
|
end
|
83
125
|
|
84
126
|
# Checks whether the token scopes match the scopes from the parameters
|
@@ -150,10 +192,9 @@ module Doorkeeper
|
|
150
192
|
# @return [Doorkeeper::AccessToken] array of matching AccessToken objects
|
151
193
|
#
|
152
194
|
def authorized_tokens_for(application_id, resource_owner_id)
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
revoked_at: nil)
|
195
|
+
where(application_id: application_id,
|
196
|
+
resource_owner_id: resource_owner_id,
|
197
|
+
revoked_at: nil)
|
157
198
|
end
|
158
199
|
|
159
200
|
# Convenience method for backwards-compatibility, return the last
|
@@ -168,7 +209,8 @@ module Doorkeeper
|
|
168
209
|
# nil if nothing was found
|
169
210
|
#
|
170
211
|
def last_authorized_token_for(application_id, resource_owner_id)
|
171
|
-
authorized_tokens_for(application_id, resource_owner_id)
|
212
|
+
authorized_tokens_for(application_id, resource_owner_id)
|
213
|
+
.ordered_by(:created_at, :desc).first
|
172
214
|
end
|
173
215
|
|
174
216
|
##
|
@@ -12,29 +12,27 @@ module Doorkeeper
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def issue_token
|
15
|
-
@token ||= AccessGrant.create!
|
15
|
+
@token ||= AccessGrant.create!(access_grant_attributes)
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
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
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
#
|
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?
|
@@ -3,7 +3,8 @@
|
|
3
3
|
module Doorkeeper
|
4
4
|
module OAuth
|
5
5
|
class AuthorizationCodeRequest < BaseRequest
|
6
|
-
validate :
|
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
|
46
|
-
|
47
|
-
|
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
|
-
|
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
|
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
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
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.
|
22
|
-
auth.
|
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,
|
@@ -25,10 +25,10 @@ module Doorkeeper
|
|
25
25
|
module Helpers
|
26
26
|
module URIChecker
|
27
27
|
def self.valid?(url)
|
28
|
-
return true if
|
28
|
+
return true if oob_uri?(url)
|
29
29
|
|
30
30
|
uri = as_uri(url)
|
31
|
-
uri
|
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.
|
82
|
-
|
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,
|
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,
|
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 :
|
9
|
-
validate :client,
|
10
|
-
validate :
|
11
|
-
validate :
|
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
|
-
|
15
|
-
|
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,
|
20
|
+
def initialize(server, attrs = {})
|
19
21
|
@server = server
|
20
|
-
@
|
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,37 @@ 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
|
-
|
48
|
+
if error == :invalid_request
|
49
|
+
OAuth::InvalidRequestResponse.from_request(self,
|
50
|
+
response_on_fragment: response_on_fragment?)
|
51
|
+
else
|
52
|
+
OAuth::ErrorResponse.from_request(self, response_on_fragment: response_on_fragment?)
|
53
|
+
end
|
43
54
|
end
|
44
55
|
|
45
|
-
def as_json(
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
}
|
56
|
+
def as_json(attributes = {})
|
57
|
+
return pre_auth_hash.merge(attributes.to_h) if attributes.respond_to?(:to_h)
|
58
|
+
|
59
|
+
pre_auth_hash
|
55
60
|
end
|
56
61
|
|
57
62
|
private
|
58
63
|
|
59
64
|
def build_scopes
|
60
|
-
client_scopes = client.
|
65
|
+
client_scopes = client.scopes
|
61
66
|
if client_scopes.blank?
|
62
67
|
server.default_scopes.to_s
|
63
68
|
else
|
@@ -65,21 +70,45 @@ module Doorkeeper
|
|
65
70
|
end
|
66
71
|
end
|
67
72
|
|
68
|
-
def
|
69
|
-
|
73
|
+
def validate_client_id
|
74
|
+
@missing_param = :client_id if client_id.blank?
|
75
|
+
|
76
|
+
@missing_param.nil?
|
70
77
|
end
|
71
78
|
|
72
79
|
def validate_client
|
73
|
-
client.
|
80
|
+
@client = OAuth::Client.find(client_id)
|
81
|
+
@client.present?
|
74
82
|
end
|
75
83
|
|
76
|
-
def
|
77
|
-
return
|
84
|
+
def validate_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_params
|
94
|
+
@missing_param = if response_type.blank?
|
95
|
+
:response_type
|
96
|
+
elsif @scope.blank? && server.default_scopes.blank?
|
97
|
+
:scope
|
98
|
+
end
|
99
|
+
|
100
|
+
@missing_param.nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
def validate_response_type
|
104
|
+
server.authorization_response_types.include?(response_type)
|
105
|
+
end
|
78
106
|
|
107
|
+
def validate_scopes
|
79
108
|
Helpers::ScopeChecker.valid?(
|
80
109
|
scope_str: scope,
|
81
110
|
server_scopes: server.scopes,
|
82
|
-
app_scopes: client.
|
111
|
+
app_scopes: client.scopes,
|
83
112
|
grant_type: grant_type
|
84
113
|
)
|
85
114
|
end
|
@@ -88,19 +117,26 @@ module Doorkeeper
|
|
88
117
|
response_type == "code" ? AUTHORIZATION_CODE : IMPLICIT
|
89
118
|
end
|
90
119
|
|
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
120
|
def validate_code_challenge_method
|
101
121
|
code_challenge.blank? ||
|
102
122
|
(code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
|
103
123
|
end
|
124
|
+
|
125
|
+
def response_on_fragment?
|
126
|
+
response_type == "token"
|
127
|
+
end
|
128
|
+
|
129
|
+
def pre_auth_hash
|
130
|
+
{
|
131
|
+
client_id: client.uid,
|
132
|
+
redirect_uri: redirect_uri,
|
133
|
+
state: state,
|
134
|
+
response_type: response_type,
|
135
|
+
scope: scope,
|
136
|
+
client_name: client.name,
|
137
|
+
status: I18n.t("doorkeeper.pre_authorization.status"),
|
138
|
+
}
|
139
|
+
end
|
104
140
|
end
|
105
141
|
end
|
106
142
|
end
|
@@ -13,26 +13,27 @@ 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
|
-
@server
|
19
|
-
@refresh_token
|
20
|
-
@credentials
|
19
|
+
@server = server
|
20
|
+
@refresh_token = refresh_token
|
21
|
+
@credentials = credentials
|
21
22
|
@original_scopes = parameters[:scope] || parameters[:scopes]
|
22
23
|
@refresh_token_parameter = parameters[:refresh_token]
|
23
|
-
|
24
|
-
if credentials
|
25
|
-
@client = Application.by_uid_and_secret credentials.uid,
|
26
|
-
credentials.secret
|
27
|
-
end
|
24
|
+
@client = load_client(credentials) if credentials
|
28
25
|
end
|
29
26
|
|
30
27
|
private
|
31
28
|
|
29
|
+
def load_client(credentials)
|
30
|
+
Application.by_uid_and_secret(credentials.uid, credentials.secret)
|
31
|
+
end
|
32
|
+
|
32
33
|
def before_successful_response
|
33
34
|
refresh_token.transaction do
|
34
35
|
refresh_token.lock!
|
35
|
-
raise Errors::
|
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.
|
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
|