twofa.php

main 107 lines · 4.4 KB Raw
Alex Alex Commit Initial commit 01/10/2026 09:20
1<?php require_once 'engine/init.php';
2znote_csrf_protect_public_post();
3protect_page();
4theme_open();
5
6$twofa2Enabled = znote2fa_v2_enabled();
7
8if ($config['twoFactorAuthenticator'] === false && !$twofa2Enabled) {
9 die("Two-factor authentication is disabled in config.php");
10}
11
12// When only 2FA v2 is enabled, this whole legacy-TFS section has nothing to
13// offer - skip straight past it.
14if ($config['twoFactorAuthenticator'] !== false) {
15 if ($config['ServerEngine'] !== 'TFS_10') {
16 view('twofa_legacy_incompatible', ['twofa2Enabled' => $twofa2Enabled]);
17 } else {
18 // If user wishes to disable Two-Factor Authentication
19 if (isset($_POST['disable_2fa'])) {
20 db()->execute("UPDATE `accounts` SET `secret` = NULL WHERE `id` = ? LIMIT 1;", [(int)$session_user_id]);
21 db()->execute("UPDATE `znote_accounts` SET `secret` = NULL WHERE `account_id` = ? LIMIT 1;", [(int)$session_user_id]);
22 }
23
24 // General init
25 require_once("engine/function/rfc6238.php");
26
27 // Fetch the secret data from accounts and znote_accounts table
28 $query = db()->fetchOne("SELECT `a`.`secret` AS `secret`, `za`.`secret` AS `znote_secret` FROM `accounts` AS `a` INNER JOIN `znote_accounts` AS `za` ON `a`.`id` = `za`.`account_id` WHERE `a`.`id` = ? LIMIT 1;", [(int)$session_user_id]);
29
30 // If secret column returns NULL on the regular accounts table, then it means the system is not active.
31 $status = ($query['secret'] === NULL) ? false : true;
32
33 // If secret column returns NULL on the znote_accounts table, then it means we havent generated a secret for it yet.
34 if ($query['znote_secret'] === NULL) {
35 $scrtString = ($query['secret'] === NULL) ? generateRandomString(16) : $query['secret'];
36 // Add secret to znote_accounts table
37 db()->execute("UPDATE `znote_accounts` SET `secret` = ? WHERE `account_id` = ?;", [$scrtString, (int)$session_user_id]);
38 $query['znote_secret'] = $scrtString;
39 }
40
41 view('twofa_legacy', ['status' => $status, 'query' => $query]);
42 }
43}
44
45// ---------------------------------------------------------------------------
46// 2FA v2 - independent of the game engine.
47// ---------------------------------------------------------------------------
48if ($twofa2Enabled) {
49
50 $accountId = (int)$session_user_id;
51 $revealedRecoveryCodes = array();
52 $successes = array();
53
54 if (empty($_POST) === false) {
55 if (isset($_POST['tfa2_totp_start'])) {
56 $secret = znote2fa_totp_start($accountId);
57
58 } else if (isset($_POST['tfa2_totp_confirm'])) {
59 $code = getValue($_POST['tfa2_totp_code'] ?? null);
60 if ($code !== false && znote2fa_totp_confirm($accountId, $code)) {
61 $successes[] = t_default('twofa2.totp_confirmed', 'Authenticator app enabled.');
62 } else {
63 $errors[] = t_default('twofa2.totp_confirm_failed', 'That code did not match. Scan the QR code again and try once more.');
64 }
65
66 } else if (isset($_POST['tfa2_totp_disable'])) {
67 znote2fa_totp_disable($accountId);
68
69 } else if (isset($_POST['tfa2_email_toggle'])) {
70 $enableEmailOtp = !empty($_POST['tfa2_email_enabled']);
71 $email = trim((string)($user_data['email'] ?? ''));
72 if ($enableEmailOtp && !znote2fa_v2_config()['email_otp_enabled']) {
73 $errors[] = t_default('twofa2.email_unavailable', 'E-mail codes are disabled by the site administrator.');
74 } else if ($enableEmailOtp && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
75 $errors[] = t_default('twofa2.email_invalid', 'Add a valid e-mail address to your account before enabling e-mail codes.');
76 } else {
77 znote2fa_email_otp_set($accountId, $enableEmailOtp);
78 }
79
80 } else if (isset($_POST['tfa2_recovery_generate'])) {
81 $revealedRecoveryCodes = znote2fa_recovery_generate($accountId);
82
83 } else if (isset($_POST['tfa2_device_revoke'])) {
84 znote2fa_trusted_device_revoke($accountId, (int)$_POST['tfa2_device_revoke']);
85
86 } else if (isset($_POST['tfa2_logout_all'])) {
87 znote2fa_logout_all_devices($accountId);
88 $_SESSION['tfa2_sv'] = znote2fa_session_version($accountId); // keep this session, the one that asked, alive
89 znote2fa_trusted_cookie_clear();
90 $successes[] = t_default('twofa2.logged_out_all', 'Every other session and trusted device has been signed out.');
91 }
92 }
93
94 $status = znote2fa_status($accountId);
95 $devices = znote2fa_trusted_devices_list($accountId);
96
97 view('twofa2', [
98 'status' => $status,
99 'devices' => $devices,
100 'revealedRecoveryCodes' => $revealedRecoveryCodes,
101 'accountId' => $accountId,
102 'successes' => $successes,
103 ]);
104}
105
106theme_close(); ?>
107
Top