From 6c4be7199cb640440f76e8e633e233e962c52596 Mon Sep 17 00:00:00 2001 From: Dan Milne Date: Sun, 19 Jul 2026 14:42:39 +1000 Subject: [PATCH] Extract OidcScopes::SUPPORTED as the shared scope list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The supported-scope list lived on OidcController, so the device authorization controller (and a test) had to reach across into OidcController::SUPPORTED_SCOPES. Move it to a shared OidcScopes::SUPPORTED module — one source of truth for the OIDC controller, the device flow, and consent handling — and update every reference. No behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_016Q4ATZHoMCWqvSpE2yYoie --- app/controllers/device_authorizations_controller.rb | 2 +- app/controllers/oidc_controller.rb | 9 ++++----- app/models/oidc_scopes.rb | 7 +++++++ test/helpers/oidc_helper_test.rb | 2 +- 4 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 app/models/oidc_scopes.rb diff --git a/app/controllers/device_authorizations_controller.rb b/app/controllers/device_authorizations_controller.rb index f4bfbf4..098c01d 100644 --- a/app/controllers/device_authorizations_controller.rb +++ b/app/controllers/device_authorizations_controller.rb @@ -80,7 +80,7 @@ class DeviceAuthorizationsController < ApplicationController end def granted_scopes(device_code) - device_code.scope.to_s.split & OidcController::SUPPORTED_SCOPES + device_code.scope.to_s.split & OidcScopes::SUPPORTED end def record_consent(device_code, user) diff --git a/app/controllers/oidc_controller.rb b/app/controllers/oidc_controller.rb index eae63f6..9929282 100644 --- a/app/controllers/oidc_controller.rb +++ b/app/controllers/oidc_controller.rb @@ -1,5 +1,4 @@ class OidcController < ApplicationController - SUPPORTED_SCOPES = %w[openid profile email groups offline_access].freeze # Grant types this authorization server supports. Single source of truth: # advertised in discovery (grant_types_supported), accepted at dynamic client @@ -69,7 +68,7 @@ class OidcController < ApplicationController grant_types_supported: SUPPORTED_GRANT_TYPES, subject_types_supported: ["pairwise"], id_token_signing_alg_values_supported: ["RS256"], - scopes_supported: SUPPORTED_SCOPES, + scopes_supported: OidcScopes::SUPPORTED, token_endpoint_auth_methods_supported: ["client_secret_post", "client_secret_basic"], claims_supported: [ "sub", # Always included @@ -127,7 +126,7 @@ class OidcController < ApplicationController end # Only accept scopes we support (mirrors the authorize endpoint). - requested_scope = (params[:scope].to_s.split & SUPPORTED_SCOPES).join(" ") + requested_scope = (params[:scope].to_s.split & OidcScopes::SUPPORTED).join(" ") requested_scope = "openid" if requested_scope.blank? # PKCE is optional but recommended for device flow (RFC 8628 §5.5). If the @@ -240,7 +239,7 @@ class OidcController < ApplicationController # Normalize requested scopes to the set we support. Needed here so claims # validation below can check claim→scope coverage against what will actually # be granted. - requested_scopes = scope.split(" ") & SUPPORTED_SCOPES + requested_scopes = scope.split(" ") & OidcScopes::SUPPORTED scope = requested_scopes.join(" ") # Parse claims parameter (JSON string) for OIDC claims request @@ -490,7 +489,7 @@ class OidcController < ApplicationController user = Current.session.user - requested_scopes = oauth_params["scope"].split(" ") & SUPPORTED_SCOPES + requested_scopes = oauth_params["scope"].split(" ") & OidcScopes::SUPPORTED parsed_claims = begin JSON.parse(oauth_params["claims_requests"]) rescue diff --git a/app/models/oidc_scopes.rb b/app/models/oidc_scopes.rb new file mode 100644 index 0000000..e612ef0 --- /dev/null +++ b/app/models/oidc_scopes.rb @@ -0,0 +1,7 @@ +# Single source of truth for the OAuth/OIDC scopes this IdP supports. Shared by +# the OIDC controller (discovery + authorize/consent), the device authorization +# flow, and consent handling, so no controller has to reach into another for the +# list. +module OidcScopes + SUPPORTED = %w[openid profile email groups offline_access].freeze +end diff --git a/test/helpers/oidc_helper_test.rb b/test/helpers/oidc_helper_test.rb index 30fac39..6a9bc63 100644 --- a/test/helpers/oidc_helper_test.rb +++ b/test/helpers/oidc_helper_test.rb @@ -12,7 +12,7 @@ class OidcHelperTest < ActionView::TestCase test "scope_description covers every SUPPORTED_SCOPE (so the consent screens can't silently drop one)" do user = User.new(email_address: "person@example.com") - OidcController::SUPPORTED_SCOPES.each do |scope| + OidcScopes::SUPPORTED.each do |scope| assert_not_equal scope, scope_description(scope, user: user), "#{scope} has no description and would render as its raw name" end