From 9f5589996062f21fcc3eb75aa9144463be55e2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ou=C5=A1ek?= <brousek@ics.muni.cz> Date: Thu, 24 Mar 2022 19:29:09 +0100 Subject: [PATCH] fix: support OTP for users without JavaScript hide elements by PHP instead of JavaScript, set preferred mode by JavaScript so that users without it always have "otp" --- templates/LoginForm.php | 38 ++++++++++++++++++++--------- www/js/loginform.js | 54 ++++++++++------------------------------- 2 files changed, 40 insertions(+), 52 deletions(-) diff --git a/templates/LoginForm.php b/templates/LoginForm.php index 988be56..e3083c6 100644 --- a/templates/LoginForm.php +++ b/templates/LoginForm.php @@ -2,6 +2,12 @@ use SimpleSAML\Module; +$this->data['u2fAvailable'] = !empty($this->data['u2fSignRequest']); +$this->data['webAuthnAvailable'] = !empty($this->data['webAuthnSignRequest']); +$this->data['noAlternatives'] = !$this->data['pushAvailable'] && (!$this->data['u2fAvailable']) && (!$this->data['webAuthnAvailable']); + +$this->data['mode'] = ($this->data['mode'] ?? null) ?: 'otp'; + // Set default scenario if isn't set if (!empty($this->data['authProcFilterScenario'])) { if (empty($this->data['username'])) { @@ -91,7 +97,7 @@ if (null !== $this->data['errorCode']) { <input type="hidden" id="username" name="username" value="<?php echo htmlspecialchars($this->data['username'] ?? '', ENT_QUOTES); ?>"/> <?php - } else { + } elseif ($this->data['step'] <= 1) { ?> <p> <label for="username" class="sr-only"> @@ -132,12 +138,15 @@ if (null !== $this->data['errorCode']) { } ?> <!-- Pass and OTP fields --> + <?php if (!$this->data['authProcFilterScenario']) { ?> <label for="password" class="sr-only"> <?php echo $this->t('{privacyidea:privacyidea:password}'); ?> </label> <input id="password" name="password" tabindex="1" type="password" value="" class="text" placeholder="<?php echo htmlspecialchars($passHint, ENT_QUOTES); ?>"/> + <?php } ?> + <?php if ($this->data['step'] > 1) { ?> <p id="message" role="alert"><?php $messageOverride = $this->data['messageOverride'] ?? null; if (null === $messageOverride || is_string($messageOverride)) { @@ -149,13 +158,16 @@ if (null !== $this->data['errorCode']) { echo call_user_func($messageOverride, $this->data['message'] ?? ''); } ?></p> + <?php } ?> + <?php if ($this->data['step'] > 1) { ?> <p> <label for="otp" class="sr-only"> <?php echo $this->t('{privacyidea:privacyidea:otp}'); ?> </label> <input id="otp" name="otp" type="password" placeholder="<?php echo htmlspecialchars($otpHint, ENT_QUOTES); ?>"> </p> + <?php } ?> <p> <button id="submitButton" tabindex="1" class="rc-button rc-button-submit" type="submit" name="Submit" value="1"> @@ -164,8 +176,8 @@ if (null !== $this->data['errorCode']) { </p> <!-- Undefined index is suppressed and the default is used for these values --> - <input id="mode" type="hidden" name="mode" - value="<?php echo htmlspecialchars(($this->data['mode'] ?? null) ?: 'otp', ENT_QUOTES); ?>"/> + <input id="mode" type="hidden" name="mode" value="otp" + data-preferred="<?php echo htmlspecialchars($this->data['mode'], ENT_QUOTES); ?>"/> <input id="pushAvailable" type="hidden" name="pushAvailable" value="<?php echo ($this->data['pushAvailable'] ?? false) ? 'true' : ''; ?>"/> @@ -243,28 +255,37 @@ if (null !== $this->data['errorCode']) { </div> <!-- slide-out--> </div> <!-- form-panel --> - <div id="AlternateLoginOptions" class="groupMargin"> + <?php if (!$this->data['noAlternatives'] && $this->data['step'] > 1) { ?> + <div id="AlternateLoginOptions" class="groupMargin hidden js-show"> <h3><?php echo $this->t('{privacyidea:privacyidea:alternate_login_options}'); ?></h3> <!-- Alternate Login Options--> + <?php if (($this->data['webAuthnAvailable'] ?? false) && 'webauthn' !== $this->data['mode']) { ?> <button id="useWebAuthnButton" name="useWebAuthnButton" type="button"> <span><?php echo $this->t('{privacyidea:privacyidea:webauthn}'); ?></span> </button> + <?php } ?> + <?php if (($this->data['pushAvailable'] ?? false) && 'push' !== $this->data['mode']) { ?> <button id="usePushButton" name="usePushButton" type="button"> <span><?php echo $this->t('{privacyidea:privacyidea:push}'); ?></span> </button> + <?php } ?> + <?php if (($this->data['otpAvailable'] ?? true) && 'otp' !== $this->data['mode']) { ?> <button id="useOTPButton" name="useOTPButton" type="button"> <span><?php echo $this->t('{privacyidea:privacyidea:otp}'); ?></span> </button> + <?php } ?> + <?php if (($this->data['u2fAvailable'] ?? false) && 'u2f' !== $this->data['mode']) { ?> <button id="useU2FButton" name="useU2FButton" type="button"> <span><?php echo $this->t('{privacyidea:privacyidea:u2f}'); ?></span> </button> + <?php } ?> </div> - <br> + <?php } ?> </form> <?php // Logout - if ($this->data['showLogout'] ?? true && isset($this->data['LogoutURL'])) { ?> + if (($this->data['showLogout'] ?? true) && isset($this->data['LogoutURL'])) { ?> <p> <a href="<?php echo htmlspecialchars($this->data['LogoutURL']); ?>"><?php echo $this->t('{status:logout}'); ?></a> </p> @@ -291,11 +312,6 @@ if (!empty($this->data['links'])) { </script> <meta id="privacyidea-step" name="privacyidea-step" content="<?php echo $this->data['step']; ?>"> - <meta id="privacyidea-hide-alternate" name="privacyidea-hide-alternate" content="<?php echo ( - !$this->data['pushAvailable'] - && (!isset($this->data['u2fSignRequest']) || ($this->data['u2fSignRequest']) === '') - && (!isset($this->data['webAuthnSignRequest']) || ($this->data['webAuthnSignRequest']) === '') - ) ? 'true' : ''; ?>"> <meta id="privacyidea-translations" name="privacyidea-translations" content="<?php $translations = []; diff --git a/www/js/loginform.js b/www/js/loginform.js index e5e2b71..3ac9a08 100644 --- a/www/js/loginform.js +++ b/www/js/loginform.js @@ -223,65 +223,41 @@ function addClickListener(id, listener) { } function initPrivacyIDEA() { + // set preferred mode by JavaScript so that users without it always have "otp" + const preferredMode = getElement("mode").dataset.preferred; + if (preferredMode) { + set("mode", preferredMode); + } + document.querySelectorAll(".js-show").forEach((el) => { + el.classList.remove("hidden"); + }); + const step = getContent("privacyidea-step"); - if (step > "1") { + if (step > 1) { hide("username"); hide("password"); } else { hide("otp"); hide("message"); - hide("AlternateLoginOptions"); - } - - // Set alternate token button visibility - if (value("webAuthnSignRequest") === "") { - hide("useWebAuthnButton"); - } - - if (value("u2fSignRequest") === "") { - hide("useU2FButton"); - } - - if (!booleanValue("pushAvailable")) { - hide("usePushButton"); - } - - if (!booleanValue("otpAvailable")) { - hide("useOTPButton"); - } - - if ( - !booleanValue("pushAvailable") && - value("webAuthnSignRequest") === "" && - value("u2fSignRequest") === "" - ) { - hide("AlternateLoginOptions"); } - if (value("mode") === "otp") { - hide("useOTPButton"); + if (step > 1 && value("mode") !== "otp") { + hide("otp"); + hide("submitButton"); } if (value("mode") === "webauthn") { - hide("otp"); - hide("submitButton"); doWebAuthn(); } if (value("mode") === "u2f") { - hide("otp"); - hide("submitButton"); doU2F(); } if (value("mode") === "push") { const pollingIntervals = [4, 3, 2, 1]; - hide("otp"); - hide("usePushButton"); - hide("submitButton"); - if (value("loadCounter") > pollingIntervals.length - 1) { refreshTime = pollingIntervals[pollingIntervals.length - 1]; } else { @@ -294,10 +270,6 @@ function initPrivacyIDEA() { }, refreshTime); } - if (getContent("privacyidea-hide-alternate") === "true") { - hide("AlternateLoginOptions"); - } - addClickListener("useWebAuthnButton", doWebAuthn); addClickListener("usePushButton", () => changeMode("push")); addClickListener("useOTPButton", () => changeMode("otp")); -- GitLab