doorkeeper 5.7.1 → 5.8.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/app/controllers/doorkeeper/tokens_controller.rb +24 -5
- data/config/locales/en.yml +4 -1
- data/lib/doorkeeper/config/validations.rb +12 -0
- data/lib/doorkeeper/config.rb +26 -1
- data/lib/doorkeeper/errors.rb +14 -1
- data/lib/doorkeeper/models/access_token_mixin.rb +2 -0
- data/lib/doorkeeper/oauth/base_request.rb +2 -1
- data/lib/doorkeeper/oauth/error.rb +4 -3
- data/lib/doorkeeper/oauth/error_response.rb +2 -1
- data/lib/doorkeeper/oauth/pre_authorization.rb +2 -2
- data/lib/doorkeeper/oauth/scopes.rb +55 -1
- data/lib/doorkeeper/oauth/token_introspection.rb +28 -16
- data/lib/doorkeeper/oauth/token_response.rb +1 -0
- data/lib/doorkeeper/orm/active_record/mixins/application.rb +10 -3
- data/lib/doorkeeper/revocable_tokens/revocable_access_token.rb +21 -0
- data/lib/doorkeeper/revocable_tokens/revocable_refresh_token.rb +21 -0
- data/lib/doorkeeper/version.rb +1 -1
- data/lib/doorkeeper.rb +5 -0
- data/lib/generators/doorkeeper/remove_applications_secret_not_null_constraint_generator.rb +33 -0
- data/lib/generators/doorkeeper/templates/migration.rb.erb +1 -0
- data/lib/generators/doorkeeper/templates/remove_applications_secret_not_null_constraint.rb.erb +7 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8364fc5d75f9cbe96cc3ef67c8010dde471eb51ced0cf328f9ca84705553976f
|
4
|
+
data.tar.gz: db817023f41b070185ae9d6fae32b9d9b0eb0fc7abf8bdd99961c80e8bece1dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 940f6253760d9117390495e97fa270aa0337a7379d2070d2be5ce2a44cf8148f451ffe7a3ba0451ab88d1cbb5bd4242f6d4a7de90204cf2749a57bdeaa4005ed
|
7
|
+
data.tar.gz: 728ea65c1e37f7f77183e5528c441cf7b9c8a4493428bbafbfe7815dec4b227d8bd033e05c4ee2dde50b9cc252cad7241b5d287d2df28e6631f9ccada5c7afc5
|
data/CHANGELOG.md
CHANGED
@@ -9,6 +9,23 @@ User-visible changes worth mentioning.
|
|
9
9
|
|
10
10
|
Add your entry here.
|
11
11
|
|
12
|
+
## 5.8.1
|
13
|
+
|
14
|
+
- [#1752] Bump the range of supported Ruby and Rails versions
|
15
|
+
- [#1747] Fix unknown pkce method error when configured
|
16
|
+
- [#1744] Allow for expired refresh tokens to be revoked
|
17
|
+
- [#1754] Fix refresh tokens with dynamic scopes
|
18
|
+
|
19
|
+
## 5.8.0
|
20
|
+
|
21
|
+
- [#1739] Add support for dynamic scopes
|
22
|
+
- [#1715] Fix token introspection invalid request reason
|
23
|
+
- [#1714] Fix `Doorkeeper::AccessToken.find_or_create_for` with empty scopes which raises NoMethodError
|
24
|
+
- [#1712] Add `Pragma: no-cache` to token response
|
25
|
+
- [#1726] Refactor token introspection class.
|
26
|
+
- [#1727] Allow to set null secret value for Applications if they are public.
|
27
|
+
- [#1735] Add `pkce_code_challenge_methods` config option.
|
28
|
+
|
12
29
|
## 5.7.1
|
13
30
|
|
14
31
|
- [#1705] Add `force_pkce` option that requires non-confidential clients to use PKCE when requesting an access_token using an authorization code
|
@@ -113,19 +113,38 @@ module Doorkeeper
|
|
113
113
|
# The authorization server responds with HTTP status code 200 if the token
|
114
114
|
# has been revoked successfully or if the client submitted an invalid
|
115
115
|
# token
|
116
|
-
|
116
|
+
revocable_token.revoke if revocable_token.revocable?
|
117
117
|
end
|
118
118
|
|
119
119
|
def token
|
120
|
-
|
120
|
+
revocable_token&.token
|
121
|
+
end
|
122
|
+
|
123
|
+
def revocable_token
|
124
|
+
return @revocable_token if defined? @revocable_token
|
125
|
+
|
126
|
+
@revocable_token =
|
121
127
|
if params[:token_type_hint] == "refresh_token"
|
122
|
-
|
128
|
+
refresh_token
|
123
129
|
else
|
124
|
-
|
125
|
-
Doorkeeper.config.access_token_model.by_refresh_token(params["token"])
|
130
|
+
access_token || refresh_token
|
126
131
|
end
|
127
132
|
end
|
128
133
|
|
134
|
+
def refresh_token
|
135
|
+
token = Doorkeeper.config.access_token_model.by_refresh_token(params["token"])
|
136
|
+
return unless token
|
137
|
+
|
138
|
+
RevocableTokens::RevocableRefreshToken.new(token)
|
139
|
+
end
|
140
|
+
|
141
|
+
def access_token
|
142
|
+
token = Doorkeeper.config.access_token_model.by_token(params["token"])
|
143
|
+
return unless token
|
144
|
+
|
145
|
+
RevocableTokens::RevocableAccessToken.new(token)
|
146
|
+
end
|
147
|
+
|
129
148
|
def strategy
|
130
149
|
@strategy ||= server.token_request(params[:grant_type])
|
131
150
|
end
|
data/config/locales/en.yml
CHANGED
@@ -100,7 +100,10 @@ en:
|
|
100
100
|
unauthorized_client: 'The client is not authorized to perform this request using this method.'
|
101
101
|
access_denied: 'The resource owner or authorization server denied the request.'
|
102
102
|
invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
|
103
|
-
invalid_code_challenge_method:
|
103
|
+
invalid_code_challenge_method:
|
104
|
+
zero: 'The authorization server does not support PKCE as there are no accepted code_challenge_method values.'
|
105
|
+
one: 'The code_challenge_method must be %{challenge_methods}.'
|
106
|
+
other: 'The code_challenge_method must be one of %{challenge_methods}.'
|
104
107
|
server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'
|
105
108
|
temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'
|
106
109
|
|
@@ -11,6 +11,7 @@ module Doorkeeper
|
|
11
11
|
validate_reuse_access_token_value
|
12
12
|
validate_token_reuse_limit
|
13
13
|
validate_secret_strategies
|
14
|
+
validate_pkce_code_challenge_methods
|
14
15
|
end
|
15
16
|
|
16
17
|
private
|
@@ -48,6 +49,17 @@ module Doorkeeper
|
|
48
49
|
)
|
49
50
|
@token_reuse_limit = 100
|
50
51
|
end
|
52
|
+
|
53
|
+
def validate_pkce_code_challenge_methods
|
54
|
+
return if pkce_code_challenge_methods.all? {|method| method =~ /^plain$|^S256$/ }
|
55
|
+
|
56
|
+
::Rails.logger.warn(
|
57
|
+
"[DOORKEEPER] You have configured an invalid value for pkce_code_challenge_methods option. " \
|
58
|
+
"It will be set to default ['plain', 'S256']",
|
59
|
+
)
|
60
|
+
|
61
|
+
@pkce_code_challenge_methods = ['plain', 'S256']
|
62
|
+
end
|
51
63
|
end
|
52
64
|
end
|
53
65
|
end
|
data/lib/doorkeeper/config.rb
CHANGED
@@ -31,6 +31,16 @@ module Doorkeeper
|
|
31
31
|
@config.instance_variable_set(:@confirm_application_owner, true)
|
32
32
|
end
|
33
33
|
|
34
|
+
# Provide support for dynamic scopes (e.g. user:*) (disabled by default)
|
35
|
+
# Optional parameter delimiter (default ":") if you want to customize
|
36
|
+
# the delimiter separating the scope name and matching value.
|
37
|
+
#
|
38
|
+
# @param opts [Hash] the options to configure dynamic scopes
|
39
|
+
def enable_dynamic_scopes(opts = {})
|
40
|
+
@config.instance_variable_set(:@enable_dynamic_scopes, true)
|
41
|
+
@config.instance_variable_set(:@dynamic_scopes_delimiter, opts[:delimiter] || ':')
|
42
|
+
end
|
43
|
+
|
34
44
|
# Define default access token scopes for your provider
|
35
45
|
#
|
36
46
|
# @param scopes [Array] Default set of access (OAuth::Scopes.new)
|
@@ -243,6 +253,7 @@ module Doorkeeper
|
|
243
253
|
option :orm, default: :active_record
|
244
254
|
option :native_redirect_uri, default: "urn:ietf:wg:oauth:2.0:oob", deprecated: true
|
245
255
|
option :grant_flows, default: %w[authorization_code client_credentials]
|
256
|
+
option :pkce_code_challenge_methods, default: %w[plain S256]
|
246
257
|
option :handle_auth_errors, default: :render
|
247
258
|
option :token_lookup_batch_size, default: 10_000
|
248
259
|
# Sets the token_reuse_limit
|
@@ -418,7 +429,7 @@ module Doorkeeper
|
|
418
429
|
default: (lambda do |token, authorized_client, authorized_token|
|
419
430
|
if authorized_token
|
420
431
|
authorized_token.application == token&.application
|
421
|
-
elsif token
|
432
|
+
elsif token&.application
|
422
433
|
authorized_client == token.application
|
423
434
|
else
|
424
435
|
true
|
@@ -510,6 +521,14 @@ module Doorkeeper
|
|
510
521
|
option_set? :enable_application_owner
|
511
522
|
end
|
512
523
|
|
524
|
+
def enable_dynamic_scopes?
|
525
|
+
option_set? :enable_dynamic_scopes
|
526
|
+
end
|
527
|
+
|
528
|
+
def dynamic_scopes_delimiter
|
529
|
+
@dynamic_scopes_delimiter
|
530
|
+
end
|
531
|
+
|
513
532
|
def polymorphic_resource_owner?
|
514
533
|
option_set? :polymorphic_resource_owner
|
515
534
|
end
|
@@ -554,6 +573,12 @@ module Doorkeeper
|
|
554
573
|
@scopes_by_grant_type ||= {}
|
555
574
|
end
|
556
575
|
|
576
|
+
def pkce_code_challenge_methods_supported
|
577
|
+
return [] unless access_grant_model.pkce_supported?
|
578
|
+
|
579
|
+
pkce_code_challenge_methods
|
580
|
+
end
|
581
|
+
|
557
582
|
def client_credentials_methods
|
558
583
|
@client_credentials_methods ||= %i[from_basic from_params]
|
559
584
|
end
|
data/lib/doorkeeper/errors.rb
CHANGED
@@ -6,6 +6,10 @@ module Doorkeeper
|
|
6
6
|
def type
|
7
7
|
message
|
8
8
|
end
|
9
|
+
|
10
|
+
def self.translate_options
|
11
|
+
{}
|
12
|
+
end
|
9
13
|
end
|
10
14
|
|
11
15
|
class InvalidGrantReuse < DoorkeeperError
|
@@ -45,6 +49,16 @@ module Doorkeeper
|
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
52
|
+
class InvalidCodeChallengeMethod < BaseResponseError
|
53
|
+
def self.translate_options
|
54
|
+
challenge_methods = Doorkeeper.config.pkce_code_challenge_methods_supported
|
55
|
+
{
|
56
|
+
challenge_methods: challenge_methods.join(", "),
|
57
|
+
count: challenge_methods.length
|
58
|
+
}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
48
62
|
UnableToGenerateToken = Class.new(DoorkeeperError)
|
49
63
|
TokenGeneratorNotFound = Class.new(DoorkeeperError)
|
50
64
|
NoOrmCleaner = Class.new(DoorkeeperError)
|
@@ -55,7 +69,6 @@ module Doorkeeper
|
|
55
69
|
InvalidScope = Class.new(BaseResponseError)
|
56
70
|
InvalidRedirectUri = Class.new(BaseResponseError)
|
57
71
|
InvalidCodeChallenge = Class.new(BaseResponseError)
|
58
|
-
InvalidCodeChallengeMethod = Class.new(BaseResponseError)
|
59
72
|
InvalidGrant = Class.new(BaseResponseError)
|
60
73
|
|
61
74
|
UnauthorizedClient = Class.new(BaseResponseError)
|
@@ -214,6 +214,8 @@ module Doorkeeper
|
|
214
214
|
# @return [Doorkeeper::AccessToken] existing record or a new one
|
215
215
|
#
|
216
216
|
def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes)
|
217
|
+
scopes = Doorkeeper::OAuth::Scopes.from_string(scopes) if scopes.is_a?(String)
|
218
|
+
|
217
219
|
if Doorkeeper.config.reuse_access_token
|
218
220
|
custom_attributes = extract_custom_attributes(token_attributes).presence
|
219
221
|
access_token = matching_token_for(
|
@@ -2,13 +2,14 @@
|
|
2
2
|
|
3
3
|
module Doorkeeper
|
4
4
|
module OAuth
|
5
|
-
Error = Struct.new(:name, :state) do
|
5
|
+
Error = Struct.new(:name, :state, :translate_options) do
|
6
6
|
def description
|
7
|
-
|
8
|
-
name,
|
7
|
+
options = (translate_options || {}).merge(
|
9
8
|
scope: %i[doorkeeper errors messages],
|
10
9
|
default: :server_error,
|
11
10
|
)
|
11
|
+
|
12
|
+
I18n.translate(name, **options)
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
@@ -12,6 +12,7 @@ module Doorkeeper
|
|
12
12
|
attributes.merge(
|
13
13
|
name: error_name_for(request.error),
|
14
14
|
exception_class: exception_class_for(request.error),
|
15
|
+
translate_options: request.error.try(:translate_options),
|
15
16
|
state: request.try(:state),
|
16
17
|
redirect_uri: request.try(:redirect_uri),
|
17
18
|
),
|
@@ -33,7 +34,7 @@ module Doorkeeper
|
|
33
34
|
delegate :name, :description, :state, to: :@error
|
34
35
|
|
35
36
|
def initialize(attributes = {})
|
36
|
-
@error = OAuth::Error.new(*attributes.values_at(:name, :state))
|
37
|
+
@error = OAuth::Error.new(*attributes.values_at(:name, :state, :translate_options))
|
37
38
|
@exception_class = attributes[:exception_class]
|
38
39
|
@redirect_uri = attributes[:redirect_uri]
|
39
40
|
@response_on_fragment = attributes[:response_on_fragment]
|
@@ -75,7 +75,7 @@ module Doorkeeper
|
|
75
75
|
if client_scopes.blank?
|
76
76
|
server.default_scopes.to_s
|
77
77
|
else
|
78
|
-
|
78
|
+
server.default_scopes.allowed(client_scopes).to_s
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
@@ -154,7 +154,7 @@ module Doorkeeper
|
|
154
154
|
return true unless Doorkeeper.config.access_grant_model.pkce_supported?
|
155
155
|
|
156
156
|
code_challenge.blank? ||
|
157
|
-
(code_challenge_method.present? && code_challenge_method
|
157
|
+
(code_challenge_method.present? && Doorkeeper.config.pkce_code_challenge_methods_supported.include?(code_challenge_method))
|
158
158
|
end
|
159
159
|
|
160
160
|
def response_on_fragment?
|
@@ -6,6 +6,8 @@ module Doorkeeper
|
|
6
6
|
include Enumerable
|
7
7
|
include Comparable
|
8
8
|
|
9
|
+
DYNAMIC_SCOPE_WILDCARD = "*"
|
10
|
+
|
9
11
|
def self.from_string(string)
|
10
12
|
string ||= ""
|
11
13
|
new.tap do |scope|
|
@@ -26,7 +28,15 @@ module Doorkeeper
|
|
26
28
|
end
|
27
29
|
|
28
30
|
def exists?(scope)
|
29
|
-
|
31
|
+
scope = scope.to_s
|
32
|
+
|
33
|
+
@scopes.any? do |allowed_scope|
|
34
|
+
if dynamic_scopes_enabled? && dynamic_scopes_present?(allowed_scope, scope)
|
35
|
+
dynamic_scope_match?(allowed_scope, scope)
|
36
|
+
else
|
37
|
+
allowed_scope == scope
|
38
|
+
end
|
39
|
+
end
|
30
40
|
end
|
31
41
|
|
32
42
|
def add(*scopes)
|
@@ -60,12 +70,56 @@ module Doorkeeper
|
|
60
70
|
end
|
61
71
|
end
|
62
72
|
|
73
|
+
# DEPRECATED: With dynamic scopes, #allowed should be called because
|
74
|
+
# A & B doesn't really make sense with dynamic scopes.
|
75
|
+
#
|
76
|
+
# For example, if A = user:* and B is user:1, A & B = [].
|
77
|
+
# If we modified this method to take dynamic scopes into an account, then order
|
78
|
+
# becomes important, and this would violate the principle that A & B = B & A.
|
63
79
|
def &(other)
|
80
|
+
return allowed(other) if dynamic_scopes_enabled?
|
81
|
+
|
64
82
|
self.class.from_array(all & to_array(other))
|
65
83
|
end
|
66
84
|
|
85
|
+
# Returns a set of scopes that are allowed, taking dynamic
|
86
|
+
# scopes into account. This instance's scopes is taken as the allowed set,
|
87
|
+
# and the passed value is the set to filter.
|
88
|
+
#
|
89
|
+
# @param other The set of scopes to filter
|
90
|
+
def allowed(other)
|
91
|
+
filtered_scopes = other.select { |scope| self.exists?(scope) }
|
92
|
+
self.class.from_array(filtered_scopes)
|
93
|
+
end
|
94
|
+
|
67
95
|
private
|
68
96
|
|
97
|
+
def dynamic_scopes_enabled?
|
98
|
+
Doorkeeper.config.enable_dynamic_scopes?
|
99
|
+
end
|
100
|
+
|
101
|
+
def dynamic_scope_delimiter
|
102
|
+
return unless dynamic_scopes_enabled?
|
103
|
+
|
104
|
+
@dynamic_scope_delimiter ||= Doorkeeper.config.dynamic_scopes_delimiter
|
105
|
+
end
|
106
|
+
|
107
|
+
def dynamic_scopes_present?(allowed, requested)
|
108
|
+
allowed.include?(dynamic_scope_delimiter) && requested.include?(dynamic_scope_delimiter)
|
109
|
+
end
|
110
|
+
|
111
|
+
def dynamic_scope_match?(allowed, requested)
|
112
|
+
allowed_pattern = allowed.split(dynamic_scope_delimiter, 2)
|
113
|
+
request_pattern = requested.split(dynamic_scope_delimiter, 2)
|
114
|
+
|
115
|
+
return false if allowed_pattern[0] != request_pattern[0]
|
116
|
+
return false if allowed_pattern[1].blank?
|
117
|
+
return false if request_pattern[1].blank?
|
118
|
+
return true if allowed_pattern[1] == DYNAMIC_SCOPE_WILDCARD && allowed_pattern[1].present?
|
119
|
+
|
120
|
+
allowed_pattern[1] == request_pattern[1]
|
121
|
+
end
|
122
|
+
|
69
123
|
def to_array(other)
|
70
124
|
case other
|
71
125
|
when Scopes
|
@@ -6,16 +6,15 @@ module Doorkeeper
|
|
6
6
|
#
|
7
7
|
# @see https://datatracker.ietf.org/doc/html/rfc7662
|
8
8
|
class TokenIntrospection
|
9
|
-
attr_reader :error
|
9
|
+
attr_reader :token, :error, :invalid_request_reason
|
10
10
|
|
11
11
|
def initialize(server, token)
|
12
12
|
@server = server
|
13
13
|
@token = token
|
14
|
-
|
15
|
-
authorize!
|
16
14
|
end
|
17
15
|
|
18
16
|
def authorized?
|
17
|
+
authorize!
|
19
18
|
@error.blank?
|
20
19
|
end
|
21
20
|
|
@@ -37,8 +36,7 @@ module Doorkeeper
|
|
37
36
|
|
38
37
|
private
|
39
38
|
|
40
|
-
attr_reader :server
|
41
|
-
attr_reader :invalid_request_reason
|
39
|
+
attr_reader :server
|
42
40
|
|
43
41
|
# If the protected resource uses OAuth 2.0 client credentials to
|
44
42
|
# authenticate to the introspection endpoint and its credentials are
|
@@ -60,24 +58,38 @@ module Doorkeeper
|
|
60
58
|
def authorize!
|
61
59
|
# Requested client authorization
|
62
60
|
if server.credentials
|
63
|
-
|
61
|
+
authorize_using_basic_auth!
|
64
62
|
elsif authorized_token
|
65
|
-
|
66
|
-
#
|
67
|
-
# If the protected resource uses an OAuth 2.0 bearer token to authorize
|
68
|
-
# its call to the introspection endpoint and the token used for
|
69
|
-
# authorization does not contain sufficient privileges or is otherwise
|
70
|
-
# invalid for this request, the authorization server responds with an
|
71
|
-
# HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
|
72
|
-
# Usage [RFC6750].
|
73
|
-
#
|
74
|
-
@error = Errors::InvalidToken unless valid_authorized_token?
|
63
|
+
authorize_using_bearer_token!
|
75
64
|
else
|
76
65
|
@error = Errors::InvalidRequest
|
77
66
|
@invalid_request_reason = :request_not_authorized
|
78
67
|
end
|
79
68
|
end
|
80
69
|
|
70
|
+
def authorize_using_basic_auth!
|
71
|
+
# Note that a properly formed and authorized query for an inactive or
|
72
|
+
# otherwise invalid token (or a token the protected resource is not
|
73
|
+
# allowed to know about) is not considered an error response by this
|
74
|
+
# specification. In these cases, the authorization server MUST instead
|
75
|
+
# respond with an introspection response with the "active" field set to
|
76
|
+
# "false" as described in Section 2.2.
|
77
|
+
@error = Errors::InvalidClient unless authorized_client
|
78
|
+
end
|
79
|
+
|
80
|
+
def authorize_using_bearer_token!
|
81
|
+
# Requested bearer token authorization
|
82
|
+
#
|
83
|
+
# If the protected resource uses an OAuth 2.0 bearer token to authorize
|
84
|
+
# its call to the introspection endpoint and the token used for
|
85
|
+
# authorization does not contain sufficient privileges or is otherwise
|
86
|
+
# invalid for this request, the authorization server responds with an
|
87
|
+
# HTTP 401 code as described in Section 3 of OAuth 2.0 Bearer Token
|
88
|
+
# Usage [RFC6750].
|
89
|
+
#
|
90
|
+
@error = Errors::InvalidToken unless valid_authorized_token?
|
91
|
+
end
|
92
|
+
|
81
93
|
# Client Authentication
|
82
94
|
def authorized_client
|
83
95
|
@authorized_client ||= server.credentials && server.client
|
@@ -20,11 +20,13 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
|
|
20
20
|
dependent: :delete_all,
|
21
21
|
class_name: Doorkeeper.config.access_token_class.to_s
|
22
22
|
|
23
|
-
validates :name, :
|
23
|
+
validates :name, :uid, presence: true
|
24
|
+
validates :secret, presence: true, if: -> { secret_required? }
|
24
25
|
validates :uid, uniqueness: { case_sensitive: true }
|
25
|
-
validates_with Doorkeeper::RedirectUriValidator, attributes: [:redirect_uri]
|
26
26
|
validates :confidential, inclusion: { in: [true, false] }
|
27
27
|
|
28
|
+
validates_with Doorkeeper::RedirectUriValidator, attributes: [:redirect_uri]
|
29
|
+
|
28
30
|
validate :scopes_match_configured, if: :enforce_scopes?
|
29
31
|
|
30
32
|
before_validation :generate_uid, :generate_secret, on: :create
|
@@ -118,7 +120,7 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
|
|
118
120
|
end
|
119
121
|
|
120
122
|
def generate_secret
|
121
|
-
return if secret.present?
|
123
|
+
return if secret.present? || !secret_required?
|
122
124
|
|
123
125
|
renew_secret
|
124
126
|
end
|
@@ -136,6 +138,11 @@ module Doorkeeper::Orm::ActiveRecord::Mixins
|
|
136
138
|
Doorkeeper.config.enforce_configured_scopes?
|
137
139
|
end
|
138
140
|
|
141
|
+
def secret_required?
|
142
|
+
confidential? ||
|
143
|
+
!self.class.columns.detect { |column| column.name == "secret" }&.null
|
144
|
+
end
|
145
|
+
|
139
146
|
# Helper method to extract collection of serializable attribute names
|
140
147
|
# considering serialization options (like `only`, `except` and so on).
|
141
148
|
#
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doorkeeper
|
4
|
+
module RevocableTokens
|
5
|
+
class RevocableAccessToken
|
6
|
+
attr_reader :token
|
7
|
+
|
8
|
+
def initialize(token)
|
9
|
+
@token = token
|
10
|
+
end
|
11
|
+
|
12
|
+
def revocable?
|
13
|
+
token.accessible?
|
14
|
+
end
|
15
|
+
|
16
|
+
def revoke
|
17
|
+
token.revoke
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doorkeeper
|
4
|
+
module RevocableTokens
|
5
|
+
class RevocableRefreshToken
|
6
|
+
attr_reader :token
|
7
|
+
|
8
|
+
def initialize(token)
|
9
|
+
@token = token
|
10
|
+
end
|
11
|
+
|
12
|
+
def revocable?
|
13
|
+
!token.revoked?
|
14
|
+
end
|
15
|
+
|
16
|
+
def revoke
|
17
|
+
token.revoke
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/doorkeeper/version.rb
CHANGED
data/lib/doorkeeper.rb
CHANGED
@@ -34,6 +34,11 @@ module Doorkeeper
|
|
34
34
|
autoload :Token, "doorkeeper/request/token"
|
35
35
|
end
|
36
36
|
|
37
|
+
module RevocableTokens
|
38
|
+
autoload :RevocableAccessToken, "doorkeeper/revocable_tokens/revocable_access_token"
|
39
|
+
autoload :RevocableRefreshToken, "doorkeeper/revocable_tokens/revocable_refresh_token"
|
40
|
+
end
|
41
|
+
|
37
42
|
module OAuth
|
38
43
|
autoload :BaseRequest, "doorkeeper/oauth/base_request"
|
39
44
|
autoload :AuthorizationCodeRequest, "doorkeeper/oauth/authorization_code_request"
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators"
|
4
|
+
require "rails/generators/active_record"
|
5
|
+
|
6
|
+
module Doorkeeper
|
7
|
+
# Generates migration with which drops NOT NULL constraint and allows not
|
8
|
+
# to bloat the database with redundant secret value.
|
9
|
+
#
|
10
|
+
class RemoveApplicationSecretNotNullConstraint < ::Rails::Generators::Base
|
11
|
+
include ::Rails::Generators::Migration
|
12
|
+
source_root File.expand_path("templates", __dir__)
|
13
|
+
desc "Removes NOT NULL constraint for OAuth2 applications."
|
14
|
+
|
15
|
+
def enable_polymorphic_resource_owner
|
16
|
+
migration_template(
|
17
|
+
"remove_applications_secret_not_null_constraint.rb.erb",
|
18
|
+
"db/migrate/remove_applications_secret_not_null_constraint.rb",
|
19
|
+
migration_version: migration_version,
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.next_migration_number(dirname)
|
24
|
+
ActiveRecord::Generators::Base.next_migration_number(dirname)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def migration_version
|
30
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -5,6 +5,7 @@ class CreateDoorkeeperTables < ActiveRecord::Migration<%= migration_version %>
|
|
5
5
|
create_table :oauth_applications do |t|
|
6
6
|
t.string :name, null: false
|
7
7
|
t.string :uid, null: false
|
8
|
+
# Remove `null: false` or use conditional constraint if you are planning to use public clients.
|
8
9
|
t.string :secret, null: false
|
9
10
|
|
10
11
|
# Remove `null: false` if you are planning to use grant flows
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: doorkeeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felipe Elias Philipp
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2024-
|
14
|
+
date: 2024-12-09 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: railties
|
@@ -290,6 +290,8 @@ files:
|
|
290
290
|
- lib/doorkeeper/request/refresh_token.rb
|
291
291
|
- lib/doorkeeper/request/strategy.rb
|
292
292
|
- lib/doorkeeper/request/token.rb
|
293
|
+
- lib/doorkeeper/revocable_tokens/revocable_access_token.rb
|
294
|
+
- lib/doorkeeper/revocable_tokens/revocable_refresh_token.rb
|
293
295
|
- lib/doorkeeper/secret_storing/base.rb
|
294
296
|
- lib/doorkeeper/secret_storing/bcrypt.rb
|
295
297
|
- lib/doorkeeper/secret_storing/plain.rb
|
@@ -305,6 +307,7 @@ files:
|
|
305
307
|
- lib/generators/doorkeeper/migration_generator.rb
|
306
308
|
- lib/generators/doorkeeper/pkce_generator.rb
|
307
309
|
- lib/generators/doorkeeper/previous_refresh_token_generator.rb
|
310
|
+
- lib/generators/doorkeeper/remove_applications_secret_not_null_constraint_generator.rb
|
308
311
|
- lib/generators/doorkeeper/templates/README
|
309
312
|
- lib/generators/doorkeeper/templates/add_confidential_to_applications.rb.erb
|
310
313
|
- lib/generators/doorkeeper/templates/add_owner_to_application_migration.rb.erb
|
@@ -313,6 +316,7 @@ files:
|
|
313
316
|
- lib/generators/doorkeeper/templates/enable_polymorphic_resource_owner_migration.rb.erb
|
314
317
|
- lib/generators/doorkeeper/templates/initializer.rb
|
315
318
|
- lib/generators/doorkeeper/templates/migration.rb.erb
|
319
|
+
- lib/generators/doorkeeper/templates/remove_applications_secret_not_null_constraint.rb.erb
|
316
320
|
- lib/generators/doorkeeper/views_generator.rb
|
317
321
|
- vendor/assets/stylesheets/doorkeeper/bootstrap.min.css
|
318
322
|
homepage: https://github.com/doorkeeper-gem/doorkeeper
|
@@ -324,6 +328,7 @@ metadata:
|
|
324
328
|
source_code_uri: https://github.com/doorkeeper-gem/doorkeeper
|
325
329
|
bug_tracker_uri: https://github.com/doorkeeper-gem/doorkeeper/issues
|
326
330
|
documentation_uri: https://doorkeeper.gitbook.io/guides/
|
331
|
+
funding_uri: https://opencollective.com/doorkeeper-gem
|
327
332
|
post_install_message: "Starting from 5.5.0 RC1 Doorkeeper requires client authentication
|
328
333
|
for Resource Owner Password Grant\nas stated in the OAuth RFC. You have to create
|
329
334
|
a new OAuth client (Doorkeeper::Application) if you didn't\nhave it before and use
|
@@ -346,7 +351,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
346
351
|
- !ruby/object:Gem::Version
|
347
352
|
version: '0'
|
348
353
|
requirements: []
|
349
|
-
rubygems_version: 3.
|
354
|
+
rubygems_version: 3.5.15
|
350
355
|
signing_key:
|
351
356
|
specification_version: 4
|
352
357
|
summary: OAuth 2 provider for Rails and Grape
|