From c89baf34a8319314a28cb5306e7b682c7cbba730 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Fri, 13 Feb 2026 10:51:27 +0100 Subject: [PATCH] Update 2FA --- AppImage/scripts/auth_manager.py | 5 ++++- AppImage/scripts/flask_auth_routes.py | 12 ++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/AppImage/scripts/auth_manager.py b/AppImage/scripts/auth_manager.py index 9e71c82b..5465d857 100644 --- a/AppImage/scripts/auth_manager.py +++ b/AppImage/scripts/auth_manager.py @@ -786,12 +786,15 @@ def authenticate(username, password, totp_token=None): if config.get("totp_enabled"): if not totp_token: + # First step: password OK, now request TOTP code (not a failure) return False, None, True, "2FA code required" # Verify TOTP token or backup code success, message = verify_totp(username, totp_token, use_backup=len(totp_token) == 9) # Backup codes are formatted XXXX-XXXX if not success: - return False, None, True, message + # TOTP code is wrong: return requires_totp=False so the caller + # logs it as a real authentication failure for Fail2Ban + return False, None, False, "Invalid 2FA code" token = generate_token(username) if token: diff --git a/AppImage/scripts/flask_auth_routes.py b/AppImage/scripts/flask_auth_routes.py index 2f26c3e7..d28a299b 100644 --- a/AppImage/scripts/flask_auth_routes.py +++ b/AppImage/scripts/flask_auth_routes.py @@ -157,15 +157,23 @@ def auth_login(): if success: return jsonify({"success": True, "token": token, "message": message}) elif requires_totp: + # First step: password OK, requesting TOTP code (not a failure) return jsonify({"success": False, "requires_totp": True, "message": message}), 200 else: - # Log failed auth for Fail2Ban detection + # Authentication failure (wrong password or wrong TOTP code) client_ip = _get_client_ip() auth_logger.warning( "authentication failure; rhost=%s user=%s", client_ip, username or "unknown" ) - return jsonify({"success": False, "message": message}), 401 + # If user submitted a TOTP token that was wrong, tell frontend + # to keep showing the TOTP field (not go back to password step) + is_totp_failure = totp_token and "2FA" in message + return jsonify({ + "success": False, + "message": message, + "requires_totp": is_totp_failure + }), 401 except Exception as e: return jsonify({"success": False, "message": str(e)}), 500