doorkeeper-openid_connect 1.10.0 → 1.10.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b09f0d036559583b3db8dd63e6e35452e5f9c42c90ce098ab263c1817016994
4
- data.tar.gz: f9c9584568d90c9d351a347d435ba471d8e798aa42c9ac28edebefcd59d33c1a
3
+ metadata.gz: f6e40af64a066cdb6b80a91eb5ecb97016c6c4a0695c4a362f72942577b7a1bd
4
+ data.tar.gz: 57999fcd4eba595ca726f41767c301dbd652b76ea039787d9f00fa5177b7aefa
5
5
  SHA512:
6
- metadata.gz: a5a7f986bd19adcc2f3b4a03467d6052e32722f06162446b5ba944a8b905a111aa27528ccfc827e493382e95ab9dc89834a4ee483895951883291bda1387db81
7
- data.tar.gz: c9fc3444e391e9b971bd193b18f388dafa47c5f5d6188d055fad2f524f9c8d6ceb67929a6b2ab5179ab50ff3c854e8d4883c9f6a43b9cd650cceba97a2e4dee2
6
+ metadata.gz: 7a722b6a8cf208f7c9ba6af64c8b4518a20d206d991f81f45d0a5fa21fffead56f6c134c0bb65f1de9e8d0addba5b3db408df02ed10d9a70f55b4d343ee3fb6c
7
+ data.tar.gz: afd99576ba3f10b38b55cd9cb2ed721ebed97a33aba778939c6aabae07ca91a07a2f4531a178716dda14adb2b4641a627c2bf9d38a8fe8b89d65a1e971ecd5c4
data/CHANGELOG.md CHANGED
@@ -2,8 +2,21 @@
2
2
 
3
3
  - Please add here
4
4
 
