doorkeeper 5.6.9 → 5.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bf87231b7fa13bb47db61868954af3c01b07b0909ac95abb2de15d1064dd423
4
- data.tar.gz: f6dda19eae61f69331fc338fbb1803a7a94c563b4dc46ea694bee37c430db1b7
3
+ metadata.gz: aeb5db3b840c0c214129e69fb029c1daf95974b033856ab550e9abd3b529b0c1
4
+ data.tar.gz: 6f948a7fa2b8a2796c56e94a762b1e256228c13f972bbd056d1b4e246bea5001
5
5
  SHA512:
6
- metadata.gz: f0d54d75716f74f23f35ab9400683a805e3a4c7483c11ea5be10669abe38385c7625f9304379712f661bea026d49cb5fe8ddc1b6653cf66115279edc1f785096
7
- data.tar.gz: 68c841037b9544b1bdfdc36405169ad8cf942b6c32b6d1023261bb7c37116bab602a8c12b209d3b006219eaf0701d7a705ac6cdec49004c19ae918d044377d53
6
+ metadata.gz: 2da3aca020ed25ba334d5dfbcbffd1c46818eaa79bbc67e38bcf01dada8105c6683bfb5552dae1cdce34a68e5baafa0c9a9b49b5c980f78464925f2f7576b873
7
+ data.tar.gz: 865a9c03a28b309624e8058a251c92d002199f9072015efafafb622fad5878274042bee32aba169a0395fc1ba0558ca86c1bd7b32b9dc288c5136b69c6a68e69
data/CHANGELOG.md CHANGED
@@ -7,7 +7,15 @@ User-visible changes worth mentioning.
7
7
 
8
8
  ## main
9
9
 
