diff --git a/inc/sso/class-sso.php b/inc/sso/class-sso.php index ac2f10b0..1b483c61 100644 --- a/inc/sso/class-sso.php +++ b/inc/sso/class-sso.php @@ -246,6 +246,16 @@ public function startup(): void { add_filter('allowed_http_origins', [$this, 'add_additional_origins']); + /* + * Domain_Mapping registers the network-owned redirect-host allow-list used + * by wp_safe_redirect(). SSO grant requests can run in contexts where domain + * mapping has not otherwise been instantiated, so load the singleton here + * instead of duplicating its redirect-host logic. + */ + if (class_exists('\WP_Ultimo\Domain_Mapping')) { + \WP_Ultimo\Domain_Mapping::get_instance(); + } + /** * Authorize a user via a bearer, and converts it into a regular cookie * authenticated user diff --git a/tests/WP_Ultimo/SSO/SSO_Coverage_Test.php b/tests/WP_Ultimo/SSO/SSO_Coverage_Test.php index 255d7d4b..9cf8821d 100644 --- a/tests/WP_Ultimo/SSO/SSO_Coverage_Test.php +++ b/tests/WP_Ultimo/SSO/SSO_Coverage_Test.php @@ -2214,6 +2214,43 @@ function ($location) use (&$redirect_url) { $this->assertStringContainsString('wu_sso_token=', $redirect_url); } + /** + * Test SSO allows safe redirects back to mapped broker domains. + */ + public function test_startup_loads_domain_mapping_redirect_host_filter(): void { + $sso = SSO::get_instance(); + $domain_name = 'sso-redirect-mapped.example.com'; + $domain = wu_create_domain( + [ + 'blog_id' => 1, + 'domain' => $domain_name, + 'active' => true, + 'primary_domain' => false, + 'secure' => false, + 'stage' => \WP_Ultimo\Database\Domains\Domain_Stage::DONE, + ] + ); + + if (is_wp_error($domain) || ! $domain instanceof \WP_Ultimo\Models\Domain) { + $this->markTestSkipped('Could not create mapped domain fixture.'); + } + + wp_cache_delete('domain:' . $domain_name, 'domain_mappings'); + wp_cache_delete('domain:www.' . $domain_name, 'domain_mappings'); + + try { + $sso->startup(); + + $result = apply_filters('allowed_redirect_hosts', ['mygratis.site'], strtoupper($domain_name)); + + $this->assertContains($domain_name, $result); + } finally { + $domain->delete(); + wp_cache_delete('domain:' . $domain_name, 'domain_mappings'); + wp_cache_delete('domain:www.' . $domain_name, 'domain_mappings'); + } + } + // ------------------------------------------------------------------ // handle_broker — verify code paths (lines 519-590) // ------------------------------------------------------------------