5
+ ## v1.10.1 (2026-06-03)
6
+
7
+ - [#294] Drop stale `Metrics/ClassLength` and `Metrics/BlockLength` overrides from `.rubocop_todo.yml`
8
+ - [#293] Drop `Naming/VariableNumber` from `.rubocop_todo.yml` and normalise test variable names
9
+ - [#291] Document multi-namespace mount pattern for multiple resource owner models ([#192](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/192))
10
+ - [#292] Drop formatting cops from `.rubocop_todo.yml` and align trailing-comma style with upstream doorkeeper
11
+ - [#296] Fix the `prompt` parameter being rejected with `invalid_request` when it contains leading or duplicate spaces (e.g. `prompt=%20none`) — blank entries in the space-delimited value are now ignored
12
+ - [#299] Raise `InvalidConfiguration` when the `issuer` config resolves to a blank value instead of silently advertising an empty `issuer` in the discovery document. Since v1.10.0 an arity-2 `issuer` block receives `(resource_owner, application)` — both `nil` in the discovery context — so a block relying on the old v1.9.0 request argument could return `nil` and produce a discovery `issuer` that mismatched the ID token `iss` ([#298](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/298))
13
+
5
14
  ## v1.10.0 (2026-06-01)
6
15
 
16
+ >[!IMPORTANT]
17
+ >
18
+ >- **Breaking (arity-2 issuer blocks):** `resolve_issuer` now dispatches arity-2 blocks with `(resource_owner, application)` in all contexts, including discovery. In v1.9.0 `DiscoveryController` passed `request` as the first argument; existing arity-2 blocks that relied on this receive `(nil, nil)` in v1.10.0 and should migrate to arity-3 — see [#298](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/298) for details and migration examples
19
+
7
20
  - [#241] Fix NameError on doorkeeper master by deferring AR model loading in run_hooks (see [Doorkeeper PR](https://github.com/doorkeeper-gem/doorkeeper/pull/1804))
8
21
  - [#242] Fix `NoMethodError` for openid_request in testing environments.
9
22
  - [#246] Fix `at_hash` to use correct hash algorithm based on `signing_algorithm`
data/README.md CHANGED
@@ -414,6 +414,92 @@ After this, `.well-known/openid-configuration` returns `"jwks_uri": "https://exa
414
414
  > [!Note]
415
415
  > A naive `match "/-/jwks", ..., as: :oauth_discovery_keys` won't work — Rails has refused to reuse a route name [since 4.0](https://github.com/rails/rails/commit/a2b7c0e69d) and raises `ArgumentError: Invalid route name, already in use: 'oauth_discovery_keys'`. The `direct` helper sidesteps this by overriding the URL helper itself rather than re-declaring the route name.
416
416
 
417
+ #### Mounting under multiple namespaces (multiple resource owner models)
418
+
419
+ If your app authenticates more than one kind of resource owner (e.g. a `User`
420
+ and a `Customer` Devise model) you may want to mount Doorkeeper — and this engine
421
+ — more than once, each under its own namespace:
422
+
423
+ ```ruby
424
+ # config/routes.rb
425
+ Rails.application.routes.draw do
426
+ scope :users, as: :users do
427
+ use_doorkeeper { controllers authorizations: "users/authorizations" }
428
+ use_doorkeeper_openid_connect
429
+ end
430
+
431
+ scope :customers, as: :customers do
432
+ use_doorkeeper { controllers authorizations: "customers/authorizations" }
433
+ use_doorkeeper_openid_connect
434
+ end
435
+ end
436
+ ```
437
+
438
+ Most of the request flow is model-agnostic: the `resource_owner_authenticator`
439
+ block can dispatch on whichever owner is signed in (`current_user ||
440
+ current_customer`), and claims / userinfo / ID token generation follow from
441
+ whatever it returns.
442
+
443
+ The one piece that needs attention is the **discovery document**.
444
+ [`DiscoveryController#provider_response`](app/controllers/doorkeeper/openid_connect/discovery_controller.rb)
445
+ builds the published endpoints by calling named route helpers
446
+ (`oauth_authorization_url`, `oauth_token_url`, …) directly. The
447
+ [`discovery_url_options`](#configuration) setting (added in #126) lets you
448
+ override the `host` / `protocol` / `port` of those URLs, but not *which* named
449
+ helper is resolved — so under multiple mounts every namespace's discovery
450
+ document would point at the same set of endpoints.
451
+
452
+ The idiomatic fix is to subclass the discovery controller per namespace and
453
+ re-point the helper calls at that namespace's routes:
454
+
455
+ ```ruby
456
+ # app/controllers/users/discovery_controller.rb
457
+ module Users
458
+ class DiscoveryController < Doorkeeper::OpenidConnect::DiscoveryController
459
+ private
460
+
461
+ # Re-point each helper used by `provider_response` at the namespaced route.
462
+ def oauth_authorization_url(opts = {})
463
+ users_oauth_authorization_url(opts)
464
+ end
465
+
466
+ def oauth_token_url(opts = {})
467
+ users_oauth_token_url(opts)
468
+ end
469
+
470
+ def oauth_revoke_url(opts = {})
471
+ users_oauth_revoke_url(opts)
472
+ end
473
+
474
+ def oauth_userinfo_url(opts = {})
475
+ users_oauth_userinfo_url(opts)
476
+ end
477
+
478
+ def oauth_discovery_keys_url(opts = {})
479
+ users_oauth_discovery_keys_url(opts)
480
+ end
481
+
482
+ # ...and `oauth_introspect_url` / `oauth_dynamic_client_registration_url`
483
+ # if you advertise those.
484
+ end
485
+ end
486
+ ```
487
+
488
+ ```ruby
489
+ # config/routes.rb (inside the `:users` scope)
490
+ get "/.well-known/openid-configuration",
491
+ to: "users/discovery#provider", as: :users_openid_connect_config
492
+ ```
493
+
494
+ Repeat for the `customers` namespace. Each `.well-known/openid-configuration`
495
+ then advertises the endpoints for its own namespace.
496
+
497
+ > [!Note]
498
+ > See [#192](https://github.com/doorkeeper-gem/doorkeeper-openid_connect/issues/192)
499
+ > for the original discussion. First-class multi-mount support is not provided
500
+ > out of the box; the per-namespace controller override above is the supported
501
+ > extension pattern for now.
502
+
417
503
  ### Nonces
418
504
 
419
505
  To support clients who send nonces you have to tweak Doorkeeper's authorization view so the parameter is passed on.
@@ -53,7 +53,7 @@ module Doorkeeper
53
53
  subject_types_supported: openid_connect.subject_types_supported,
54
54
 
55
55
  id_token_signing_alg_values_supported: [
56
- ::Doorkeeper::OpenidConnect.signing_algorithm
56
+ ::Doorkeeper::OpenidConnect.signing_algorithm,
57
57
  ],
58
58
 
59
59
  claim_types_supported: [
@@ -93,8 +93,8 @@ module Doorkeeper
93
93
  {
94
94
  rel: WEBFINGER_RELATION,
95
95
  href: issuer,
96
- }
97
- ]
96
+ },
97
+ ],
98
98
  }
99
99
  end
100
100
 
@@ -113,7 +113,7 @@ module Doorkeeper
113
113
 
114
114
  def discovery_url_default_options
115
115
  {
116
- protocol: protocol
116
+ protocol: protocol,
117
117
  }
118
118
  end
119
119
 
@@ -17,7 +17,7 @@ module Doorkeeper
17
17
  render json: registration_response(client, registration), status: :created
18
18
  rescue ActiveRecord::RecordInvalid => e
19
19
  render json: { error: "invalid_client_params", error_description: e.record.errors.full_messages.join(", ") },
20
- status: :bad_request
20
+ status: :bad_request
21
21
  end
22
22
 
23
23
  private
@@ -22,4 +22,5 @@ en:
22
22
  select_account_for_resource_owner_not_configured: 'Failure due to Doorkeeper::OpenidConnect.configure.select_account_for_resource_owner missing configuration.'
23
23
  subject_not_configured: 'ID Token generation failed due to Doorkeeper::OpenidConnect.configure.subject missing configuration.'
24
24
  signing_key_not_configured: 'Doorkeeper::OpenidConnect.configure.signing_key must resolve to at least one key.'
25
+ issuer_not_configured: 'Doorkeeper::OpenidConnect.configure.issuer must resolve to a non-blank value.'
25
26
  dynamic_client_registration_unauthorized: 'Authorization required for client registration'
@@ -21,7 +21,7 @@ module Doorkeeper
21
21
  def body
22
22
  {
23
23
  state: pre_auth.state,
24
- id_token: id_token.as_jws_token
24
+ id_token: id_token.as_jws_token,
25
25
  }
26
26
  end
27
27
 
@@ -7,7 +7,7 @@ module Doorkeeper
7
7
  super.merge({
8
8
  access_token: auth.token.token,
9
9
  token_type: auth.token.token_type,
10
- expires_in: auth.token.expires_in_seconds
10
+ expires_in: auth.token.expires_in_seconds,
11
11
  })
12
12
  end
13
13
  end
@@ -31,7 +31,7 @@ module Doorkeeper
31
31
  name: name,
32
32
  response: response,
33
33
  scope: scope,
34
- generator: block
34
+ generator: block,
35
35
  )
36
36
  end
37
37
  alias claim normal_claim
@@ -5,7 +5,7 @@ module Doorkeeper
5
5
  def self.configure(&block)
6
6
  if Doorkeeper.configuration.orm != :active_record
7
7
  raise Errors::InvalidConfiguration,
8
- "Doorkeeper OpenID Connect currently only supports the ActiveRecord ORM adapter"
8
+ "Doorkeeper OpenID Connect currently only supports the ActiveRecord ORM adapter"
9
9
  end
10
10
 
11
11
  @config = Config::Builder.new(&block).build
@@ -176,7 +176,11 @@ module Doorkeeper
176
176
  end
177
177
 
178
178
  def oidc_prompt_values
179
- @oidc_prompt_values ||= params[:prompt].to_s.split(/ +/).uniq
179
+ # Reject blank entries so leading/duplicate spaces in the
180
+ # space-delimited `prompt` parameter don't surface as an empty
181
+ # value (which would otherwise be treated as an unknown prompt and
182
+ # rejected with `invalid_request`).
183
+ @oidc_prompt_values ||= params[:prompt].to_s.split(/ +/).reject(&:blank?).uniq
180
184
  end
181
185
 
182
186
  # Resolve auth_time for max_age enforcement.
@@ -26,7 +26,7 @@ module Doorkeeper
26
26
  exp: expiration,
27
27
  iat: issued_at,
28
28
  nonce: nonce,
29
- auth_time: auth_time
29
+ auth_time: auth_time,
30
30
  )
31
31
  end
32
32
 
@@ -36,9 +36,9 @@ module Doorkeeper
36
36
 
37
37
  def as_jws_token
38
38
  ::JWT.encode(as_json,
39
- Doorkeeper::OpenidConnect.signing_key.keypair,
40
- Doorkeeper::OpenidConnect.signing_algorithm.to_s,
41
- { typ: "JWT", kid: Doorkeeper::OpenidConnect.signing_key.kid }).to_s
39
+ Doorkeeper::OpenidConnect.signing_key.keypair,
40
+ Doorkeeper::OpenidConnect.signing_algorithm.to_s,
41
+ { typ: "JWT", kid: Doorkeeper::OpenidConnect.signing_key.kid }).to_s
42
42
  end
43
43
 
44
44
  private
@@ -46,13 +46,15 @@ module Doorkeeper
46
46
  def issuer
47
47
  Doorkeeper::OpenidConnect.resolve_issuer(
48
48
  resource_owner: @resource_owner,
49
- application: @access_token.application
49
+ application: @access_token.application,
50
50
  )
51
51
  end
52
52
 
53
53
  def subject
54
- Doorkeeper::OpenidConnect.configuration.subject.call(@resource_owner,
55
- @access_token.application).to_s
54
+ Doorkeeper::OpenidConnect.configuration.subject.call(
55
+ @resource_owner,
56
+ @access_token.application,
57
+ ).to_s
56
58
  end
57
59
 
58
60
  def audience
@@ -27,7 +27,7 @@ module Doorkeeper
27
27
  def create_openid_request(access_grant)
28
28
  ::Doorkeeper::OpenidConnect.configuration.open_id_request_model.create!(
29
29
  access_grant: access_grant,
30
- nonce: pre_auth.nonce
30
+ nonce: pre_auth.nonce,
31
31
  )
32
32
  end
33
33
  end
@@ -6,10 +6,10 @@ module Doorkeeper
6
6
  def self.prepended(base)
7
7
  base.class_eval do
8
8
  has_one :openid_request,
9
- class_name: Doorkeeper::OpenidConnect.configuration.open_id_request_class,
10
- foreign_key: "access_grant_id",
11
- inverse_of: :access_grant,
12
- dependent: :delete
9
+ class_name: Doorkeeper::OpenidConnect.configuration.open_id_request_class,
10
+ foreign_key: "access_grant_id",
11
+ inverse_of: :access_grant,
12
+ dependent: :delete
13
13
  end
14
14
  end
15
15
  end
@@ -11,7 +11,7 @@ module Doorkeeper
11
11
  module ActiveRecord
12
12
  module Mixins
13
13
  autoload :OpenidRequest,
14
- "doorkeeper/openid_connect/orm/active_record/mixins/openid_request"
14
+ "doorkeeper/openid_connect/orm/active_record/mixins/openid_request"
15
15
  end
16
16
 
17
17
  def run_hooks
@@ -30,7 +30,7 @@ module Doorkeeper
30
30
  if Doorkeeper.configuration.respond_to?(:active_record_options) && Doorkeeper.configuration.active_record_options[:establish_connection]
31
31
  [Doorkeeper::OpenidConnect.configuration.open_id_request_model].each do |c|
32
32
  c.send :establish_connection,
33
- Doorkeeper.configuration.active_record_options[:establish_connection]
33
+ Doorkeeper.configuration.active_record_options[:establish_connection]
34
34
  end
35
35
  end
36
36
  end
@@ -51,7 +51,7 @@ Doorkeeper.configuration.active_record_options[:establish_connection]
51
51
  if Doorkeeper.configuration.active_record_options[:establish_connection]
52
52
  [Doorkeeper::OpenidConnect.configuration.open_id_request_model].each do |c|
53
53
  c.send :establish_connection,
54
- Doorkeeper.configuration.active_record_options[:establish_connection]
54
+ Doorkeeper.configuration.active_record_options[:establish_connection]
55
55
  end
56
56
  end
57
57
  end
@@ -11,13 +11,13 @@ module Doorkeeper
11
11
  @controllers = {
12
12
  userinfo: "doorkeeper/openid_connect/userinfo",
13
13
  discovery: "doorkeeper/openid_connect/discovery",
14
- dynamic_client_registration: "doorkeeper/openid_connect/dynamic_client_registration"
14
+ dynamic_client_registration: "doorkeeper/openid_connect/dynamic_client_registration",
15
15
  }
16
16
 
17
17
  @as = {
18
18
  userinfo: :userinfo,
19
19
  discovery: :discovery,
20
- dynamic_client_registration: :dynamic_client_registration
20
+ dynamic_client_registration: :dynamic_client_registration,
21
21
  }
22
22
 
23
23
  @skips = []
@@ -26,7 +26,7 @@ module Doorkeeper
26
26
  def [](routes)
27
27
  {
28
28
  controllers: @controllers[routes],
29
- as: @as[routes]
29
+ as: @as[routes],
30
30
  }
31
31
  end
32
32
 
@@ -14,7 +14,7 @@ module Doorkeeper
14
14
  # the canonical subject identifier (which would defeat pairwise /
15
15
  # subject-type guarantees).
16
16
  ClaimsBuilder.generate(@access_token, :user_info).merge(
17
- sub: subject
17
+ sub: subject,
18
18
  )
19
19
  end
20
20
 
@@ -4,7 +4,7 @@ module Doorkeeper
4
4
  module OpenidConnect
5
5
  MAJOR = 1
6
6
  MINOR = 10
7
- TINY = 0
7
+ TINY = 1
8
8
  PRE = nil
9
9
 
10
10
  # Full version number
@@ -132,18 +132,29 @@ module Doorkeeper
132
132
  # @return [String] the issuer string
133
133
  def self.resolve_issuer(resource_owner: nil, application: nil, request: nil)
134
134
  issuer = configuration.issuer
135
- return issuer.to_s unless issuer.respond_to?(:call)
136
-
137
- case issuer.arity
138
- when 0
139
- issuer.call
140
- when 1
141
- issuer.call(request || resource_owner)
142
- when 2
143
- issuer.call(resource_owner, application)
144
- else
145
- issuer.call(resource_owner, application, request)
146
- end.to_s
135
+
136
+ value =
137
+ if issuer.respond_to?(:call)
138
+ case issuer.arity
139
+ when 0
140
+ issuer.call
141
+ when 1
142
+ issuer.call(request || resource_owner)
143
+ when 2
144
+ issuer.call(resource_owner, application)
145
+ else
146
+ issuer.call(resource_owner, application, request)
147
+ end
148
+ else
149
+ issuer
150
+ end.to_s
151
+
152
+ if value.blank?
153
+ raise Errors::InvalidConfiguration,
154
+ I18n.translate("doorkeeper.openid_connect.errors.messages.issuer_not_configured")
155
+ end
156
+
157
+ value
147
158
  end
148
159
 
149
160
  Doorkeeper::GrantFlow.register(
@@ -161,7 +172,7 @@ module Doorkeeper
161
172
  )
162
173
 
163
174
  Doorkeeper::GrantFlow.register_alias(
164
- "implicit_oidc", as: ["implicit", "id_token", "id_token token"]
175
+ "implicit_oidc", as: ["implicit", "id_token", "id_token token"],
165
176
  )
166
177
  end
167
178
  end
@@ -10,7 +10,7 @@ module Doorkeeper
10
10
  def install
11
11
  template "initializer.rb", "config/initializers/doorkeeper_openid_connect.rb"
12
12
  copy_file File.expand_path("../../../../config/locales/en.yml", __dir__),
13
- "config/locales/doorkeeper_openid_connect.en.yml"
13
+ "config/locales/doorkeeper_openid_connect.en.yml"
14
14
  route "use_doorkeeper_openid_connect"
15
15
  end
16
16
  end
@@ -13,7 +13,7 @@ module Doorkeeper
13
13
  migration_template(
14
14
  "migration.rb.erb",
15
15
  "db/migrate/create_doorkeeper_openid_connect_tables.rb",
16
- migration_version: migration_version
16
+ migration_version: migration_version,
17
17
  )
18
18
  end
19
19
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doorkeeper-openid_connect
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0
4
+ version: 1.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Dengler
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2026-06-01 00:00:00.000000000 Z
13
+ date: 2026-06-03 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: doorkeeper