Skip to content

fix: preserve admin targets after SSO denial#1542

Merged
superdav42 merged 1 commit into
mainfrom
fix/sso-admin-same-domain-redirect
Jun 27, 2026
Merged

fix: preserve admin targets after SSO denial#1542
superdav42 merged 1 commit into
mainfrom
fix/sso-admin-same-domain-redirect

Conversation

@superdav42

@superdav42 superdav42 commented Jun 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Preserve admin redirect_to targets when anonymous SSO grants are denied, so mapped-domain /wp-admin/ requests fall back to the login form instead of the site homepage.
  • Send invalid admin SSO verification responses to wp_login_url( redirect_to, true ) while keeping public anonymous SSO denials on their original return URL.
  • Avoid wrapping same-domain login redirects in an unnecessary SSO handoff when no cookie-less token is added.

Verification

  • Reproduced on the local multisite test site using a mapped/subdomain subsite: logged-out /wp-admin/ redirected through SSO denial and ended at the subsite homepage before the fix.
  • Verified patched flow: logged-out /wp-admin/ now redirects to wp-login.php?redirect_to=<mapped-subsite>/wp-admin/&reauth=1; login POST returns Location: <mapped-subsite>/wp-admin/.
  • vendor/bin/phpcs inc/sso/class-sso.php tests/WP_Ultimo/SSO/SSO_Test.php tests/WP_Ultimo/SSO/SSO_Coverage_Test.php
  • vendor/bin/phpunit --filter 'WP_Ultimo\\SSO\\SSO_Test|WP_Ultimo\\SSO\\SSO_Coverage_Test'

aidevops.sh v3.25.0 plugin for OpenCode v1.17.11 with gpt-5.5 spent 36m and 255,862 tokens on this with the user in an interactive session.

Summary by CodeRabbit

  • Bug Fixes

    • Improved SSO redirect handling so users are sent to the intended admin/login page when access is denied or verification fails.
    • Preserved destination URLs more reliably during login redirects, reducing unexpected returns to the home page.
    • Added safeguards to prevent redirect issues when response headers have already been sent.
  • Tests

    • Expanded coverage for admin redirect flows and denial handling.
    • Updated existing checks to match the current redirect behavior.

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

SSO redirects now preserve admin redirect_to values through anonymous denial and broker invalid-verify paths, conditionally append redirect_to in cookie-less login redirects, and update tests for the revised redirect construction.

Changes

SSO redirect target handling

Layer / File(s) Summary
Anonymous denial redirect preserves admin targets
inc/sso/class-sso.php, tests/WP_Ultimo/SSO/SSO_Coverage_Test.php, tests/WP_Ultimo/SSO/SSO_Test.php
The main-site denial redirect now builds query args from an array, carries validated admin targets into the broker URL, and the matching tests update assertions, cleanup, and redirect coverage.
Cookie-less login redirect keeps the requested target
inc/sso/class-sso.php, tests/WP_Ultimo/SSO/SSO_Test.php
handle_login_redirect() only adds redirect_to when the generated token URL changes, and the unit test checks the same-domain redirect target.
Broker invalid-verify fallback uses login URL
inc/sso/class-sso.php, tests/WP_Ultimo/SSO/SSO_Coverage_Test.php
The broker invalid-verify branch now guards the denial cookie with headers_sent() and rewrites admin targets to wp_login_url(), with coverage for the login fallback.

Sequence Diagram(s)

sequenceDiagram
  participant HS as handle_server()
  participant HB as handle_broker()
  participant HSent as headers_sent()
  participant WPL as wp_login_url()
  participant WPR as wp_redirect()
  HS->>WPR: redirect broker URL with sso_verify=invalid and redirect_to
  HB->>HSent: check before setcookie()
  HB->>WPL: build wp-login.php for admin redirect_to
  HB->>WPR: redirect to login URL
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

review-feedback-scanned, status:in-review

Poem

🐇 I hopped through redirects with care,
To admin doors and login stairs.
redirect_to stayed snug and bright,
While invalid flags blinked out of sight.
Thump-thump—SSO danced through the night.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: preserving admin targets after SSO denial.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sso-admin-same-domain-redirect

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

