Skip to content

Follow the host's default_namespace after SSO sign-in - #19

Closed
Fivell wants to merge 3 commits into
mainfrom
fix/sso-redirect-respects-default-namespace
Closed

Fivell wants to merge 3 commits into
mainfrom
fix/sso-redirect-respects-default-namespace

Conversation

@Fivell

@Fivell Fivell commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

The bug

OmniauthCallbacksController#after_sign_in_path_for fell back to a hardcoded '/admin':

stored_location_for(resource) || '/admin'

ActiveAdmin does not always mount at /admin. A host that sets config.default_namespace = false mounts it at /. On those hosts the post-SSO redirect landed on a path that does not exist.

It presented as intermittent, which is what made it hard to place: the redirect is correct whenever stored_location_for is set — i.e. when the user hit a protected page while logged out and Devise's failure app stored admin_user_return_to in the session cookie. It 404s when the sign-in started at the login page itself, or when that cookie was rotated or expired during the IdP round-trip.

Observed in production on a host with ActiveAdmin at / and Zitadel as the IdP:

/              302 -> /login
/login         200
/admin         404     <- where SSO dropped you

Why nothing caught it

All three existing dummy apps (spec/dummy, spec/dummy_engine, spec/dummy_isolated) mount ActiveAdmin at /admin, so the hardcoded fallback happened to be right in every one of them.

Commits

Written red-first, so the reproduction is reviewable on its own.

1. 4c09f08 — reproduce (CI red)

  • spec/dummy_root/ — spec/dummy/ with one config line flipped: config.default_namespace = false. No host root route, so anything the gem redirects to outside the ActiveAdmin route table 404s.
  • spec/root/ — drives the full OmniAuth handshake against it.
  • rake spec:root, added to spec:all.
  • CI runs one step per dummy app instead of a single combined spec:all, so a failure names the host shape that broke.

CI on this commit:

✓ Default suite (spec/dummy — ActiveAdmin at /admin)     149 examples, 0 failures
✓ Engine-mounted Devise suite (spec/dummy_engine)          7 examples, 0 failures
✓ Isolated-engine suite (spec/dummy_isolated)              5 examples, 0 failures
X Root-mounted suite (spec/dummy_root — ActiveAdmin at /)  4 examples, 2 failures

  expected: "/"
       got: "/admin"
  ActionController::RoutingError: No route matches [GET] "/admin"

The two examples that pass pin the harness down: the dummy really has no /admin, and the stored-location path (the reason this looks intermittent in production) already worked.

2. 301a913 — pin json < 3

Unrelated to this branch, but it masked the suite. json 3.0.2 removed the quirks_mode keyword that ActiveSupport::JSON.decode/encode still passes to JSON.parse / JSON.generate (activesupport 7.2 and 8.0, lib/active_support/json/{decoding,encoding}.rb). CI resolves without a lockfile, so it picked json 3.x up as soon as it was released and every request spec died with ArgumentError: unknown keyword: quirks_mode. main last went green on 2026-09-03, before that release.

3. 216d5fb — fix (CI green)

-stored_location_for(resource) || '/admin'
+stored_location_for(resource) || root_path

root_path comes from ActiveAdmin::Devise::Controller and resolves the namespace root from ActiveAdmin.application.default_namespace — the same helper ActiveAdmin uses for its own logout redirect. /admin remains the answer for default hosts; hosts mounted at / now get /.

All four suites green across the full matrix (Ruby 3.2/3.3/3.4 × ActiveAdmin 3.5/4.0).

@Fivell
Fivell force-pushed the fix/sso-redirect-respects-default-namespace branch from a179648 to cf05737 Compare September 30, 2026 10:07
@Fivell Fivell changed the title Follow the host's default_namespace after SSO sign-in Reproduce: post-SSO 404 on hosts that mount ActiveAdmin at / Sep 30, 2026
Every dummy app in the suite mounts ActiveAdmin at /admin, so nothing
covers hosts that set `config.default_namespace = false` and mount it
at /. On those hosts the post-SSO redirect lands on /admin, which does
not exist.

spec/dummy_root/ is spec/dummy/ with that one config line flipped.
spec/root/ drives a full OmniAuth handshake against it and asserts the
landing page. CI runs each dummy app as its own step so the failing
host shape is named rather than buried in a combined run.

Red on purpose: `rake spec:root` fails with

  expected: "/"
       got: "/admin"
  ActionController::RoutingError: No route matches [GET] "/admin"

The two specs that do pass pin the harness down — the dummy really has
no /admin, and the stored-location path (the reason the bug looks
intermittent in production) already works.
json 3.0.2 removed the `quirks_mode` keyword that
ActiveSupport::JSON.decode/encode still passes to JSON.parse and
JSON.generate. CI resolves without a lockfile, so it picked up json 3.x
as soon as it was released and every request spec now fails with
`ArgumentError: unknown keyword: quirks_mode` — unrelated to anything
in this branch, and it masks the suite it is supposed to run.
@Fivell
Fivell force-pushed the fix/sso-redirect-respects-default-namespace branch from 43a4a38 to 301a913 Compare September 30, 2026 10:12
`after_sign_in_path_for` fell back to a hardcoded '/admin' whenever
Devise had no stored location. Resolve the landing page through
ActiveAdmin::Devise::Controller#root_path instead, which reads
`ActiveAdmin.application.default_namespace` — the same helper
ActiveAdmin uses for its own logout redirect. `/admin` stays the answer
for default hosts; hosts mounted at / now get /.

Turns spec/root/ green.
@Fivell Fivell changed the title Reproduce: post-SSO 404 on hosts that mount ActiveAdmin at / Follow the host's default_namespace after SSO sign-in Sep 30, 2026
@Fivell

Fivell commented Sep 30, 2026

Copy link
Copy Markdown
Member Author

close if favour of #17

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant