Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/views/active_admin/devise/sessions/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</h2>

<%= 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',
Expand All @@ -15,7 +15,7 @@
<div id="login">
<h2><%= active_admin_application.site_title(self) %></h2>

<%= form_tag "/admin/auth/oidc",
<%= form_tag "#{OmniAuth.config.path_prefix}/oidc",
method: :post,
class: "activeadmin-oidc-login-form formtastic",
data: { turbo: false } do %>
Expand Down
56 changes: 56 additions & 0 deletions spec/requests/login_path_helper_spec.rb
Original file line number Diff line number Diff line change
@@ -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 `<sentinel>/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
Loading