🔨 Build Complete - Ready for Testing!

📦 Download Build Artifact (Recommended)

Download the zip build, upload to WordPress and test:

🌐 Test in WordPress Playground (Very Experimental)

Click the link below to instantly test this PR in your browser - no installation needed!
Playground support for multisite is very limitied, hopefully it will get better in the future.

🚀 Launch in Playground

Login credentials: admin / password

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/WP_Ultimo/SSO/SSO_Coverage_Test.php`:
- Around line 2121-2124: The handle_server() redirect assertion in
SSO_Coverage_Test is too brittle because add_query_arg() encodes query values,
so matching a literal /wp-admin/ may fail even when the redirect target is
correct. Update the test to inspect the redirect_to parameter by parsing
$redirect_url and asserting the decoded redirect target contains /wp-admin/,
using the existing redirect_url assertions in this test method as the place to
adjust.
- Around line 2315-2318: The SSO redirect assertion is brittle because
`wp_login_url()` already encodes `redirect_to` before `add_query_arg()` builds
the final URL. Update the `handle_broker()` test expectation to inspect the
decoded login redirect target instead of matching the raw encoded string, and
keep the existing checks in `SSO_Coverage_Test` focused on the `wp-login.php`
redirect plus the decoded `redirect_to` value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5a6f36bb-baaf-4fff-8289-c1bf2d6de0ad

📥 Commits

Reviewing files that changed from the base of the PR and between acba35f and 5a788a5.

📒 Files selected for processing (3)
  • inc/sso/class-sso.php
  • tests/WP_Ultimo/SSO/SSO_Coverage_Test.php
  • tests/WP_Ultimo/SSO/SSO_Test.php

Comment on lines +2121 to +2124
$this->assertNotNull($redirect_url, 'handle_server() must redirect anonymous SSO grant requests back to the broker');
$this->assertStringContainsString('sso_verify=invalid', $redirect_url);
$this->assertStringContainsString('redirect_to=', $redirect_url);
$this->assertStringContainsString('/wp-admin/', $redirect_url);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Parse redirect_to instead of matching the encoded URL.

Line 2124 checks for a literal /wp-admin/, but add_query_arg() percent-encodes query values. This test can fail even when the redirect target is preserved correctly.

Suggested fix
 		$this->assertNotNull($redirect_url, 'handle_server() must redirect anonymous SSO grant requests back to the broker');
 		$this->assertStringContainsString('sso_verify=invalid', $redirect_url);
-		$this->assertStringContainsString('redirect_to=', $redirect_url);
-		$this->assertStringContainsString('/wp-admin/', $redirect_url);
+		$query = [];
+		parse_str((string) wp_parse_url($redirect_url, PHP_URL_QUERY), $query);
+
+		$this->assertArrayHasKey('redirect_to', $query);
+		$this->assertSame($redirect_to, $query['redirect_to']);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$this->assertNotNull($redirect_url, 'handle_server() must redirect anonymous SSO grant requests back to the broker');
$this->assertStringContainsString('sso_verify=invalid', $redirect_url);
$this->assertStringContainsString('redirect_to=', $redirect_url);
$this->assertStringContainsString('/wp-admin/', $redirect_url);
$this->assertNotNull($redirect_url, 'handle_server() must redirect anonymous SSO grant requests back to the broker');
$this->assertStringContainsString('sso_verify=invalid', $redirect_url);
$query = [];
parse_str((string) wp_parse_url($redirect_url, PHP_URL_QUERY), $query);
$this->assertArrayHasKey('redirect_to', $query);
$this->assertSame($redirect_to, $query['redirect_to']);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/WP_Ultimo/SSO/SSO_Coverage_Test.php` around lines 2121 - 2124, The
handle_server() redirect assertion in SSO_Coverage_Test is too brittle because
add_query_arg() encodes query values, so matching a literal /wp-admin/ may fail
even when the redirect target is correct. Update the test to inspect the
redirect_to parameter by parsing $redirect_url and asserting the decoded
redirect target contains /wp-admin/, using the existing redirect_url assertions
in this test method as the place to adjust.

