diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index c3fa47f3..bbba4a79 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -183,6 +183,15 @@ jobs: echo "✅ All checkout tests passed!" + - name: Run SSO Redirect Loop Tests (After Setup) + id: sso-tests + run: | + echo "=== Starting SSO Redirect Loop Tests ===" + npx cypress run \ + --config-file cypress.config.test.js \ + --spec "tests/e2e/cypress/integration/065-sso-redirect-loop.spec.js" \ + --browser ${{ matrix.browser }} + - name: Run Stripe Tests (After Setup) id: stripe-tests env: diff --git a/assets/js/sso.js b/assets/js/sso.js index 16c1d599..492b0b64 100644 --- a/assets/js/sso.js +++ b/assets/js/sso.js @@ -1,46 +1,27 @@ -/* global wu_create_cookie, wu_sso_config, wu_read_cookie, detectIncognito */ +/* global wu_create_cookie, wu_sso_config, wu_read_cookie */ (function(o) { - window.wu = window.wu || {}; + window.wu = window.wu || {}; - window.is_incognito = false; + window.wu.sso_denied = function() { - window.wu.sso_denied = function() { + wu_create_cookie('wu_sso_denied', 1, o.expiration_in_days); - wu_create_cookie('wu_sso_denied', 1, o.expiration_in_days); + }; - }; + const w = document.createElement('script'); - window.wu.check_for_incognito_window = function() { + w.type = 'text/javascript'; - try { + w.async = true; - detectIncognito(results => window.is_incognito = results.isPrivate); + w.defer = true; - } catch (e) { + w.src = o.server_url + '?_jsonp=1'; - // If detectIncognito fails, assume not incognito - window.is_incognito = false; + const denied = wu_read_cookie('wu_sso_denied'); - } - - } - - window.wu.check_for_incognito_window(); - - const w = document.createElement('script'); - - w.type = 'text/javascript'; - - w.async = true; - - w.defer = true; - - w.src = o.server_url + '?_jsonp=1'; - - const denied = wu_read_cookie('wu_sso_denied'); - - document.head.insertAdjacentHTML('beforeend', ` + document.head.insertAdjacentHTML('beforeend', ` `); - if (! o.is_user_logged_in && ! denied) { + if (! o.is_user_logged_in && ! denied) { - const s = document.getElementsByTagName('script')[0]; + const s = document.getElementsByTagName('script')[ 0 ]; - s.parentNode.insertBefore(w, s); + s.parentNode.insertBefore(w, s); - document.body.insertAdjacentHTML('beforeend', '
 
'); + document.body.insertAdjacentHTML('beforeend', '
 
'); - } + } - window.wu.sso = function(payload) { + window.wu.sso = function(payload) { - const encodedLocation = encodeURIComponent(window.location.href); + const encodedLocation = encodeURIComponent(window.location.href); - if (payload.code === 200) { + if (payload.code === 200) { - if (o.use_overlay) { + if (o.use_overlay) { - document.body.classList.add('sso-loading'); + document.body.classList.add('sso-loading'); - } - - /** - * In case we're dealing with http (without ssl), - * we force a redirect to bypass browser cookie - * limitations. - * - * Otherwise, on the else block, - * we redirect with the verification code attached, - * to perform a regular SSO flow. - */ - if (payload.verify === 'must-redirect') { - - window.location.replace(`${ o.server_url }?return_url=${ encodedLocation }`); - - } else { - window.location.replace(`${ o.server_url }?sso_verify=${ payload.verify }&return_url=${ encodedLocation }`); - } - - } else { + } - /** - * If we are in a incognito window, - * we give it another try with a full redirect, - * as chrome settings might be blocking - * cookies from being sent anyways. - */ - if (window.is_incognito) { + /** + * In case we're dealing with http (without ssl), + * we force a redirect to bypass browser cookie + * limitations. + * + * Otherwise, on the else block, + * we redirect with the verification code attached, + * to perform a regular SSO flow. + */ + if (payload.verify === 'must-redirect') { - if (o.use_overlay) { + window.location.replace(`${ o.server_url }?return_url=${ encodedLocation }`); - document.body.classList.add('sso-loading'); + } else { + window.location.replace(`${ o.server_url }?sso_verify=${ payload.verify }&return_url=${ encodedLocation }`); + } - } - - window.location.replace(`${o.server_url}?return_url=${ encodedLocation }`); - - return; - - } + } else { - window.wu.sso_denied(); + /** + * SSO failed (user not logged in on main site). + * Set the denied cookie so we don't try again for 5 minutes, + * and remove the loading overlay. + * + * Previously, incognito windows would attempt a full redirect + * as a fallback, but this caused an infinite redirect loop: + * redirect -> sso_verify=invalid -> redirect -> repeat. + * The denied cookie now prevents re-entry in all cases. + */ + window.wu.sso_denied(); - document.body.classList.remove('sso-loading'); + document.body.classList.remove('sso-loading'); - } + } - }; + }; - (function clean_up_query_args() { + (function clean_up_query_args() { - if (window.history.replaceState) { + if (window.history.replaceState) { - window.history.replaceState(null, null, o.filtered_url + window.location.hash); + window.history.replaceState(null, null, o.filtered_url + window.location.hash); - } + } - }()); + }()); }(wu_sso_config)); diff --git a/assets/js/sso.min.js b/assets/js/sso.min.js index f096812a..52070300 100644 --- a/assets/js/sso.min.js +++ b/assets/js/sso.min.js @@ -1,4 +1,4 @@ -(n=>{window.wu=window.wu||{},window.is_incognito=!1,window.wu.sso_denied=function(){wu_create_cookie("wu_sso_denied",1,n.expiration_in_days)},window.wu.check_for_incognito_window=function(){try{detectIncognito(o=>window.is_incognito=o.isPrivate)}catch(o){window.is_incognito=!1}},window.wu.check_for_incognito_window();var o=document.createElement("script"),e=(o.type="text/javascript",o.async=!0,o.defer=!0,o.src=n.server_url+"?_jsonp=1",wu_read_cookie("wu_sso_denied"));document.head.insertAdjacentHTML("beforeend",` +(n=>{window.wu=window.wu||{},window.wu.sso_denied=function(){wu_create_cookie("wu_sso_denied",1,n.expiration_in_days)};var e=document.createElement("script"),o=(e.type="text/javascript",e.async=!0,e.defer=!0,e.src=n.server_url+"?_jsonp=1",wu_read_cookie("wu_sso_denied"));document.head.insertAdjacentHTML("beforeend",` - `),n.is_user_logged_in||e||((e=document.getElementsByTagName("script")[0]).parentNode.insertBefore(o,e),document.body.insertAdjacentHTML("beforeend",'
 
')),window.wu.sso=function(o){var e=encodeURIComponent(window.location.href);200===o.code?(n.use_overlay&&document.body.classList.add("sso-loading"),"must-redirect"===o.verify?window.location.replace(n.server_url+"?return_url="+e):window.location.replace(`${n.server_url}?sso_verify=${o.verify}&return_url=`+e)):window.is_incognito?(n.use_overlay&&document.body.classList.add("sso-loading"),window.location.replace(n.server_url+"?return_url="+e)):(window.wu.sso_denied(),document.body.classList.remove("sso-loading"))},window.history.replaceState&&window.history.replaceState(null,null,n.filtered_url+window.location.hash)})(wu_sso_config); \ No newline at end of file + `),n.is_user_logged_in||o||((o=document.getElementsByTagName("script")[0]).parentNode.insertBefore(e,o),document.body.insertAdjacentHTML("beforeend",'
 
')),window.wu.sso=function(e){var o=encodeURIComponent(window.location.href);200===e.code?(n.use_overlay&&document.body.classList.add("sso-loading"),"must-redirect"===e.verify?window.location.replace(n.server_url+"?return_url="+o):window.location.replace(`${n.server_url}?sso_verify=${e.verify}&return_url=`+o)):(window.wu.sso_denied(),document.body.classList.remove("sso-loading"))},window.history.replaceState&&window.history.replaceState(null,null,n.filtered_url+window.location.hash)})(wu_sso_config); \ No newline at end of file diff --git a/inc/sso/class-sso.php b/inc/sso/class-sso.php index 333c4ea6..33acb98b 100644 --- a/inc/sso/class-sso.php +++ b/inc/sso/class-sso.php @@ -543,22 +543,41 @@ public function handle_broker($response_type = 'redirect'): void { // Attach through redirect if the client isn't attached yet. if ( ! $broker->isAttached()) { - $return_url = $this->get_current_url(); - - if ( 'jsonp' === $response_type) { - $attach_url = $broker->getAttachUrl( - [ - '_jsonp' => '1', - ] - ); - } else { - $attach_url = $broker->getAttachUrl( - [ - 'return_url' => $return_url, - ] + /* + * For JSONP requests (initiated by a