Improve session polling to prevent accidental redirects

Closes #33071

Signed-off-by: Jon Koops <jonkoops@gmail.com>
(cherry picked from commit 687223f3b17f1804ddea1b74cacdc6b3564b73e6)
This commit is contained in:
Jon Koops 2024-11-20 13:06:41 +01:00 committed by Marek Posolda
parent 73ed0613ee
commit 7acb30269b
10 changed files with 48 additions and 32 deletions

View File

@ -91,7 +91,7 @@
<#if recaptchaRequired?? && !(recaptchaVisible!false)>
<script>
function onSubmitRecaptcha(token) {
document.getElementById("kc-register-form").submit();
document.getElementById("kc-register-form").requestSubmit();
}
</script>
<div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">

View File

@ -1,18 +1,23 @@
const CHECK_INTERVAL_MILLISECS = 2000;
const SESSION_POLLING_INTERVAL = 2000;
const initialSession = getSession();
const forms = Array.from(document.forms);
let timeout;
// Remove the timeout when unloading to avoid execution of the
// checkCookiesAndSetTimer when the page is already submitted
addEventListener("beforeunload", () => {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
});
// Stop polling for a session when a form is submitted to prevent unexpected redirects.
// This is required as Safari does not support the 'beforeunload' event properly.
// See: https://bugs.webkit.org/show_bug.cgi?id=219102
forms.forEach((form) =>
form.addEventListener("submit", () => stopSessionPolling()),
);
export function checkCookiesAndSetTimer(loginRestartUrl) {
// Stop polling for a session when the page is unloaded to prevent unexpected redirects.
globalThis.addEventListener("beforeunload", () => stopSessionPolling());
/**
* Starts polling to check if a new session was started in another context (e.g. a tab or window), and redirects to the specified URL if a session is detected.
* @param {string} redirectUrl - The URL to redirect to if a new session is detected.
*/
export function startSessionPolling(redirectUrl) {
if (initialSession) {
// We started with a session, so there is nothing to do, exit.
return;
@ -21,14 +26,25 @@ export function checkCookiesAndSetTimer(loginRestartUrl) {
const session = getSession();
if (!session) {
// The session is not present, check again later.
// No new session detected, check again later.
timeout = setTimeout(
() => checkCookiesAndSetTimer(loginRestartUrl),
CHECK_INTERVAL_MILLISECS,
() => startSessionPolling(redirectUrl),
SESSION_POLLING_INTERVAL,
);
} else {
// Redirect to the login restart URL. This can typically automatically login user due the SSO
location.href = loginRestartUrl;
// A new session was detected, redirect to the specified URL and stop polling.
location.href = redirectUrl;
stopSessionPolling();
}
}
/**
* Stops polling the session.
*/
function stopSessionPolling() {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
}

View File

@ -73,10 +73,10 @@ export function returnSuccess(result) {
if (result.response.userHandle) {
document.getElementById("userHandle").value = base64url.stringify(new Uint8Array(result.response.userHandle), { pad: false });
}
document.getElementById("webauth").submit();
document.getElementById("webauth").requestSubmit();
}
export function returnFailure(err) {
document.getElementById("error").value = err;
document.getElementById("webauth").submit();
}
document.getElementById("webauth").requestSubmit();
}

View File

@ -131,10 +131,10 @@ function returnSuccess(result, initLabel, initLabelPrompt) {
}
document.getElementById("authenticatorLabel").value = labelResult;
document.getElementById("register").submit();
document.getElementById("register").requestSubmit();
}
function returnFailure(err) {
document.getElementById("error").value = err;
document.getElementById("register").submit();
document.getElementById("register").requestSubmit();
}

View File

@ -3,7 +3,7 @@
<#if section = "header">
${msg("saml.post-form.title")}
<#elseif section = "form">
<script>window.onload = function() {document.forms[0].submit()};</script>
<script>window.onload = function() {document.forms[0].requestSubmit()};</script>
<p>${msg("saml.post-form.message")}</p>
<form name="saml-post-binding" method="post" action="${samlPost.url}">
<#if samlPost.SAMLRequest??>

View File

@ -9,7 +9,7 @@
<#list user.organizations as organization>
<li>
<a id="organization-${organization.alias}" class="${properties.kcFormSocialAccountListButtonClass!} <#if user.organizations?size gt 3>${properties.kcFormSocialAccountGridItem!}</#if>"
type="button" onclick="document.forms[0]['kc.org'].value = '${organization.alias}'; document.forms[0].submit()">
type="button" onclick="document.forms[0]['kc.org'].value = '${organization.alias}'; document.forms[0].requestSubmit()">
<span class="${properties.kcFormSocialAccountNameClass!}">${organization.name!}</span>
</a>
</li>

View File

@ -44,9 +44,9 @@
</#list>
</#if>
<script type="module">
import { checkCookiesAndSetTimer } from "${url.resourcesPath}/js/authChecker.js";
import { startSessionPolling } from "${url.resourcesPath}/js/authChecker.js";
checkCookiesAndSetTimer(
startSessionPolling(
"${url.ssoLoginInOtherTabsUrl?no_esc}"
);
</script>
@ -148,7 +148,7 @@
<div class="${properties.kcFormGroupClass!}">
<input type="hidden" name="tryAnotherWay" value="on"/>
<a href="#" id="try-another-way"
onclick="document.forms['kc-select-try-another-way-form'].submit();return false;">${msg("doTryAnotherWay")}</a>
onclick="document.forms['kc-select-try-another-way-form'].requestSubmit();return false;">${msg("doTryAnotherWay")}</a>
</div>
</form>
</#if>

View File

@ -8,7 +8,7 @@
refreshPage = () => {
document.getElementById('isSetRetry').value = 'retry';
document.getElementById('executionValue').value = '${execution}';
document.getElementById('kc-error-credential-form').submit();
document.getElementById('kc-error-credential-form').requestSubmit();
}
</script>

View File

@ -14,7 +14,7 @@
<form id="kc-select-credential-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
<input type="hidden" name="authenticationExecution" value="${authenticationSelection.authExecId}">
</form>
<div class="${properties.kcSelectAuthListItemClass!}" onclick="document.forms[${authenticationSelection?index}].submit()">
<div class="${properties.kcSelectAuthListItemClass!}" onclick="document.forms[${authenticationSelection?index}].requestSubmit()">
<div class="pf-v5-c-data-list__item-content">
<div class="${properties.kcSelectAuthListItemIconClass!}">
<i class="${properties['${authenticationSelection.iconCssClass}']!authenticationSelection.iconCssClass} ${properties.kcSelectAuthListItemIconPropertyClass!}"></i>

View File

@ -66,9 +66,9 @@
</#if>
<script type="module" src="${url.resourcesPath}/js/passwordVisibility.js"></script>
<script type="module">
import { checkCookiesAndSetTimer } from "${url.resourcesPath}/js/authChecker.js";
import { startSessionPolling } from "${url.resourcesPath}/js/authChecker.js";
checkCookiesAndSetTimer(
startSessionPolling(
"${url.ssoLoginInOtherTabsUrl?no_esc}"
);
@ -190,7 +190,7 @@
<#if auth?has_content && auth.showTryAnotherWayLink()>
<form id="kc-select-try-another-way-form" action="${url.loginAction}" method="post" novalidate="novalidate">
<input type="hidden" name="tryAnotherWay" value="on"/>
<a id="try-another-way" href="javascript:document.forms['kc-select-try-another-way-form'].submit()"
<a id="try-another-way" href="javascript:document.forms['kc-select-try-another-way-form'].requestSubmit()"
class="${properties.kcButtonSecondaryClass} ${properties.kcButtonBlockClass} ${properties.kcMarginTopClass}">
${kcSanitize(msg("doTryAnotherWay"))?no_esc}
</a>