diff --git a/app/views/active_admin/devise/sessions/new.html.erb b/app/views/active_admin/devise/sessions/new.html.erb index efe7aa0..700d875 100644 --- a/app/views/active_admin/devise/sessions/new.html.erb +++ b/app/views/active_admin/devise/sessions/new.html.erb @@ -5,7 +5,7 @@ <%= button_to ActiveAdmin::Oidc.config.login_button_label, - "/admin/auth/oidc", + "#{OmniAuth.config.path_prefix}/oidc", method: :post, class: "activeadmin-oidc-login-button w-full", form_class: 'formtastic', @@ -15,7 +15,7 @@

<%= active_admin_application.site_title(self) %>

- <%= form_tag "/admin/auth/oidc", + <%= form_tag "#{OmniAuth.config.path_prefix}/oidc", method: :post, class: "activeadmin-oidc-login-form formtastic", data: { turbo: false } do %> diff --git a/spec/requests/login_path_helper_spec.rb b/spec/requests/login_path_helper_spec.rb new file mode 100644 index 0000000..09e5d35 --- /dev/null +++ b/spec/requests/login_path_helper_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require "rails_helper" + +# Regression spec for MEDIUM #4 — login view must derive the OmniAuth +# callback path from `OmniAuth.config.path_prefix`, not hardcode it. +# +# `app/views/active_admin/devise/sessions/new.html.erb` used to hardcode +# `"/admin/auth/oidc"` in the form action. Hosts that customise +# `Devise.omniauth_path_prefix` (mount Devise at a non-`/admin` path, +# or use a different sub-prefix for SSO) ended up with a button POSTing +# to a dead URL — the gem's strategy is registered at the configured +# prefix, not the hardcoded one. +# +# We can't use Devise's `omniauth_authorize_path` helper here because +# the OmniAuth middleware lives at the Rack level (global path prefix), +# while Devise route helpers resolve through the engine and get +# re-prefixed by the engine mount — producing e.g. `/admin/admin/auth/oidc` +# when Devise is engine-mounted. `OmniAuth.config.path_prefix` is the +# single source of truth for where the middleware actually listens. +RSpec.describe "Login view OmniAuth path", type: :request do + before do + ActiveAdmin::Oidc.configure do |c| + c.issuer = "https://idp.example.com" + c.client_id = "client-abc" + c.on_login = ->(*) { true } + end + + # Force routes to load NOW. Otherwise Rails 8 lazy loading defers + # `devise_for` until the first request — at which point the stub + # below is active, Devise's "OmniAuth.config.path_prefix matches + # Devise.omniauth_path_prefix" guard sees the sentinel, and raises. + # `execute_unless_loaded` is Rails 8+; fall back for 7.x. + reloader = Rails.application.routes_reloader + if reloader.respond_to?(:execute_unless_loaded) + reloader.execute_unless_loaded + else + Rails.application.reload_routes! + end + end + + it "renders the form action from OmniAuth.config.path_prefix (no hardcoded literal)" do + # Stub the OmniAuth path prefix to a sentinel value the hardcoded + # string could never match. If the view actually reads the prefix, + # the rendered form action will be `/oidc`; if it + # hardcodes the path, the literal "/admin/auth/oidc" stays. + sentinel = "/sentinel-omniauth-prefix" + allow(OmniAuth.config).to receive(:path_prefix).and_return(sentinel) + + get "/admin/login" + + expect(response.body).to include(%(action="#{sentinel}/oidc")), + "form action ignores Devise.omniauth_path_prefix — hosts that " \ + "customise the prefix get a button POSTing to a dead URL" + end +end