[TikiWiki-commits] [Git][tikiwiki/tiki][master] [ENH] Improve 2FA setup panel, vendor-neutral strings as per description.
"Joseph Ushindi \(@iRosh243\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <69f2d6dfd8098_3818f63c1773@gitlab-sidekiq-low-urgency-cpu-bound-v2-6ddfb88f6d-bmxgm.mail> |
Joseph Ushindi pushed to branch master at Tiki Wiki CMS Groupware / Tiki
Commits:
c9c854f0 by Joseph Ushindi at 2026-04-30T03:55:55+00:00
[ENH] Improve 2FA setup panel, vendor-neutral strings as per description.
---
* [ENH] Enhanced Two-Factor Authentification description.
See merge request tikiwiki/tiki!9837
- - - - -
3 changed files:
- + lib/jquery_tiki/tiki-user_preferences.js
- templates/tiki-user_preferences.tpl
- tiki-user_preferences.php
Changes:
=====================================
lib/jquery_tiki/tiki-user_preferences.js
=====================================
@@ -0,0 +1,34 @@
+document.addEventListener('DOMContentLoaded', () => {
+ const digits = document.querySelectorAll('.otp-digit');
+ const hidden = document.getElementById('authCode');
+
+ function syncHidden() {
+ hidden.value = Array.from(digits).map(input => input.value).join('');
+ }
+
+ digits.forEach((input, i) => {
+ input.addEventListener('input', () => {
+ input.value = input.value.replace(/\D/g, '').slice(-1);
+ syncHidden();
+ if (input.value && i < digits.length - 1) digits[i + 1].focus();
+ });
+
+ input.addEventListener('keydown', (e) => {
+ if (e.key === 'Backspace' && !input.value && i > 0) digits[i - 1].focus();
+ });
+
+ input.addEventListener('paste', (e) => {
+ e.preventDefault();
+ const pasted = (e.clipboardData || window.clipboardData)
+ .getData('text')
+ .replace(/\D/g, '')
+ .slice(0, digits.length);
+ [...pasted].forEach((char, j) => {
+ if (digits[i + j]) digits[i + j].value = char;
+ });
+ const nextIndex = Math.min(i + pasted.length, digits.length - 1);
+ digits[nextIndex].focus();
+ syncHidden();
+ });
+ });
+});
\ No newline at end of file
=====================================
templates/tiki-user_preferences.tpl
=====================================
@@ -794,85 +794,104 @@
{if $prefs.twoFactorAuth eq 'y' and ($tiki_p_admin ne 'y' or $userwatch eq $user)}
{tab name="{tr}Security{/tr}"}
- <h2>{title help="Two-factor-authentication"}{tr}Two-Factor Authentication{/tr}{/title}</h2>
- {*If Two-factor authentication is required and the user has not yet enabled it, show a warning.*}
- {if $prefs.twoFactorAuth eq 'y' and $force2FA eq 'y' and empty($twoFactorSecret)}
- {remarksbox type="error" title="{tr}Two-factor authentication is required{/tr}" close="n"}{tr}Your access to the site is restricted until you enable <strong>Two-factor authentication</strong>. Please enable Two-factor authentication to keep using normally the site.{/tr}{/remarksbox}
- {/if}
- {remarksbox type="tip"}
- {tr}Two-factor authentication is a security measure that requires an extra code when you log in.
- When enabled, Tiki will prompt you for a TOTP code generated by any authenticator app
- (FreeOTP, Authy, Google Authenticator, etc.).{/tr}
- {/remarksbox}
- {if $tfaSecret }
- <form action="tiki-user_preferences.php" method="post">
- {ticket}
- <div class="tiki-form-group row">
- <div class="col-md-5">
- <img id="tfaQrCodeImage" class="responsive" src="data:image/{$imageType};base64,{$tfaSecretQR}"/>
- {if $imageType eq 'svg+xml'}
- {assign var="fileExtension" value="svg"}
- {else}
- {assign var="fileExtension" value="png"}
- {/if}
- {capture assign="downloadFileName"}tfa_qr_code.{$fileExtension}{/capture}
-
-
- <div class="mt-1 d-flex justify-content-center align-items-center gap-2">
- <button type="button" class="btn btn-sm btn-secondary" id="downloadQrCode">
- {tr}Download QR Code{/tr}
- </button>
- <button type="button" class="btn btn-sm btn-secondary" id="showSecret">
- {tr}Show Secret Code{/tr}
- </button>
- </div>
-
- <div id="secretContainer" class="d-none mt-3 text-center">
- <strong>{tr}Secret Code:{/tr}</strong>
- <span id="secretField">{$tfaSecret}</span>
+ <h2>{title help="Two-factor-authentication"}{tr}Two-Factor Authentication{/tr}{/title}</h2>
+ {*If Two-factor authentication is required and the user has not yet enabled it, show a warning.*}
+ {if $prefs.twoFactorAuth eq 'y' and $force2FA eq 'y' and empty($twoFactorSecret)}
+ {remarksbox type="error" title="{tr}Two-factor authentication is required{/tr}" close="n"}
+ {tr}Your access to the site is restricted until you enable Two-factor authentication. Please enable Two-factor authentication to continue using the site normally.{/tr}
+ {/remarksbox}
+ {/if}
+ {remarksbox type="tip"}
+ {tr}Two-factor authentication adds a second verification step at login using a time-based
+ one-time password (TOTP), as defined in RFC 6238. When enabled, Tiki will prompt you for
+ a 6-digit code generated by any compatible authenticator app.{/tr}
+ {/remarksbox}
+ {if $tfaSecret}
+ <form action="tiki-user_preferences.php" method="post">
+ {ticket}
+ <div class="tiki-form-group row">
+ <div class="col-md-5 p-4 text-center">
+ <img id="tfaQrCodeImage" class="responsive" src="data:image/{$imageType};base64,{$tfaSecretQR}"/>
+ {if $imageType eq 'svg+xml'}
+ {assign var="fileExtension" value="svg"}
+ {else}
+ {assign var="fileExtension" value="png"}
+ {/if}
+ {capture assign="downloadFileName"}tfa_qr_code.{$fileExtension}{/capture}
- <div class="mt-2">
- <small class="text-secondary">{tr}Save this secret code in a safe place. It is required to restore your Two-Factor Authentication if you lose your device.{/tr}</small>
+ <div class="mt-4 d-flex justify-content-center align-items-center gap-2">
+ <button type="button" class="btn btn-sm btn-secondary fs-6" id="downloadQrCode">
+ {tr}Download QR Code{/tr}
+ </button>
+ <button type="button" class="btn btn-sm btn-secondary fs-6" id="showSecret">
+ {tr}Show Secret Code{/tr}
+ </button>
+ </div>
+ <div id="secretContainer" class="d-none w-100 text-center">
+ <div class="text-uppercase fs-5 fw-semibold mt-3 mb-1">{tr}Secret Code{/tr}</div>
+ <div class="d-flex justify-content-center align-items-center gap-2 flex-wrap">
+ <code id="secretField" class="p-1 rounded border">{$tfaSecret}</code>
+ <button type="button" class="btn btn-md btn-primary py-0 px-2 copy" data-clipboard-target="#secretField">{tr}Copy{/tr}</button>
+ </div>
+ <div class="mt-3 alert alert-warning py-2 px-3">
+ <small>{tr}Save this secret code in a secure location. You will need it to restore Two-Factor Authentication if you lose or replace your device.{/tr}</small>
+ </div>
</div>
</div>
- </div>
- <div class="col-md-7 p-4">
- <div class="d-flex mt-4">
- <div class="well">
- {tr}Install a soft token authenticator like FreeOTP or Google Authenticator from your application repository and use that app to scan this QR code. More information is available in the documentation.{/tr} <a href="https://en.wikipedia.org/wiki/Comparison_of_OTP_applications" target="_blank">{tr}Learn more about authenticator apps{/tr}</a>
+ <div class="col-md-7 p-4">
+ <div class="d-flex mt-4">
+ <div class="well">
+ {tr}Install any (RFC 6238-compatible) authenticator app from your device's application repository, then use it to scan this QR code. More information is available in the documentation.{/tr} <a href="https://en.wikipedia.org/wiki/Comparison_of_OTP_applications" target="_blank">{tr}Compare RFC 6238-compatible authenticator apps{/tr}</a>
- <div class="well mt-3">
- {tr}Scan this QR code with your TOTP authenticator app (FreeOTP, Google Authenticator, etc.). For security, consider saving this QR code or secret key as a backup so you can restore it if you lose or change your device.{/tr}
- </div>
+ <div class="mt-3">
+ {tr}Scan this QR code with your TOTP authenticator app. Save the QR code or the Secret Code in a secure location, you will need it to restore access if you lose or replace your device.{/tr}
+ </div>
+ <div class="mt-4 tiki-form-group">
+ <label class="fs-5">{tr}Verification Code:{/tr}</label>
+ <input type="hidden" name="tfaPin" id="authCode">
- <div style="margin-top: 20px" class="tiki-form-group">
- <label for="authCode">{tr}2FA Code{/tr}</label>
- <input type="text" class="form-control" name="tfaPin" id="authCode" placeholder="{tr}Enter the 6-digit code from your authenticator app{/tr}">
+ {* OTP-style 6-digit input *}
+ <div class="d-flex gap-2" id="otpInputs">
+ <input type="text" class="form-control form-control-lg text-center font-monospace otp-digit"
+ maxlength="1" inputmode="numeric">
+ <input type="text" class="form-control form-control-lg text-center font-monospace otp-digit"
+ maxlength="1" inputmode="numeric">
+ <input type="text" class="form-control form-control-lg text-center font-monospace otp-digit"
+ maxlength="1" inputmode="numeric">
+ <input type="text" class="form-control form-control-lg text-center font-monospace otp-digit"
+ maxlength="1" inputmode="numeric">
+ <input type="text" class="form-control form-control-lg text-center font-monospace otp-digit"
+ maxlength="1" inputmode="numeric">
+ <input type="text" class="form-control form-control-lg text-center font-monospace otp-digit"
+ maxlength="1" inputmode="numeric">
+ </div>
+ <small class="form-text text-secondary pb-2 mt-2">
+ {tr}Enter the 6-digit code generated by your authenticator app to verify and enable Two-Factor Authentication.{/tr}
+ </small>
+ </div>
</div>
</div>
- </div>
- <input type="text" value="{$tfaSecret}" hidden name="tfaSecret">
- <div class="submit d-flex justify-content-end mt-2">
- <input type="submit" class="btn btn-primary btn-md" name="twofactor" value="{tr}Enable Two-Factor Auth{/tr}">
+ <input type="hidden" value="{$tfaSecret}" name="tfaSecret">
+ <div class="submit justify-content-start mt-2">
+ <input type="submit" class="btn btn-primary btn-md" name="twofactor" value="{tr}Enable Two-Factor Auth{/tr}">
+ </div>
</div>
</div>
- </div>
-
- </form>
- {else}
- <form action="tiki-user_preferences.php" method="post">
- {ticket}
- {if $twoFactorSecret}
- <div class="submit text-center">
- <input type="submit" class="btn btn-danger btn-sm" name="removetwofactor" value="{tr}Disable Two-Factor Auth{/tr}">
- </div>
- {else}
- <div class="submit text-center">
- <input type="submit" class="btn btn-secondary btn-sm" name="twofactor" value="{tr}Enable Two-Factor Auth{/tr}">
- </div>
- {/if}
- </form>
- {/if}
+ </form>
+ {else}
+ <form action="tiki-user_preferences.php" method="post">
+ {ticket}
+ {if $twoFactorSecret}
+ <div class="submit text-center">
+ <input type="submit" class="btn btn-danger btn-sm" name="removetwofactor" value="{tr}Disable Two-Factor Auth{/tr}">
+ </div>
+ {else}
+ <div class="submit text-center">
+ <input type="submit" class="btn btn-secondary btn-sm" name="twofactor" value="{tr}Enable Two-Factor Auth{/tr}">
+ </div>
+ {/if}
+ </form>
+ {/if}
{/tab}
{/if}
{*Do not give access to tab Account Deletion, if 2FA is required but not enabled by the user, except for the user 'admin'*}
=====================================
tiki-user_preferences.php
=====================================
@@ -14,68 +14,70 @@ use Tiki\Lib\Theme\ThemeLib;
$inputConfiguration = [
[
'staticKeyFilters' => [
- 'userId' => 'int', //post
- 'view_user' => 'username', //post
- 'generate' => 'bool', //post
- 'tfagenerate' => 'bool', //post
- 'new_info' => 'bool', //post
- 'realName' => 'striptags', //post
- 'gender' => 'striptags', //post
- 'country' => 'striptags', //post
- 'location' => 'striptags', //post
- 'homePage' => 'pagename', //post
- 'new_prefs' => 'bool', //post
- 'mytheme' => 'striptags', //post
- 'userbreadCrumb' => 'digits', //post
- 'language' => 'lang', //post
- 'languageAdmin' => 'lang', //post
- 'read_language' => 'lang', //post
- 'display_timezone' => 'striptags', //post
- 'display_12hr_clock' => 'bool', //post
- 'diff_versions' => 'bool', //post
- 'remember_closed_rboxes' => 'bool', //post
- 'email_isPublic' => 'bool', //post
- 'mailCharset' => 'striptags', //post
- 'prefName' => 'striptags', //post
- 'mess_maxRecords' => 'digits', //post
- 'mess_archiveAfter' => 'digits', //post
- 'mess_sendReadStatus' => 'bool', //post
- 'minPrio' => 'digits', //post
- 'allowMsgs' => 'bool', //post
- 'mytiki_pages' => 'bool', //post
- 'mytiki_blogs' => 'bool', //post
- 'mytiki_msgs' => 'bool', //post
- 'mytiki_tasks' => 'bool', //post
- 'mytiki_forum_topics' => 'bool', //post
- 'mytiki_forum_replies' => 'bool', //post
- 'mytiki_items' => 'bool', //post
- 'mytiki_articles' => 'bool', //post
- 'tasks_maxRecords' => 'digits', //post
- 'xmpp_jid' => 'striptags', //post
- 'xmpp_password' => 'password', //post
- 'xmpp_custom_server_http_bind' => 'striptags', //post
- 'perspective_preferred' => 'digits', //post
- 'webmonetization_payment_pointer' => 'striptags', //post
- 'webmonetization_paywall_text' => 'striptags', //post
- 'notify_oneself' => 'bool', //post
- 'switch_user_notification' => 'bool', //post
- 'cookie_consent_update' => 'word', //post
- 'chgadmin' => 'bool', //post
- 'pass' => 'password', //post
- 'email' => 'email', //post
- 'pass1' => 'password', //post
- 'pass2' => 'password', //post
- 'twofactor' => 'bool', //post
- 'tfaSecret' => 'striptags', //post
- 'tfaPin' => 'digits', //post
- 'removetwofactor' => 'bool', //post
- 'deleteaccount' => 'bool', //post
+ 'userId' => 'int', //post
+ 'view_user' => 'username', //post
+ 'generate' => 'bool', //post
+ 'tfagenerate' => 'bool', //post
+ 'new_info' => 'bool', //post
+ 'realName' => 'striptags', //post
+ 'gender' => 'striptags', //post
+ 'country' => 'striptags', //post
+ 'location' => 'striptags', //post
+ 'homePage' => 'pagename', //post
+ 'new_prefs' => 'bool', //post
+ 'mytheme' => 'striptags', //post
+ 'userbreadCrumb' => 'digits', //post
+ 'language' => 'lang', //post
+ 'languageAdmin' => 'lang', //post
+ 'read_language' => 'lang', //post
+ 'display_timezone' => 'striptags', //post
+ 'display_12hr_clock' => 'bool', //post
+ 'diff_versions' => 'bool', //post
+ 'remember_closed_rboxes' => 'bool', //post
+ 'email_isPublic' => 'bool', //post
+ 'mailCharset' => 'striptags', //post
+ 'prefName' => 'striptags', //post
+ 'mess_maxRecords' => 'digits', //post
+ 'mess_archiveAfter' => 'digits', //post
+ 'mess_sendReadStatus' => 'bool', //post
+ 'minPrio' => 'digits', //post
+ 'allowMsgs' => 'bool', //post
+ 'mytiki_pages' => 'bool', //post
+ 'mytiki_blogs' => 'bool', //post
+ 'mytiki_msgs' => 'bool', //post
+ 'mytiki_tasks' => 'bool', //post
+ 'mytiki_forum_topics' => 'bool', //post
+ 'mytiki_forum_replies' => 'bool', //post
+ 'mytiki_items' => 'bool', //post
+ 'mytiki_articles' => 'bool', //post
+ 'tasks_maxRecords' => 'digits', //post
+ 'xmpp_jid' => 'striptags', //post
+ 'xmpp_password' => 'password', //post
+ 'xmpp_custom_server_http_bind' => 'striptags', //post
+ 'perspective_preferred' => 'digits', //post
+ 'webmonetization_payment_pointer' => 'striptags', //post
+ 'webmonetization_paywall_text' => 'striptags', //post
+ 'notify_oneself' => 'bool', //post
+ 'switch_user_notification' => 'bool', //post
+ 'cookie_consent_update' => 'word', //post
+ 'chgadmin' => 'bool', //post
+ 'pass' => 'password', //post
+ 'email' => 'email', //post
+ 'pass1' => 'password', //post
+ 'pass2' => 'password', //post
+ 'twofactor' => 'bool', //post
+ 'tfaSecret' => 'striptags', //post
+ 'tfaPin' => 'digits', //post
+ 'removetwofactor' => 'bool', //post
+ 'deleteaccount' => 'bool', //post
],
],
];
require_once('tiki-setup.php');
+
use Tiki\Sections;
+
$section = Sections::SECTION_MY_TIKI;
Sections::setCurrentSection($section);
$modlib = TikiLib::lib('mod');
@@ -728,6 +730,9 @@ if ($prefs['feature_wiki'] == 'y' and $prefs['feature_wiki_userpage'] == 'y') {
$smarty->assign('userPageExists', 'y');
}
}
+
+$headerlib->add_jsfile('lib/jquery_tiki/tiki-user_preferences.js');
+
include_once('tiki-section_options.php');
$smarty->assign('mid', 'tiki-user_preferences.tpl');
$smarty->display("tiki.tpl");
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/c9c854f066cd1d07f072b8ed380d48bab49e32de
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/c9c854f066cd1d07f072b8ed380d48bab49e32de
You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs