totp adjustments

This commit is contained in:
Ryan
2025-04-05 23:42:52 -04:00
committed by GitHub
parent 5100e8bf3b
commit 8d370fd1bb
4 changed files with 47 additions and 66 deletions

View File

@@ -186,8 +186,8 @@ if (!preg_match('/^[A-Za-z0-9_\- ]+$/', $username)) {
$user = authenticate($username, $password); $user = authenticate($username, $password);
if ($user !== false) { if ($user !== false) {
if (!empty($user['totp_secret'])) { if (!empty($user['totp_secret'])) {
if (empty($data['totp_code'])) { // If TOTP code is missing or malformed, indicate that TOTP is required.
http_response_code(401); if (empty($data['totp_code']) || !preg_match('/^\d{6}$/', $data['totp_code'])) {
echo json_encode([ echo json_encode([
"totp_required" => true, "totp_required" => true,
"message" => "TOTP code required" "message" => "TOTP code required"
@@ -197,7 +197,6 @@ if ($user !== false) {
$tfa = new \RobThree\Auth\TwoFactorAuth('FileRise'); $tfa = new \RobThree\Auth\TwoFactorAuth('FileRise');
$providedCode = trim($data['totp_code']); $providedCode = trim($data['totp_code']);
if (!$tfa->verifyCode($user['totp_secret'], $providedCode)) { if (!$tfa->verifyCode($user['totp_secret'], $providedCode)) {
http_response_code(401);
echo json_encode(["error" => "Invalid TOTP code"]); echo json_encode(["error" => "Invalid TOTP code"]);
exit(); exit();
} }
@@ -232,10 +231,13 @@ if ($user !== false) {
]; ];
$encryptedContent = encryptData(json_encode($persistentTokens, JSON_PRETTY_PRINT), $encryptionKey); $encryptedContent = encryptData(json_encode($persistentTokens, JSON_PRETTY_PRINT), $encryptionKey);
file_put_contents($persistentTokensFile, $encryptedContent, LOCK_EX); file_put_contents($persistentTokensFile, $encryptedContent, LOCK_EX);
// Define $secure based on whether HTTPS is enabled
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
setcookie('remember_me_token', $token, $expiry, '/', '', $secure, true); setcookie('remember_me_token', $token, $expiry, '/', '', $secure, true);
} }
echo json_encode([ echo json_encode([
"status" => "ok",
"success" => "Login successful", "success" => "Login successful",
"isAdmin" => $_SESSION["isAdmin"], "isAdmin" => $_SESSION["isAdmin"],
"folderOnly"=> $_SESSION["folderOnly"], "folderOnly"=> $_SESSION["folderOnly"],

View File

@@ -98,14 +98,14 @@ function loadAdminConfigFunc() {
localStorage.setItem("disableFormLogin", config.loginOptions.disableFormLogin); localStorage.setItem("disableFormLogin", config.loginOptions.disableFormLogin);
localStorage.setItem("disableBasicAuth", config.loginOptions.disableBasicAuth); localStorage.setItem("disableBasicAuth", config.loginOptions.disableBasicAuth);
localStorage.setItem("disableOIDCLogin", config.loginOptions.disableOIDCLogin); localStorage.setItem("disableOIDCLogin", config.loginOptions.disableOIDCLogin);
localStorage.setItem("globalOtpauthUrl", config.globalOtpauthUrl || "otpauth://totp/FileRise?issuer=FileRise"); localStorage.setItem("globalOtpauthUrl", config.globalOtpauthUrl || "otpauth://totp/{label}?secret={secret}&issuer=FileRise");
updateLoginOptionsUIFromStorage(); updateLoginOptionsUIFromStorage();
}) })
.catch(() => { .catch(() => {
localStorage.setItem("disableFormLogin", "false"); localStorage.setItem("disableFormLogin", "false");
localStorage.setItem("disableBasicAuth", "false"); localStorage.setItem("disableBasicAuth", "false");
localStorage.setItem("disableOIDCLogin", "false"); localStorage.setItem("disableOIDCLogin", "false");
localStorage.setItem("globalOtpauthUrl", "otpauth://totp/FileRise?issuer=FileRise"); localStorage.setItem("globalOtpauthUrl", "otpauth://totp/{label}?secret={secret}&issuer=FileRise");
updateLoginOptionsUIFromStorage(); updateLoginOptionsUIFromStorage();
}); });
} }
@@ -225,9 +225,9 @@ function checkAuthentication(showLoginToast = true) {
function submitLogin(data) { function submitLogin(data) {
setLastLoginData(data); setLastLoginData(data);
window.__lastLoginData = data; window.__lastLoginData = data;
sendRequest("auth.php", "POST", data, { "X-CSRF-Token": window.csrfToken }) sendRequest("auth.php", "POST", data, { "X-CSRF-Token": window.csrfToken })
.then(response => { .then(response => {
if (response.success) { if (response.success || response.status === "ok") {
sessionStorage.setItem("welcomeMessage", "Welcome back, " + data.username + "!"); sessionStorage.setItem("welcomeMessage", "Welcome back, " + data.username + "!");
window.location.reload(); window.location.reload();
} else if (response.totp_required) { } else if (response.totp_required) {

View File

@@ -49,15 +49,11 @@ export function openTOTPLoginModal() {
totpInput.addEventListener("input", function () { totpInput.addEventListener("input", function () {
const code = this.value.trim(); const code = this.value.trim();
if (code.length === 6) { if (code.length === 6) {
// FORM-BASED LOGIN
if (lastLoginData) { if (lastLoginData) {
totpLoginModal.style.display = "none"; totpLoginModal.style.display = "none";
lastLoginData.totp_code = code; lastLoginData.totp_code = code;
window.submitLogin(lastLoginData); window.submitLogin(lastLoginData);
// BASIC-AUTH / OIDC LOGIN
} else { } else {
// keep modal open until we know the result
fetch("totp_verify.php", { fetch("totp_verify.php", {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
@@ -69,10 +65,10 @@ export function openTOTPLoginModal() {
}) })
.then(res => res.json()) .then(res => res.json())
.then(json => { .then(json => {
if (json.success) { if (json.status === "ok") {
window.location.href = "index.html"; window.location.href = "index.html";
} else { } else {
showToast(json.error || json.message || "TOTP verification failed"); showToast(json.message || "TOTP verification failed");
this.value = ""; this.value = "";
totpLoginModal.style.display = "flex"; totpLoginModal.style.display = "flex";
totpInput.focus(); totpInput.focus();
@@ -92,7 +88,6 @@ export function openTOTPLoginModal() {
const modalContent = totpLoginModal.firstElementChild; const modalContent = totpLoginModal.firstElementChild;
modalContent.style.background = modalBg; modalContent.style.background = modalBg;
modalContent.style.color = textColor; modalContent.style.color = textColor;
// reset input if reopening
const totpInput = document.getElementById("totpLoginInput"); const totpInput = document.getElementById("totpLoginInput");
if (totpInput) { if (totpInput) {
totpInput.value = ""; totpInput.value = "";
@@ -232,19 +227,17 @@ export function openTOTPModal() {
</div> </div>
`; `;
document.body.appendChild(totpModal); document.body.appendChild(totpModal);
// Bind the X button to call closeTOTPModal with disable=true
document.getElementById("closeTOTPModal").addEventListener("click", () => { document.getElementById("closeTOTPModal").addEventListener("click", () => {
closeTOTPModal(true); closeTOTPModal(true);
}); });
// Add event listener for TOTP confirmation
document.getElementById("confirmTOTPBtn").addEventListener("click", function () { document.getElementById("confirmTOTPBtn").addEventListener("click", function () {
const code = document.getElementById("totpConfirmInput").value.trim(); const code = document.getElementById("totpConfirmInput").value.trim();
if (code.length !== 6) { if (code.length !== 6) {
showToast("Please enter a valid 6-digit code."); showToast("Please enter a valid 6-digit code.");
return; return;
} }
// Call the endpoint to verify the TOTP code
fetch("totp_verify.php", { fetch("totp_verify.php", {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
@@ -256,12 +249,11 @@ export function openTOTPModal() {
}) })
.then(r => r.json()) .then(r => r.json())
.then(result => { .then(result => {
if (result.success) { if (result.status === 'ok') {
showToast("TOTP successfully enabled."); showToast("TOTP successfully enabled.");
// On success, close the modal without disabling
closeTOTPModal(false); closeTOTPModal(false);
} else { } else {
showToast("TOTP verification failed: " + (result.error || "Invalid code.")); showToast("TOTP verification failed: " + (result.message || "Invalid code."));
} }
}) })
.catch(() => { showToast("Error verifying TOTP code."); }); .catch(() => { showToast("Error verifying TOTP code."); });
@@ -508,7 +500,7 @@ export function openAdminPanel() {
document.getElementById("oidcClientId").value = window.currentOIDCConfig.clientId; document.getElementById("oidcClientId").value = window.currentOIDCConfig.clientId;
document.getElementById("oidcClientSecret").value = window.currentOIDCConfig.clientSecret; document.getElementById("oidcClientSecret").value = window.currentOIDCConfig.clientSecret;
document.getElementById("oidcRedirectUri").value = window.currentOIDCConfig.redirectUri; document.getElementById("oidcRedirectUri").value = window.currentOIDCConfig.redirectUri;
document.getElementById("globalOtpauthUrl").value = window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/FileRise?issuer=FileRise'; document.getElementById("globalOtpauthUrl").value = window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/{label}?secret={secret}&issuer=FileRise';
document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true; document.getElementById("disableFormLogin").checked = config.loginOptions.disableFormLogin === true;
document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true; document.getElementById("disableBasicAuth").checked = config.loginOptions.disableBasicAuth === true;
document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true; document.getElementById("disableOIDCLogin").checked = config.loginOptions.disableOIDCLogin === true;
@@ -529,7 +521,7 @@ export function openAdminPanel() {
document.getElementById("oidcClientId").value = window.currentOIDCConfig.clientId; document.getElementById("oidcClientId").value = window.currentOIDCConfig.clientId;
document.getElementById("oidcClientSecret").value = window.currentOIDCConfig.clientSecret; document.getElementById("oidcClientSecret").value = window.currentOIDCConfig.clientSecret;
document.getElementById("oidcRedirectUri").value = window.currentOIDCConfig.redirectUri; document.getElementById("oidcRedirectUri").value = window.currentOIDCConfig.redirectUri;
document.getElementById("globalOtpauthUrl").value = window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/FileRise?issuer=FileRise'; document.getElementById("globalOtpauthUrl").value = window.currentOIDCConfig.globalOtpauthUrl || 'otpauth://totp/{label}?secret={secret}&issuer=FileRise';
document.getElementById("disableFormLogin").checked = localStorage.getItem("disableFormLogin") === "true"; document.getElementById("disableFormLogin").checked = localStorage.getItem("disableFormLogin") === "true";
document.getElementById("disableBasicAuth").checked = localStorage.getItem("disableBasicAuth") === "true"; document.getElementById("disableBasicAuth").checked = localStorage.getItem("disableBasicAuth") === "true";
document.getElementById("disableOIDCLogin").checked = localStorage.getItem("disableOIDCLogin") === "true"; document.getElementById("disableOIDCLogin").checked = localStorage.getItem("disableOIDCLogin") === "true";

View File

@@ -4,19 +4,6 @@
require_once 'vendor/autoload.php'; require_once 'vendor/autoload.php';
require_once 'config.php'; require_once 'config.php';
// Secure session cookie
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => '', // your domain
'secure' => true, // only over HTTPS
'httponly' => true,
'samesite' => 'Lax'
]);
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
// JSON + CSP // JSON + CSP
header('Content-Type: application/json'); header('Content-Type: application/json');
header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';"); header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self';");