doorkeeper 5.9.6 → 5.9.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 233efb73ba5d84b316e665251df23989888d403dd553cd34375891645c00eb14
4
- data.tar.gz: eac0e2ca7376e065d96d43c81cafc54931f77386f2b8b2c00d1a69f7ca0f4b93
3
+ metadata.gz: b21e3bb3454e1896aa0a0d022741de322d5cf604de408dfe58a1a4ef2a21cfbc
4
+ data.tar.gz: 9384048264886cad0506a879964635253fb00a4903a6347e7801c066c6f971d3
5
5
  SHA512:
6
- metadata.gz: 57c525e75aee988ee335c12415b6d0cea2490890a4ae83dbccc5a9e4b16d6589792aa9c9032ccfb38dd63a30cf9d8080ec87913afe31d577cf07baa7ef916497
7
- data.tar.gz: 6dd5a81b7fab1497885c73b601459ab6a12245dff817a17fbba3d40c39bf38a45571ef8526a09a3800baeeabd7e148ceefd412d970b5bff19df8d59374b56d74
6
+ metadata.gz: 18f0100ea947793a51547ee3de5ffd0927b8c4a6e1a9b491448c4149927ec34521b4d28c45056c5ca0d4b6dd87f79b796167e463f0a4cb1830eb2bf5732fc9b8
7
+ data.tar.gz: 16c2996fa91d47283fcbf491773d7f2ed4d0e14b8faf0566f6757875de1b31af3ac058e19a77487c74ec13ca6dfde6a619048484b73f5a4bccc9814441a24122
data/CHANGELOG.md CHANGED
@@ -5,6 +5,11 @@ upgrade guides.
5
5
 
6
6
  User-visible changes worth mentioning.
7
7
 