Comment on lines +2315 to +2318
$this->assertNotNull($redirect_url, 'handle_broker() must redirect invalid admin SSO requests');
$this->assertStringContainsString('wp-login.php', $redirect_url);
$this->assertStringContainsString(rawurlencode($redirect_to), $redirect_url);
$this->assertStringNotContainsString($return_url . '?sso_verify=invalid', $redirect_url);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Decode the login redirect target before asserting it.

wp_login_url() encodes redirect_to before handing it to add_query_arg(), so Line 2317 is brittle against the actual URL shape and can fail on a correct redirect.

Suggested fix
 		$this->assertNotNull($redirect_url, 'handle_broker() must redirect invalid admin SSO requests');
 		$this->assertStringContainsString('wp-login.php', $redirect_url);
-		$this->assertStringContainsString(rawurlencode($redirect_to), $redirect_url);
+		$query = [];
+		parse_str((string) wp_parse_url($redirect_url, PHP_URL_QUERY), $query);
+
+		$this->assertArrayHasKey('redirect_to', $query);
+		$this->assertSame($redirect_to, urldecode((string) $query['redirect_to']));
 		$this->assertStringNotContainsString($return_url . '?sso_verify=invalid', $redirect_url);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$this->assertNotNull($redirect_url, 'handle_broker() must redirect invalid admin SSO requests');
$this->assertStringContainsString('wp-login.php', $redirect_url);
$this->assertStringContainsString(rawurlencode($redirect_to), $redirect_url);
$this->assertStringNotContainsString($return_url . '?sso_verify=invalid', $redirect_url);
$this->assertNotNull($redirect_url, 'handle_broker() must redirect invalid admin SSO requests');
$this->assertStringContainsString('wp-login.php', $redirect_url);
$query = [];
parse_str((string) wp_parse_url($redirect_url, PHP_URL_QUERY), $query);
$this->assertArrayHasKey('redirect_to', $query);
$this->assertSame($redirect_to, urldecode((string) $query['redirect_to']));
$this->assertStringNotContainsString($return_url . '?sso_verify=invalid', $redirect_url);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/WP_Ultimo/SSO/SSO_Coverage_Test.php` around lines 2315 - 2318, The SSO
redirect assertion is brittle because `wp_login_url()` already encodes
`redirect_to` before `add_query_arg()` builds the final URL. Update the
`handle_broker()` test expectation to inspect the decoded login redirect target
instead of matching the raw encoded string, and keep the existing checks in
`SSO_Coverage_Test` focused on the `wp-login.php` redirect plus the decoded
`redirect_to` value.

@superdav42 superdav42 merged commit 144c5b9 into main Jun 27, 2026
11 checks passed
@superdav42

Copy link
Copy Markdown
Collaborator Author

Summary

  • Preserve admin redirect_to targets when anonymous SSO grants are denied, so mapped-domain /wp-admin/ requests fall back to the login form instead of the site homepage.
  • Send invalid admin SSO verification responses to wp_login_url( redirect_to, true ) while keeping public anonymous SSO denials on their original return URL.
  • Avoid wrapping same-domain login redirects in an unnecessary SSO handoff when no cookie-less token is added.

Verification

  • Reproduced on the local multisite test site using a mapped/subdomain subsite: logged-out /wp-admin/ redirected through SSO denial and ended at the subsite homepage before the fix.
  • Verified patched flow: logged-out /wp-admin/ now redirects to wp-login.php?redirect_to=<mapped-subsite>/wp-admin/&reauth=1; login POST returns Location: <mapped-subsite>/wp-admin/.
  • vendor/bin/phpcs inc/sso/class-sso.php tests/WP_Ultimo/SSO/SSO_Test.php tests/WP_Ultimo/SSO/SSO_Coverage_Test.php
  • vendor/bin/phpunit --filter 'WP_Ultimo\\SSO\\SSO_Test|WP_Ultimo\\SSO\\SSO_Coverage_Test'

aidevops.sh v3.25.0 plugin for OpenCode v1.17.11 with gpt-5.5 spent 36m and 255,862 tokens on this with the user in an interactive session.


Merged via PR #1542 to main.
Merged by deterministic merge pass (pulse-wrapper.sh).

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

Labels

review-feedback-scanned Merged PR already scanned for quality feedback

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant