diff --git a/assets/js/sso.js b/assets/js/sso.js index 492b0b648..14a2fb6b5 100644 --- a/assets/js/sso.js +++ b/assets/js/sso.js @@ -21,6 +21,11 @@ const denied = wu_read_cookie('wu_sso_denied'); + const checkout_form = document.querySelector( + '#wrapper-field-checkout, .wu-checkout, .wu-checkout-form' + ); + const checkout_url = /\/register\/?$/.test(window.location.pathname); + document.head.insertAdjacentHTML('beforeend', ` `); - if (! o.is_user_logged_in && ! denied) { + if (! o.is_user_logged_in && ! denied && ! checkout_form && ! checkout_url) { const s = document.getElementsByTagName('script')[ 0 ]; diff --git a/inc/sso/class-sso.php b/inc/sso/class-sso.php index 63ec842ce..cbbff0894 100644 --- a/inc/sso/class-sso.php +++ b/inc/sso/class-sso.php @@ -977,13 +977,87 @@ public function handle_broker($response_type = 'redirect'): void { // Attach through redirect if the client isn't attached yet. if ( ! $broker->isAttached()) { + $sso_path = $this->get_url_path(); + + /* + * Determine the page we ultimately want the user to land on + * after the magic-link round-trip. + * + * The JSONP must-redirect fallback navigates the top-level + * browser to `/sso?return_url=`, so on + * that second hit `get_current_url()` is the /sso URL itself + * — using it would feed `/sso?...` back into return_url and + * the main site would echo a longer `/sso?return_url=/sso?...` + * URL on each iteration, producing a `ERR_TOO_MANY_REDIRECTS` + * (the URL grows on every hop, never converges). + * + * Prefer the explicit `return_url` query param when present + * (set by sso.js on the must-redirect branch); only fall back + * to `get_current_url()` on a direct first hit. Guard the + * value against pointing at the /sso path itself so a + * stray/legacy URL can't restart the same loop. + */ + $requested_return = (string) $this->input('return_url', ''); + + if ( '' === $requested_return ) { + $return_url = $this->get_current_url(); + } else { + $return_url = $requested_return; + } + + $return_path = (string) wp_parse_url($return_url, PHP_URL_PATH); + + if ( '' === $return_path || preg_match("#/{$sso_path}(-grant)?/?$#", $return_path) ) { + // Refuses to round-trip the /sso endpoint itself; fall back to subsite home. + $return_url = home_url('/'); + } + + /* + * Front-end auto-SSO target: send the user to the main site's + * /sso endpoint with a return_url + redirect_to. handle_server() + * on the main site checks the main-site auth cookie (first-party + * at that point — no third-party cookie restrictions apply) and, + * if logged in, issues an HMAC-signed `wu_sso_token` magic-link + * back to this subsite. handle_cookie_less_sso_token() consumes + * it on init and sets the broker-side auth cookie. + * + * The legacy `$broker->getAttachUrl()` returned a /sso-grant URL + * whose handler (`wu_sso_handle_sso_grant_grant`) is unreachable + * under the cookie-less rework introduced in #1084, so the old + * attach handshake silently fell through to a WordPress 404 and + * SSO never completed. Using /sso directly hits the cookie-less + * server path and works in any browser that allows the main-site + * first-party auth cookie. + * + * Pass `redirect_to` explicitly so main's `get_sso_redirect_to()` + * does not append `/wp/wp-admin/` (its fallback when only a + * cross-domain return_url is supplied), which would land the + * user on the subsite admin instead of the page they were + * actually viewing. + */ + $main_sso_url = add_query_arg( + [ + $sso_path => 'login', + 'return_url' => $return_url, + 'redirect_to' => $return_url, + ], + get_home_url(wu_get_main_site_id(), $sso_path) + ); + /* - * For JSONP requests (initiated by a