10
- - [#PR ID] Add your changelog here.
10
+ Add your entry here.
11
+
12
+ ## 5.7.0
13
+
14
+ - [#1696] Add missing `#issued_token` method to `OAuth::TokenResponse`
15
+ - [#1697] Allow a TokenResponse body to be customized (memoize response body).
16
+ - [#1702] Fix bugs for error response in the form_post and error view
17
+ - [#1660] Custom access token attributes are now considered when finding matching tokens (fixes #1665).
18
+ Introduce `revoke_previous_client_credentials_token` configuration option.
11
19
 
12
20
  ## 5.6.9
13
21
 
@@ -33,17 +41,17 @@ User-visible changes worth mentioning.
33
41
  - [#1648] Add custom token attributes to Refresh Token Request.
34
42
  - [#1649] Fixed custom_access_token_attributes related errors.
35
43
 
36
- # 5.6.5
44
+ ## 5.6.5
37
45
 
38
46
  - [#1602] Allow custom data to be stored inside access grants/tokens.
39
47
  - [#1634] Code refactoring for custom token attributes.
40
48
  - [#1639] Add grant type validation to avoid Internal Server Error for DELETE /oauth/authorize endpoint.
41
49
 
42
- # 5.6.4
50
+ ## 5.6.4
43
51
 
44
52
  - [#1633] Apply ORM configuration in #to_prepare block to avoid autoloading errors.
45
53
 
46
- # 5.6.3
54
+ ## 5.6.3
47
55
 
48
56
  - [#1622] Drop support for Rubies 2.5 and 2.6
49
57
  - [#1605] Fix URI validation for Ruby 3.2+.
@@ -31,7 +31,7 @@ module Doorkeeper
31
31
  private
32
32
 
33
33
  def render_success
34
- if skip_authorization? || (matching_token? && pre_auth.client.application.confidential?)
34
+ if skip_authorization? || can_authorize_response?
35
35
  redirect_or_render(authorize_response)
36
36
  elsif Doorkeeper.configuration.api_only
37
37
  render json: pre_auth
@@ -52,10 +52,16 @@ module Doorkeeper
52
52
  end
53
53
  end
54
54
 
55
+ def can_authorize_response?
56
+ Doorkeeper.config.custom_access_token_attributes.empty? && pre_auth.client.application.confidential? && matching_token?
57
+ end
58
+
55
59
  # Active access token issued for the same client and resource owner with
56
60
  # the same set of the scopes exists?
57
61
  def matching_token?
58
- Doorkeeper.config.access_token_model.matching_token_for(
62
+ # We don't match tokens on the custom attributes here - we're in the pre-auth here,
63
+ # so they haven't been supplied yet (there are no custom attributes to match on yet)
64
+ @matching_token ||= Doorkeeper.config.access_token_model.matching_token_for(
59
65
  pre_auth.client,
60
66
  current_resource_owner,
61
67
  pre_auth.scopes,
@@ -77,7 +83,7 @@ module Doorkeeper
77
83
  )
78
84
  end
79
85
  elsif pre_auth.form_post_response?
80
- render :form_post
86
+ render :form_post, locals: { auth: auth }
81
87
  else
82
88
  redirect_to auth.redirect_uri, allow_other_host: true
83
89
  end
@@ -4,6 +4,6 @@
4
4
 
5
5
  <main role="main">
6
6
  <pre>
7
- <%= (respond_to?(:error_response) ? error_response : @pre_auth.error_response).body[:error_description] %>
7
+ <%= (local_assigns[:error_response] ? error_response : @pre_auth.error_response).body[:error_description] %>
8
8
  </pre>
9
9
  </main>
@@ -3,7 +3,7 @@
3
3
  </header>
4
4
 
5
5
  <%= form_tag @pre_auth.redirect_uri, method: :post, name: :redirect_form, authenticity_token: false do %>
6
- <% @authorize_response.body.compact.each do |key, value| %>
6
+ <% auth.body.compact.each do |key, value| %>
7
7
  <%= hidden_field_tag key, value %>
8
8
  <% end %>
9
9
  <% end %>
@@ -106,6 +106,13 @@ module Doorkeeper
106
106
  @config.instance_variable_set(:@revoke_previous_client_credentials_token, true)
107
107
  end
108
108
 
109
+ # Only allow one valid access token obtained via authorization code
110
+ # per client. If a new access token is obtained before the old one
111
+ # expired, the old one gets revoked (disabled by default)
112
+ def revoke_previous_authorization_code_token
113
+ @config.instance_variable_set(:@revoke_previous_authorization_code_token, true)
114
+ end
115
+
109
116
  # Use an API mode for applications generated with --api argument
110
117
  # It will skip applications controller, disable forgery protection
111
118
  def api_only
@@ -481,6 +488,10 @@ module Doorkeeper
481
488
  option_set? :revoke_previous_client_credentials_token
482
489
  end
483
490
 
491
+ def revoke_previous_authorization_code_token?
492
+ option_set? :revoke_previous_authorization_code_token
493
+ end
494
+
484
495
  def enforce_configured_scopes?
485
496
  option_set? :enforce_configured_scopes
486
497
  end
@@ -83,14 +83,18 @@ module Doorkeeper
83
83
  # Resource Owner model instance or it's ID
84
84
  # @param scopes [String, Doorkeeper::OAuth::Scopes]
85
85
  # set of scopes
86
+ # @param custom_attributes [Nilable Hash]
87
+ # A nil value, or hash with keys corresponding to the custom attributes
88
+ # configured with the `custom_access_token_attributes` config option.
89
+ # A nil value will ignore custom attributes.
86
90
  #
87
91
  # @return [Doorkeeper::AccessToken, nil] Access Token instance or
88
92
  # nil if matching record was not found
89
93
  #
90
- def matching_token_for(application, resource_owner, scopes, include_expired: true)
94
+ def matching_token_for(application, resource_owner, scopes, custom_attributes: nil, include_expired: true)
91
95
  tokens = authorized_tokens_for(application&.id, resource_owner)
92
96
  tokens = tokens.not_expired unless include_expired
93
- find_matching_token(tokens, application, scopes)
97
+ find_matching_token(tokens, application, custom_attributes, scopes)
94
98
  end
95
99
 
96
100
  # Interface to enumerate access token records in batches in order not
@@ -114,11 +118,15 @@ module Doorkeeper
114
118
  # Application instance
115
119
  # @param scopes [String, Doorkeeper::OAuth::Scopes]
116
120
  # set of scopes
121
+ # @param custom_attributes [Nilable Hash]
122
+ # A nil value, or hash with keys corresponding to the custom attributes
123
+ # configured with the `custom_access_token_attributes` config option.
124
+ # A nil value will ignore custom attributes.
117
125
  #
118
126
  # @return [Doorkeeper::AccessToken, nil] Access Token instance or
119
127
  # nil if matching record was not found
120
128
  #
121
- def find_matching_token(relation, application, scopes)
129
+ def find_matching_token(relation, application, custom_attributes, scopes)
122
130
  return nil unless relation
123
131
 
124
132
  matching_tokens = []
@@ -126,7 +134,8 @@ module Doorkeeper
126
134
 
127
135
  find_access_token_in_batches(relation, batch_size: batch_size) do |batch|
128
136
  tokens = batch.select do |token|
129
- scopes_match?(token.scopes, scopes, application&.scopes)
137
+ scopes_match?(token.scopes, scopes, application&.scopes) &&
138
+ custom_attributes_match?(token, custom_attributes)
130
139
  end
131
140
 
132
141
  matching_tokens.concat(tokens)
@@ -160,6 +169,31 @@ module Doorkeeper
160
169
  )
161
170
  end
162
171
 
172
+ # Checks whether the token custom attribute values match the custom
173
+ # attributes from the parameters.
174
+ #
175
+ # @param token [Doorkeeper::AccessToken]
176
+ # The access token whose custom attributes are being compared
177
+ # to the custom_attributes.
178
+ #
179
+ # @param custom_attributes [Hash]
180
+ # A hash of the attributes for which we want to determine whether
181
+ # the token's custom attributes match.
182
+ #
183
+ # @return [Boolean] true if the token's custom attribute values
184
+ # match those in the custom_attributes, or if both are empty/blank.
185
+ # False otherwise.
186
+ def custom_attributes_match?(token, custom_attributes)
187
+ return true if custom_attributes.nil?
188
+
189
+ token_attribs = token.custom_attributes
190
+ return true if token_attribs.blank? && custom_attributes.blank?
191
+
192
+ Doorkeeper.config.custom_access_token_attributes.all? do |attribute|
193
+ token_attribs[attribute] == custom_attributes[attribute]
194
+ end
195
+ end
196
+
163
197
  # Looking for not expired AccessToken record with a matching set of
164
198
  # scopes that belongs to specific Application and Resource Owner.
165
199
  # If it doesn't exists - then creates it.
@@ -181,7 +215,9 @@ module Doorkeeper
181
215
  #
182
216
  def find_or_create_for(application:, resource_owner:, scopes:, **token_attributes)
183
217
  if Doorkeeper.config.reuse_access_token
184
- access_token = matching_token_for(application, resource_owner, scopes, include_expired: false)
218
+ custom_attributes = extract_custom_attributes(token_attributes).presence
219
+ access_token = matching_token_for(
220
+ application, resource_owner, scopes, custom_attributes: custom_attributes, include_expired: false)
185
221
 
186
222
  return access_token if access_token&.reusable?
187
223
  end
@@ -276,6 +312,18 @@ module Doorkeeper
276
312
  def fallback_secret_strategy
277
313
  ::Doorkeeper.config.token_secret_fallback_strategy
278
314
  end
315
+
316
+ # Extracts the token's custom attributes (defined by the
317
+ # custom_access_token_attributes config option) from the token's attributes.
318
+ #
319
+ # @param attributes [Hash]
320
+ # A hash of the access token's attributes.
321
+ # @return [Hash]
322
+ # A hash containing only the custom access token attributes.
323
+ def extract_custom_attributes(attributes)
324
+ attributes.with_indifferent_access.slice(
325
+ *Doorkeeper.configuration.custom_access_token_attributes)
326
+ end
279
327
  end
280
328
 
281
329
  # Access Token type: Bearer.
@@ -308,6 +356,14 @@ module Doorkeeper
308
356
  end
309
357
  end
310
358
 
359
+ # The token's custom attributes, as defined by
360
+ # the custom_access_token_attributes config option.
361
+ #
362
+ # @return [Hash] hash of custom access token attributes.
363
+ def custom_attributes
364
+ self.class.extract_custom_attributes(attributes)
365
+ end
366
+
311
367
  # Indicates whether the token instance have the same credential
312
368
  # as the other Access Token.
313
369
  #
@@ -29,6 +29,10 @@ module Doorkeeper
29
29
  grant.lock!
30
30
  raise Errors::InvalidGrantReuse if grant.revoked?
31
31
 
32
+ if Doorkeeper.config.revoke_previous_authorization_code_token?
33
+ revoke_previous_tokens(grant.application, resource_owner)
34
+ end
35
+
32
36
  grant.revoke
33
37
 
34
38
  find_or_create_access_token(
@@ -109,6 +113,10 @@ module Doorkeeper
109
113
  .slice(*Doorkeeper.config.custom_access_token_attributes)
110
114
  .symbolize_keys
111
115
  end
116
+
117
+ def revoke_previous_tokens(application, resource_owner)
118
+ Doorkeeper.config.access_token_model.revoke_all_for(application.id, resource_owner)
119
+ end
112
120
  end
113
121
  end
114
122
  end
@@ -8,7 +8,7 @@ module Doorkeeper
8
8
  existing_token = nil
9
9
 
10
10
  if lookup_existing_token?
11
- existing_token = find_active_existing_token_for(client, scopes)
11
+ existing_token = find_active_existing_token_for(client, scopes, attributes)
12
12
  return existing_token if Doorkeeper.config.reuse_access_token && existing_token&.reusable?
13
13
  end
14
14
 
@@ -44,8 +44,11 @@ module Doorkeeper
44
44
  Doorkeeper.config.revoke_previous_client_credentials_token?
45
45
  end
46
46
 
47
- def find_active_existing_token_for(client, scopes)
48
- Doorkeeper.config.access_token_model.matching_token_for(client, nil, scopes, include_expired: false)
47
+ def find_active_existing_token_for(client, scopes, attributes)
48
+ custom_attributes = Doorkeeper.config.access_token_model.
49
+ extract_custom_attributes(attributes).presence
50
+ Doorkeeper.config.access_token_model.matching_token_for(
51
+ client, nil, scopes, custom_attributes: custom_attributes, include_expired: false)
49
52
  end
50
53
  end
51
54
  end
@@ -31,7 +31,7 @@ module Doorkeeper
31
31
  @code_challenge = parameters[:code_challenge]
32
32
  @code_challenge_method = parameters[:code_challenge_method]
33
33
  @resource_owner = resource_owner
34
- @custom_access_token_attributes = parameters.slice(*Doorkeeper.config.custom_access_token_attributes)
34
+ @custom_access_token_attributes = parameters.slice(*Doorkeeper.config.custom_access_token_attributes).to_h
35
35
  end
36
36
 
37
37
  def authorizable?
@@ -5,12 +5,14 @@ module Doorkeeper
5
5
  class TokenResponse
6
6
  attr_reader :token
7
7
 
8
+ alias issued_token token
9
+
8
10
  def initialize(token)
9
11
  @token = token
10
12
  end
11
13
 
12
14
  def body
13
- {
15
+ @body ||= {
14
16
  "access_token" => token.plaintext_token,
15
17
  "token_type" => token.token_type,
16
18
  "expires_in" => token.expires_in_seconds,
@@ -4,8 +4,8 @@ module Doorkeeper
4
4
  module VERSION
5
5
  # Semantic versioning
6
6
  MAJOR = 5
7
- MINOR = 6
8
- TINY = 9
7
+ MINOR = 7
8
+ TINY = 0
9
9
  PRE = nil
10
10
 
11
11
  # Full version number
@@ -32,7 +32,7 @@ Doorkeeper.configure do
32
32
  # You can use your own model classes if you need to extend (or even override) default
33
33
  # Doorkeeper models such as `Application`, `AccessToken` and `AccessGrant.
34
34
  #
35
- # Be default Doorkeeper ActiveRecord ORM uses it's own classes:
35
+ # By default Doorkeeper ActiveRecord ORM uses its own classes:
36
36
  #
37
37
  # access_token_class "Doorkeeper::AccessToken"
38
38
  # access_grant_class "Doorkeeper::AccessGrant"
@@ -91,7 +91,10 @@ Doorkeeper.configure do
91
91
  # authorization_code_expires_in 10.minutes
92
92
 
93
93
  # Access token expiration time (default: 2 hours).
94
- # If you want to disable expiration, set this to `nil`.
94
+ # If you set this to `nil` Doorkeeper will not expire the token and omit expires_in in response.
95
+ # It is RECOMMENDED to set expiration time explicitly.
96
+ # Prefer access_token_expires_in 100.years or similar,
97
+ # which would be functionally equivalent and avoid the risk of unexpected behavior by callers.
95
98
  #
96
99
  # access_token_expires_in 2.hours
97
100
 
@@ -164,6 +167,12 @@ Doorkeeper.configure do
164
167
  #
165
168
  # revoke_previous_client_credentials_token
166
169
 
170
+ # Only allow one valid access token obtained via authorization code
171
+ # per client. If a new access token is obtained before the old one
172
+ # expired, the old one gets revoked (disabled by default)
173
+ #
174
+ # revoke_previous_authorization_code_token
175
+
167
176
  # Hash access and refresh tokens before persisting them.
168
177
  # This will disable the possibility to use +reuse_access_token+
169
178
  # since plain values can no longer be retrieved.
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.6.9
4
+ version: 5.7.0
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-02-14 00:00:00.000000000 Z
14
+ date: 2024-04-24 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: railties
@@ -346,7 +346,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
346
346
  - !ruby/object:Gem::Version
347
347
  version: '0'
348
348
  requirements: []
349
- rubygems_version: 3.1.6
349
+ rubygems_version: 3.2.3
350
350
  signing_key:
351
351
  specification_version: 4
352
352
  summary: OAuth 2 provider for Rails and Grape