Implement email-based 2FA authentication for Telegram bot with Oracle integration fixes

This commit adds a complete email authentication flow for the Telegram bot, allowing users to login with email + password instead of web app linking codes. Includes critical bug fixes for Oracle integration.

**New Features:**
- Email-based 2FA authentication with 6-digit codes sent via SMTP
- Backend endpoints: verify-email and login-with-email
- ConversationHandler for email authentication flow in Telegram bot
- Session token verification to prevent user ID spoofing
- Rate limiting (5 attempts per 5 minutes)
- Email code expiry (5 minutes) with automatic cleanup

**Bug Fixes:**
- Fixed Oracle column name: ACTIV → INACTIV (with inverted logic)
- Fixed Oracle password verification: verificautilizator returns checksum, not user_id
- Fixed username case sensitivity: Oracle usernames must be uppercase
- Fixed SMTP connection: use start_tls parameter instead of manual STARTTLS
- Added middleware exclusions for public email auth endpoints

**Backend Changes:**
- Added verify-email endpoint (public) in telegram.py
- Added login-with-email endpoint (public) with rate limiting and session verification
- Updated middleware exclusions in main.py and auth_middleware_wrapper.py
- Added AUTH_SESSION_SECRET configuration for session token signing

**Telegram Bot Changes:**
- New modules: app/auth/email_auth.py, app/bot/email_handlers.py
- New utilities: app/utils/email_service.py (SMTP email sending)
- Updated handlers.py: ignore callbacks handled by ConversationHandler
- Updated menus.py: show Login button for unauthenticated users
- Updated API client: verify_email() and login_with_email() methods
- Database: email_auth_codes table with cleanup task

**Configuration:**
- Added SMTP configuration to telegram-bot .env.example
- Added AUTH_SESSION_SECRET to backend .env.example
- Updated .gitignore: exclude temporary files (*.pid, *.checksum, test scripts)

**Dependencies:**
- Added aiosmtplib for async SMTP email sending

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-11 12:00:46 +02:00
parent 1378ee1e6a
commit 706062dc0f
19 changed files with 2032 additions and 101 deletions

View File

@@ -39,6 +39,76 @@ check_port() {
fi
}
# Function to check if requirements.txt has changed
check_requirements_changed() {
local requirements_file=$1
local checksum_file="${requirements_file}.checksum"
if [ ! -f "$requirements_file" ]; then
return 1 # Requirements file doesn't exist
fi
# Calculate current checksum
current_checksum=$(md5sum "$requirements_file" | cut -d' ' -f1)
# Check if checksum file exists and compare
if [ -f "$checksum_file" ]; then
stored_checksum=$(cat "$checksum_file")
if [ "$current_checksum" = "$stored_checksum" ]; then
return 1 # No change
fi
fi
return 0 # Changed or first time
}
# Function to save requirements checksum
save_requirements_checksum() {
local requirements_file=$1
local checksum_file="${requirements_file}.checksum"
if [ -f "$requirements_file" ]; then
md5sum "$requirements_file" | cut -d' ' -f1 > "$checksum_file"
fi
}
# Function to install or update Python dependencies
install_python_dependencies() {
local project_name=$1
local venv_path=$2
local requirements_file=$3
# Check if venv exists
if [ ! -d "$venv_path" ]; then
print_message "Creating Python virtual environment for $project_name..."
python3 -m venv "$venv_path"
fi
# Activate virtual environment
source "$venv_path/bin/activate"
# Check if requirements have changed or dependencies are missing
local should_install=false
if check_requirements_changed "$requirements_file"; then
print_message "Requirements changed for $project_name - updating dependencies..."
should_install=true
elif ! python -c "import sys; import importlib; [importlib.import_module(line.split('>=')[0].split('==')[0]) for line in open('$requirements_file').read().splitlines() if line and not line.startswith('#')]" 2>/dev/null; then
print_message "Missing dependencies detected for $project_name - installing..."
should_install=true
fi
if [ "$should_install" = true ]; then
print_message "Installing/updating $project_name dependencies..."
pip install --upgrade pip > /dev/null 2>&1
pip install -r "$requirements_file"
save_requirements_checksum "$requirements_file"
print_success "$project_name dependencies installed/updated successfully"
else
print_message "$project_name dependencies are up to date"
fi
}
# Function to cleanup processes on exit
cleanup() {
print_message "Stopping services..."
@@ -284,15 +354,7 @@ start_service() {
fi
cd reports-app/backend/
if [ ! -d "venv" ]; then
print_message "Creating Python virtual environment..."
python3 -m venv venv
fi
source venv/bin/activate
if ! python -c "import fastapi, uvicorn" 2>/dev/null; then
print_message "Installing backend dependencies..."
pip install -r requirements.txt
fi
install_python_dependencies "Backend" "venv" "requirements.txt"
print_message "Starting uvicorn server..."
# NOTE: --reload disabled for cache to work properly (global variables issue)
@@ -375,15 +437,7 @@ start_service() {
exit 1
fi
if [ ! -d "venv" ]; then
print_message "Creating Python virtual environment for Telegram bot..."
python3 -m venv venv
fi
source venv/bin/activate
if ! python -c "import telegram" 2>/dev/null; then
print_message "Installing Telegram bot dependencies..."
pip install -r requirements.txt
fi
install_python_dependencies "Telegram Bot" "venv" "requirements.txt"
print_message "Starting Telegram bot..."
nohup python -m app.main > /tmp/roa2web_telegram.log 2>&1 &