doorkeeper 5.4.0.rc2 → 5.5.1
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/CHANGELOG.md +90 -10
- data/README.md +4 -4
- data/app/controllers/doorkeeper/application_controller.rb +1 -0
- data/app/controllers/doorkeeper/authorizations_controller.rb +16 -5
- data/app/controllers/doorkeeper/authorized_applications_controller.rb +1 -1
- data/app/controllers/doorkeeper/token_info_controller.rb +12 -2
- data/app/controllers/doorkeeper/tokens_controller.rb +34 -26
- data/app/views/doorkeeper/applications/show.html.erb +16 -12
- data/app/views/doorkeeper/authorizations/form_post.html.erb +11 -0
- data/config/locales/en.yml +3 -1
- data/lib/doorkeeper.rb +5 -0
- data/lib/doorkeeper/config.rb +91 -62
- data/lib/doorkeeper/config/option.rb +1 -3
- data/lib/doorkeeper/config/validations.rb +53 -0
- data/lib/doorkeeper/engine.rb +1 -1
- data/lib/doorkeeper/grant_flow.rb +45 -0
- data/lib/doorkeeper/grant_flow/fallback_flow.rb +15 -0
- data/lib/doorkeeper/grant_flow/flow.rb +44 -0
- data/lib/doorkeeper/grant_flow/registry.rb +50 -0
- data/lib/doorkeeper/helpers/controller.rb +4 -0
- data/lib/doorkeeper/models/access_grant_mixin.rb +1 -2
- data/lib/doorkeeper/models/access_token_mixin.rb +4 -4
- data/lib/doorkeeper/models/concerns/revocable.rb +1 -1
- data/lib/doorkeeper/oauth/authorization/code.rb +5 -1
- data/lib/doorkeeper/oauth/authorization/context.rb +5 -5
- data/lib/doorkeeper/oauth/authorization/token.rb +11 -5
- data/lib/doorkeeper/oauth/authorization/uri_builder.rb +1 -1
- data/lib/doorkeeper/oauth/authorization_code_request.rb +10 -17
- data/lib/doorkeeper/oauth/base_request.rb +1 -1
- data/lib/doorkeeper/oauth/client_credentials/creator.rb +2 -1
- data/lib/doorkeeper/oauth/client_credentials/issuer.rb +1 -0
- data/lib/doorkeeper/oauth/code_request.rb +2 -2
- data/lib/doorkeeper/oauth/code_response.rb +17 -11
- data/lib/doorkeeper/oauth/error_response.rb +4 -3
- data/lib/doorkeeper/oauth/helpers/scope_checker.rb +1 -3
- data/lib/doorkeeper/oauth/password_access_token_request.rb +23 -3
- data/lib/doorkeeper/oauth/pre_authorization.rb +33 -8
- data/lib/doorkeeper/oauth/refresh_token_request.rb +13 -0
- data/lib/doorkeeper/oauth/token.rb +3 -3
- data/lib/doorkeeper/oauth/token_introspection.rb +1 -5
- data/lib/doorkeeper/oauth/token_request.rb +1 -1
- data/lib/doorkeeper/orm/active_record.rb +5 -14
- data/lib/doorkeeper/orm/active_record/mixins/access_grant.rb +11 -1
- data/lib/doorkeeper/orm/active_record/mixins/access_token.rb +9 -1
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +26 -15
- data/lib/doorkeeper/orm/active_record/redirect_uri_validator.rb +5 -0
- data/lib/doorkeeper/rails/routes.rb +1 -3
- data/lib/doorkeeper/rake/db.rake +3 -3
- data/lib/doorkeeper/rake/setup.rake +5 -0
- data/lib/doorkeeper/request.rb +49 -12
- data/lib/doorkeeper/request/password.rb +1 -0
- data/lib/doorkeeper/server.rb +1 -1
- data/lib/doorkeeper/stale_records_cleaner.rb +4 -4
- data/lib/doorkeeper/version.rb +3 -7
- data/lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb +1 -1
- data/lib/generators/doorkeeper/templates/initializer.rb +9 -7
- metadata +26 -13
@@ -89,8 +89,7 @@ module Doorkeeper
|
|
89
89
|
# suitable for PKCE validation
|
90
90
|
#
|
91
91
|
def generate_code_challenge(code_verifier)
|
92
|
-
|
93
|
-
padded_result.split("=")[0] # Remove any trailing '='
|
92
|
+
Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier), padding: false)
|
94
93
|
end
|
95
94
|
|
96
95
|
def pkce_supported?
|
@@ -94,8 +94,8 @@ module Doorkeeper
|
|
94
94
|
# Interface to enumerate access token records in batches in order not
|
95
95
|
# to bloat the memory. Could be overloaded in any ORM extension.
|
96
96
|
#
|
97
|
-
def find_access_token_in_batches(relation,
|
98
|
-
relation.find_in_batches(
|
97
|
+
def find_access_token_in_batches(relation, **args, &block)
|
98
|
+
relation.find_in_batches(**args, &block)
|
99
99
|
end
|
100
100
|
|
101
101
|
# Enumerates AccessToken records in batches to find a matching token.
|
@@ -374,10 +374,10 @@ module Doorkeeper
|
|
374
374
|
# and clears `:previous_refresh_token` attribute.
|
375
375
|
#
|
376
376
|
def revoke_previous_refresh_token!
|
377
|
-
return
|
377
|
+
return if !self.class.refresh_token_revoked_on_use? || previous_refresh_token.blank?
|
378
378
|
|
379
379
|
old_refresh_token&.revoke
|
380
|
-
|
380
|
+
update_attribute(:previous_refresh_token, "")
|
381
381
|
end
|
382
382
|
|
383
383
|
private
|
@@ -11,7 +11,7 @@ module Doorkeeper
|
|
11
11
|
@resource_owner = resource_owner
|
12
12
|
end
|
13
13
|
|
14
|
-
def issue_token
|
14
|
+
def issue_token!
|
15
15
|
return @token if defined?(@token)
|
16
16
|
|
17
17
|
@token = Doorkeeper.config.access_grant_model.create!(access_grant_attributes)
|
@@ -21,6 +21,10 @@ module Doorkeeper
|
|
21
21
|
{ action: :show, code: token.plaintext_token }
|
22
22
|
end
|
23
23
|
|
24
|
+
def access_grant?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
24
28
|
private
|
25
29
|
|
26
30
|
def authorization_code_expires_in
|
@@ -4,12 +4,12 @@ module Doorkeeper
|
|
4
4
|
module OAuth
|
5
5
|
module Authorization
|
6
6
|
class Context
|
7
|
-
attr_reader :client, :grant_type, :scopes
|
7
|
+
attr_reader :client, :grant_type, :resource_owner, :scopes
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
def initialize(**attributes)
|
10
|
+
attributes.each do |name, value|
|
11
|
+
instance_variable_set(:"@#{name}", value) if respond_to?(name)
|
12
|
+
end
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -7,7 +7,7 @@ module Doorkeeper
|
|
7
7
|
attr_reader :pre_auth, :resource_owner, :token
|
8
8
|
|
9
9
|
class << self
|
10
|
-
def build_context(pre_auth_or_oauth_client, grant_type, scopes)
|
10
|
+
def build_context(pre_auth_or_oauth_client, grant_type, scopes, resource_owner)
|
11
11
|
oauth_client = if pre_auth_or_oauth_client.respond_to?(:application)
|
12
12
|
pre_auth_or_oauth_client.application
|
13
13
|
elsif pre_auth_or_oauth_client.respond_to?(:client)
|
@@ -17,9 +17,10 @@ module Doorkeeper
|
|
17
17
|
end
|
18
18
|
|
19
19
|
Doorkeeper::OAuth::Authorization::Context.new(
|
20
|
-
oauth_client,
|
21
|
-
grant_type,
|
22
|
-
scopes,
|
20
|
+
client: oauth_client,
|
21
|
+
grant_type: grant_type,
|
22
|
+
scopes: scopes,
|
23
|
+
resource_owner: resource_owner,
|
23
24
|
)
|
24
25
|
end
|
25
26
|
|
@@ -48,13 +49,14 @@ module Doorkeeper
|
|
48
49
|
@resource_owner = resource_owner
|
49
50
|
end
|
50
51
|
|
51
|
-
def issue_token
|
52
|
+
def issue_token!
|
52
53
|
return @token if defined?(@token)
|
53
54
|
|
54
55
|
context = self.class.build_context(
|
55
56
|
pre_auth.client,
|
56
57
|
Doorkeeper::OAuth::IMPLICIT,
|
57
58
|
pre_auth.scopes,
|
59
|
+
resource_owner,
|
58
60
|
)
|
59
61
|
|
60
62
|
@token = Doorkeeper.config.access_token_model.find_or_create_for(
|
@@ -74,6 +76,10 @@ module Doorkeeper
|
|
74
76
|
}
|
75
77
|
end
|
76
78
|
|
79
|
+
def access_token?
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
77
83
|
private
|
78
84
|
|
79
85
|
def controller
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module Doorkeeper
|
4
4
|
module OAuth
|
5
5
|
class AuthorizationCodeRequest < BaseRequest
|
6
|
-
validate :pkce_support, error: :invalid_request
|
7
6
|
validate :params, error: :invalid_request
|
8
7
|
validate :client, error: :invalid_client
|
9
8
|
validate :grant, error: :invalid_grant
|
@@ -32,12 +31,6 @@ module Doorkeeper
|
|
32
31
|
|
33
32
|
grant.revoke
|
34
33
|
|
35
|
-
resource_owner = if Doorkeeper.config.polymorphic_resource_owner?
|
36
|
-
grant.resource_owner
|
37
|
-
else
|
38
|
-
grant.resource_owner_id
|
39
|
-
end
|
40
|
-
|
41
34
|
find_or_create_access_token(
|
42
35
|
grant.application,
|
43
36
|
resource_owner,
|
@@ -49,16 +42,16 @@ module Doorkeeper
|
|
49
42
|
super
|
50
43
|
end
|
51
44
|
|
52
|
-
def
|
53
|
-
Doorkeeper.config.
|
45
|
+
def resource_owner
|
46
|
+
if Doorkeeper.config.polymorphic_resource_owner?
|
47
|
+
grant.resource_owner
|
48
|
+
else
|
49
|
+
grant.resource_owner_id
|
50
|
+
end
|
54
51
|
end
|
55
52
|
|
56
|
-
def
|
57
|
-
|
58
|
-
!pkce_supported? &&
|
59
|
-
code_verifier.present?
|
60
|
-
|
61
|
-
@invalid_request_reason.nil?
|
53
|
+
def pkce_supported?
|
54
|
+
Doorkeeper.config.access_grant_model.pkce_supported?
|
62
55
|
end
|
63
56
|
|
64
57
|
def validate_params
|
@@ -91,8 +84,8 @@ module Doorkeeper
|
|
91
84
|
# if either side (server or client) request PKCE, check the verifier
|
92
85
|
# against the DB - if PKCE is supported
|
93
86
|
def validate_code_verifier
|
94
|
-
return true unless
|
95
|
-
return
|
87
|
+
return true unless pkce_supported?
|
88
|
+
return grant.code_challenge.blank? if code_verifier.blank?
|
96
89
|
|
97
90
|
if grant.code_challenge_method == "S256"
|
98
91
|
grant.code_challenge == generate_code_challenge(code_verifier)
|
@@ -27,7 +27,7 @@ module Doorkeeper
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def find_or_create_access_token(client, resource_owner, scopes, server)
|
30
|
-
context = Authorization::Token.build_context(client, grant_type, scopes)
|
30
|
+
context = Authorization::Token.build_context(client, grant_type, scopes, resource_owner)
|
31
31
|
@access_token = server_config.access_token_model.find_or_create_for(
|
32
32
|
application: client,
|
33
33
|
resource_owner: resource_owner,
|
@@ -39,7 +39,8 @@ module Doorkeeper
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def lookup_existing_token?
|
42
|
-
server_config.reuse_access_token ||
|
42
|
+
server_config.reuse_access_token ||
|
43
|
+
server_config.revoke_previous_client_credentials_token?
|
43
44
|
end
|
44
45
|
|
45
46
|
def find_existing_token_for(client, scopes)
|
@@ -6,13 +6,13 @@ module Doorkeeper
|
|
6
6
|
attr_reader :pre_auth, :resource_owner
|
7
7
|
|
8
8
|
def initialize(pre_auth, resource_owner)
|
9
|
-
@pre_auth
|
9
|
+
@pre_auth = pre_auth
|
10
10
|
@resource_owner = resource_owner
|
11
11
|
end
|
12
12
|
|
13
13
|
def authorize
|
14
14
|
auth = Authorization::Code.new(pre_auth, resource_owner)
|
15
|
-
auth.issue_token
|
15
|
+
auth.issue_token!
|
16
16
|
CodeResponse.new(pre_auth, auth)
|
17
17
|
end
|
18
18
|
|
@@ -21,23 +21,29 @@ module Doorkeeper
|
|
21
21
|
auth.token
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
25
|
-
if
|
26
|
-
|
27
|
-
elsif response_on_fragment
|
28
|
-
Authorization::URIBuilder.uri_with_fragment(
|
29
|
-
pre_auth.redirect_uri,
|
24
|
+
def body
|
25
|
+
if auth.try(:access_token?)
|
26
|
+
{
|
30
27
|
access_token: auth.token.plaintext_token,
|
31
28
|
token_type: auth.token.token_type,
|
32
29
|
expires_in: auth.token.expires_in_seconds,
|
33
30
|
state: pre_auth.state,
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
pre_auth.redirect_uri,
|
31
|
+
}
|
32
|
+
elsif auth.try(:access_grant?)
|
33
|
+
{
|
38
34
|
code: auth.token.plaintext_token,
|
39
35
|
state: pre_auth.state,
|
40
|
-
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def redirect_uri
|
41
|
+
if URIChecker.oob_uri?(pre_auth.redirect_uri)
|
42
|
+
auth.oob_redirect
|
43
|
+
elsif response_on_fragment
|
44
|
+
Authorization::URIBuilder.uri_with_fragment(pre_auth.redirect_uri, body)
|
45
|
+
else
|
46
|
+
Authorization::URIBuilder.uri_with_query(pre_auth.redirect_uri, body)
|
41
47
|
end
|
42
48
|
end
|
43
49
|
end
|
@@ -5,6 +5,8 @@ module Doorkeeper
|
|
5
5
|
class ErrorResponse < BaseResponse
|
6
6
|
include OAuth::Helpers
|
7
7
|
|
8
|
+
NON_REDIRECTABLE_STATES = %i[invalid_redirect_uri invalid_client unauthorized_client].freeze
|
9
|
+
|
8
10
|
def self.from_request(request, attributes = {})
|
9
11
|
new(
|
10
12
|
attributes.merge(
|
@@ -32,7 +34,7 @@ module Doorkeeper
|
|
32
34
|
end
|
33
35
|
|
34
36
|
def status
|
35
|
-
if name == :invalid_client
|
37
|
+
if name == :invalid_client || name == :unauthorized_client
|
36
38
|
:unauthorized
|
37
39
|
else
|
38
40
|
:bad_request
|
@@ -40,8 +42,7 @@ module Doorkeeper
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def redirectable?
|
43
|
-
name
|
44
|
-
!URIChecker.oob_uri?(@redirect_uri)
|
45
|
+
!NON_REDIRECTABLE_STATES.include?(name) && !URIChecker.oob_uri?(@redirect_uri)
|
45
46
|
end
|
46
47
|
|
47
48
|
def redirect_uri
|
@@ -12,9 +12,7 @@ module Doorkeeper
|
|
12
12
|
@scope_str = scope_str
|
13
13
|
@valid_scopes = valid_scopes(server_scopes, app_scopes)
|
14
14
|
|
15
|
-
if grant_type
|
16
|
-
@scopes_by_grant_type = Doorkeeper.config.scopes_by_grant_type[grant_type.to_sym]
|
17
|
-
end
|
15
|
+
@scopes_by_grant_type = Doorkeeper.config.scopes_by_grant_type[grant_type.to_sym] if grant_type
|
18
16
|
end
|
19
17
|
|
20
18
|
def valid?
|
@@ -10,12 +10,13 @@ module Doorkeeper
|
|
10
10
|
validate :resource_owner, error: :invalid_grant
|
11
11
|
validate :scopes, error: :invalid_scope
|
12
12
|
|
13
|
-
attr_reader :client, :resource_owner, :parameters, :access_token
|
13
|
+
attr_reader :client, :credentials, :resource_owner, :parameters, :access_token
|
14
14
|
|
15
|
-
def initialize(server, client, resource_owner, parameters = {})
|
15
|
+
def initialize(server, client, credentials, resource_owner, parameters = {})
|
16
16
|
@server = server
|
17
17
|
@resource_owner = resource_owner
|
18
18
|
@client = client
|
19
|
+
@credentials = credentials
|
19
20
|
@parameters = parameters
|
20
21
|
@original_scopes = parameters[:scope]
|
21
22
|
@grant_type = Doorkeeper::OAuth::PASSWORD
|
@@ -43,8 +44,27 @@ module Doorkeeper
|
|
43
44
|
resource_owner.present?
|
44
45
|
end
|
45
46
|
|
47
|
+
# Section 4.3.2. Access Token Request for Resource Owner Password Credentials Grant:
|
48
|
+
#
|
49
|
+
# If the client type is confidential or the client was issued client credentials (or assigned
|
50
|
+
# other authentication requirements), the client MUST authenticate with the authorization
|
51
|
+
# server as described in Section 3.2.1.
|
52
|
+
#
|
53
|
+
# The authorization server MUST:
|
54
|
+
#
|
55
|
+
# o require client authentication for confidential clients or for any client that was
|
56
|
+
# issued client credentials (or with other authentication requirements)
|
57
|
+
#
|
58
|
+
# o authenticate the client if client authentication is included,
|
59
|
+
#
|
60
|
+
# @see https://tools.ietf.org/html/rfc6749#section-4.3
|
61
|
+
#
|
46
62
|
def validate_client
|
47
|
-
|
63
|
+
if Doorkeeper.config.skip_client_authentication_for_password_grant
|
64
|
+
client.present? || (!parameters[:client_id] && credentials.blank?)
|
65
|
+
else
|
66
|
+
client.present?
|
67
|
+
end
|
48
68
|
end
|
49
69
|
|
50
70
|
def validate_client_supports_grant_flow
|
@@ -12,16 +12,19 @@ module Doorkeeper
|
|
12
12
|
validate :redirect_uri, error: :invalid_redirect_uri
|
13
13
|
validate :params, error: :invalid_request
|
14
14
|
validate :response_type, error: :unsupported_response_type
|
15
|
+
validate :response_mode, error: :unsupported_response_mode
|
15
16
|
validate :scopes, error: :invalid_scope
|
16
17
|
validate :code_challenge_method, error: :invalid_code_challenge_method
|
17
18
|
|
18
19
|
attr_reader :client, :code_challenge, :code_challenge_method, :missing_param,
|
19
|
-
:redirect_uri, :resource_owner, :response_type, :state
|
20
|
+
:redirect_uri, :resource_owner, :response_type, :state,
|
21
|
+
:authorization_response_flow, :response_mode
|
20
22
|
|
21
23
|
def initialize(server, parameters = {}, resource_owner = nil)
|
22
24
|
@server = server
|
23
25
|
@client_id = parameters[:client_id]
|
24
26
|
@response_type = parameters[:response_type]
|
27
|
+
@response_mode = parameters[:response_mode]
|
25
28
|
@redirect_uri = parameters[:redirect_uri]
|
26
29
|
@scope = parameters[:scope]
|
27
30
|
@state = parameters[:state]
|
@@ -57,6 +60,10 @@ module Doorkeeper
|
|
57
60
|
pre_auth_hash
|
58
61
|
end
|
59
62
|
|
63
|
+
def form_post_response?
|
64
|
+
response_mode == "form_post"
|
65
|
+
end
|
66
|
+
|
60
67
|
private
|
61
68
|
|
62
69
|
attr_reader :client_id, :server
|
@@ -84,6 +91,11 @@ module Doorkeeper
|
|
84
91
|
Doorkeeper.config.allow_grant_flow_for_client?(grant_type, client.application)
|
85
92
|
end
|
86
93
|
|
94
|
+
def validate_resource_owner_authorize_for_client
|
95
|
+
# The `authorize_resource_owner_for_client` config option is used for this validation
|
96
|
+
client.application.authorized_for_resource_owner?(@resource_owner)
|
97
|
+
end
|
98
|
+
|
87
99
|
def validate_redirect_uri
|
88
100
|
return false if redirect_uri.blank?
|
89
101
|
|
@@ -104,7 +116,21 @@ module Doorkeeper
|
|
104
116
|
end
|
105
117
|
|
106
118
|
def validate_response_type
|
107
|
-
server.
|
119
|
+
server.authorization_response_flows.any? do |flow|
|
120
|
+
if flow.matches_response_type?(response_type)
|
121
|
+
@authorization_response_flow = flow
|
122
|
+
true
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def validate_response_mode
|
128
|
+
if response_mode.blank?
|
129
|
+
@response_mode = authorization_response_flow.default_response_mode
|
130
|
+
return true
|
131
|
+
end
|
132
|
+
|
133
|
+
authorization_response_flow.matches_response_mode?(response_mode)
|
108
134
|
end
|
109
135
|
|
110
136
|
def validate_scopes
|
@@ -117,17 +143,16 @@ module Doorkeeper
|
|
117
143
|
end
|
118
144
|
|
119
145
|
def validate_code_challenge_method
|
146
|
+
return true unless Doorkeeper.config.access_grant_model.pkce_supported?
|
147
|
+
|
120
148
|
code_challenge.blank? ||
|
121
149
|
(code_challenge_method.present? && code_challenge_method =~ /^plain$|^S256$/)
|
122
150
|
end
|
123
151
|
|
124
|
-
def validate_resource_owner_authorize_for_client
|
125
|
-
# The `authorize_resource_owner_for_client` config option is used for this validation
|
126
|
-
client.application.authorized_for_resource_owner?(@resource_owner)
|
127
|
-
end
|
128
|
-
|
129
152
|
def response_on_fragment?
|
130
|
-
response_type == "token"
|
153
|
+
return response_type == "token" if response_mode.nil?
|
154
|
+
|
155
|
+
response_mode == "fragment"
|
131
156
|
end
|
132
157
|
|
133
158
|
def grant_type
|