8
+ ## 5.9.7
9
+
10
+ - Refuse requests that transmit an access token by more than one method (RFC 6750 §2), instead of silently authorizing with the first configured `access_token_methods` entry that matched and discarding the other tokens. Such a request now fails closed as carrying no usable token (401 `invalid_token`); no calling contract changes. The form-encoded body (§2.2) and the URI query (§2.3) count as two methods even though Rails and Rack merge them into a single `params` hash. The same token repeated across two methods is refused too — §2 forbids the second method, not a disagreement between the two — and a custom callable extractor in `access_token_methods` keeps the historical first-wins behavior and is never invoked more than once. (The strict `invalid_request` (400) answer §3.1 prescribes ships with Doorkeeper 6.0.)
11
+ - [#1925] Internal: pin the development dependency on `json` below 3.0. json 3 removed the positional options Hash from `JSON.parse` and the `quirks_mode` option from `JSON.generate`, both of which Active Support still uses, so the suite could not run on any supported Rails version.
12
+
8
13
  ## 5.9.6
9
14
 
10
15
  - Reject requests that present more than one client identity (e.g. an `Authorization: Basic` header for one client and a `client_id` parameter naming another) with an `invalid_request` error, instead of authenticating the first extracted identity and silently discarding the other one. A `client_id` sent alongside another authentication method keeps working when it identifies the same client (RFC 7521 §4.2). Like the RFC 6749 §2.3 check released in 5.9.5, this validation does not apply when `client_credentials` is configured with a callable extractor, since the credentials the remaining extractors would return are never evaluated — the `client_credentials` option documents that now.
@@ -62,7 +67,6 @@ User-visible changes worth mentioning.
62
67
  - [#1779] Only lock previous access token model when creating a new token from its refresh token if revoke_previous_refresh_token_on_use is false
63
68
  - [#1778] Ensure that token revocation is idempotent by checking that that token has not already been revoked before revoking.
64
69
 
65
-
66
70
  ## 5.8.2
67
71
 
68
72
  - [#1755] Fix the error message for force_pkce
@@ -3,8 +3,54 @@
3
3
  module Doorkeeper
4
4
  module OAuth
5
5
  class Token
6
+ # Built-in extractors that read a request parameter, and the parameter
7
+ # each of them reads. RFC 6750 treats the form-encoded body (§2.2) and
8
+ # the URI query string (§2.3) as two distinct transmission methods, but
9
+ # Rack and ActionDispatch both collapse them into a single parameter
10
+ # hash — ActionDispatch lets the query win, Rack lets the body win — so
11
+ # a request carrying the parameter in both would present a single value
12
+ # to the extractor and never be refused. The multi-method check reads
13
+ # the two sources separately for these extractors; selection keeps
14
+ # using the extractor, so which one wins is unchanged.
15
+ PARAMETER_EXTRACTORS = {
16
+ from_access_token_param: "access_token",
17
+ from_bearer_param: "bearer_token",
18
+ }.freeze
19
+
6
20
  class << self
21
+ # RFC 6750 §2: "Clients MUST NOT use more than one method to transmit
22
+ # the token in each request", and §3.1 lists using more than one method
23
+ # among the conditions an invalid_request answers. Returning the first
24
+ # method that yields a value would discard every other token presented
25
+ # in the same request with no error, warning or log entry, leaving
26
+ # which token authorizes the request to be decided by the configured
27
+ # order of +access_token_methods+ rather than by what the caller sent —
28
+ # so a layer in front of Doorkeeper that reads a different one of them
29
+ # can reach a different verdict about the very same request.
30
+ #
31
+ # What §2 forbids is using more than one method, so the check counts
32
+ # transmission methods rather than comparing the tokens they carry:
33
+ # the same value presented twice is still two methods. Refusal answers
34
+ # nil: the caller is told the request carries no usable token and
35
+ # fails closed with the invalid_token (401) response, keeping every
36
+ # calling contract on this stable branch intact — the invalid_request
37
+ # (400) that §3.1 strictly prescribes needs a new error and render
38
+ # path, so it ships with 6.0 only.
39
+ #
40
+ # Only the built-in extractors — symbols naming methods on this class,
41
+ # all of them side-effect-free reads of the request — take part in
42
+ # that check. A custom callable extractor is a configuration adapter
43
+ # rather than a transmission method: it keeps the historical
44
+ # first-wins selection and is never invoked more than once, the same
45
+ # exemption client authentication gives its legacy callable
46
+ # extractors (Request#validate_client_authentication!).
7
47
  def from_request(request, *methods)
48
+ used = methods.sum do |method|
49
+ method.is_a?(Symbol) ? transmission_methods_used(request, method) : 0
50
+ end
51
+
52
+ return if used > 1
53
+
8
54
  methods.inject(nil) do |_, method|
9
55
  method = self.method(method) if method.is_a?(Symbol)
10
56
  credentials = method.call(request)
@@ -44,6 +90,36 @@ module Doorkeeper
44
90
 
45
91
  private
46
92
 
93
+ # How many transmission methods the given built-in extractor finds a
94
+ # token in. Usually one, or none — but for the parameter extractors
95
+ # above it is the body and the query counted separately, since the
96
+ # parameter hash collapsed them into the single value the extractor
97
+ # reads. That value is counted on its own only when neither raw source
98
+ # explains it, so a token reaching the extractor by some other route —
99
+ # an :access_token path segment, or a host that overrode the extractor
100
+ # — is still counted exactly once rather than twice.
101
+ def transmission_methods_used(request, method)
102
+ value = self.method(method).call(request).presence
103
+
104
+ parameter = PARAMETER_EXTRACTORS[method]
105
+ return value ? 1 : 0 unless parameter
106
+
107
+ sources = parameter_sources(request, parameter)
108
+ sources.size + (value && !sources.include?(value) ? 1 : 0)
109
+ end
110
+
111
+ # The form-encoded body (§2.2) and the URI query (§2.3) as Rack
112
+ # exposes them, for a request object that keeps the two apart.
113
+ def parameter_sources(request, parameter)
114
+ return [] unless request.respond_to?(:GET) && request.respond_to?(:POST)
115
+
116
+ query = request.GET
117
+ body = request.POST
118
+ return [] unless query.is_a?(Hash) && body.is_a?(Hash)
119
+
120
+ [query[parameter], body[parameter]].filter_map(&:presence)
121
+ end
122
+
47
123
  def token_from_basic_header(header, pattern)
48
124
  encoded_header = token_from_header(header, pattern)
49
125
  decode_basic_credentials_token(encoded_header)
@@ -5,7 +5,7 @@ module Doorkeeper
5
5
  # Semantic versioning
6
6
  MAJOR = 5
7
7
  MINOR = 9
8
- TINY = 6
8
+ TINY = 7
9
9
  PRE = nil
10
10
 
11
11
  # Full version number
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.9.6
4
+ version: 5.9.7
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: 2026-08-11 00:00:00.000000000 Z
14
+ date: 2026-09-10 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: railties