Files
roa2web-service-auto/TELEGRAM_EMAIL_AUTH_PLAN.md
Marius Mutu 1378ee1e6a Implement hybrid two-tier cache system with full monitoring and Telegram bot enhancements
Cache System (Backend):
- Implemented two-tier hybrid cache: L1 (in-memory) + L2 (SQLite)
- L1 cache: Fast dictionary-based with 5-minute TTL for hot data
- L2 cache: Persistent SQLite with 1-hour TTL for warm data
- Cache decorator with automatic tier management and fallback
- Cache key generation with per-user isolation
- Event monitoring system for cache statistics
- Cache benchmarking utilities for performance testing
- Added cache management endpoints: /api/cache/stats, /api/cache/clear, /api/cache/benchmark
- Cache configuration via environment variables (CACHE_ENABLED, CACHE_L1_TTL, etc.)

Backend Services:
- Updated dashboard_service to use @cached decorator with request context
- Added cache support to invoice_service and treasury_service
- Integrated cache manager into main.py with lifespan events
- Added Request parameter to service methods for cache metadata

Frontend Enhancements:
- New CacheStatsView.vue for real-time cache monitoring dashboard
- Cache store (cacheStore.js) for state management
- Updated router to include /cache-stats route
- Navigation updates in DashboardHeader and HamburgerMenu
- Cache stats accessible from main navigation

Telegram Bot Improvements:
- Enhanced formatters with YTD comparison data
- Improved menu navigation and button layout
- Better error handling and user feedback
- Bot startup improvements with graceful shutdown

Auth & Middleware:
- Enhanced middleware with cache metadata injection
- Improved request state handling for cache source tracking

Development:
- Updated start-dev.sh with better error handling
- Added TELEGRAM_EMAIL_AUTH_PLAN.md documentation
- Updated requirements.txt with aiosqlite for async SQLite

Performance:
- L1 cache provides <1ms response for hot data
- L2 cache provides ~5ms response for warm data
- Database queries only for cold data or cache misses
- Cache hit rates tracked and displayed in real-time

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 22:42:00 +02:00

1674 lines
58 KiB
Markdown

# Plan: Autentificare Telegram Bot prin Email + Parolă (2FA)
**Data**: 2025-11-07
**Autor**: Claude Code
**Status**: Planificat - Neîmplementat
---
## 🎯 Obiectiv
Implementare autentificare alternativă **email + parolă** pentru Telegram Bot, în **paralel** cu metoda actuală (cod din web app). Ambele metode vor fi disponibile pentru toți utilizatorii.
**Cerințe cheie:**
- ✅ Minimal invaziv - nu modifica logica existentă
- ✅ Ambele metode de autentificare în paralel
- ✅2FA real: email possession + parolă Oracle
- ✅ Simplu de testat cu un singur utilizator (mmarius28@gmail.com)
---
## 📊 Context: Sistemul Actual
### Metoda Actuală de Autentificare (rămâne neschimbată)
```
1. User se autentifică în web app (username + parolă)
2. User cere linking Telegram → backend generează cod 8-char
3. Backend salvează cod în telegram-bot via POST /internal/save-code
4. User trimite /start ABC123XY în Telegram
5. Bot validează codul și auto-linkează (fără parolă din nou)
6. User autentificat în bot
```
**Caracteristici:**
- Nu necesită email
- Necesită acces la web app
- Auto-linking fără re-introducere parolă
- Cod expiră în 15 minute
---
## 🔄 Noul Flux de Autentificare (Email + Parolă)
### Flow Complet
```
┌─────────────────────────────────────────────────────────────┐
│ 1. User: /login SAU apasă buton "🔐 Login cu Email" │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. Bot: "📧 Introdu adresa ta de email Oracle:" │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. User: mmarius28@gmail.com │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. Bot: │
│ - Verifică email în Oracle UTILIZATORI table │
│ - Generează cod 6-digit random │
│ - Salvează în email_auth_codes (expiry 5 min) │
│ - Trimite email SMTP: "Codul tău: 123456" │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. Bot: "✉️ Cod trimis pe email. Introdu codul (5 min):" │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 6. User: 123456 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 7. Bot: │
│ - Validează cod în email_auth_codes │
│ - Verifică expiry (5 minute) │
│ - Marchează cod ca "used" │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 8. Bot: "🔐 Introdu parola ta Oracle:" │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 9. User: parola_mea (mesaj va fi șters automat) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 10. Bot → Backend: │
│ POST /api/telegram/auth/login-with-email │
│ { │
│ "email": "mmarius28@gmail.com", │
│ "password": "parola_mea", │
│ "telegram_user_id": 123456789 │
│ } │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 11. Backend: │
│ - Găsește username din email în UTILIZATORI │
│ - Verifică parolă: pack_drepturi.verificautilizator() │
│ - Generează JWT tokens (access + refresh) │
│ - Returnează tokens + user data │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 12. Bot: │
│ - Salvează JWT în telegram_users table │
│ - Linkează telegram_user_id cu oracle_username │
│ - Șterge mesajele cu parolă │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 13. Bot: "✅ Autentificat cu succes! Folosește /help" │
└─────────────────────────────────────────────────────────────┘
```
### Conversation States (ConversationHandler)
```python
AWAITING_EMAIL = 1 # Așteaptă email de la user
AWAITING_CODE = 2 # Așteaptă cod din email
AWAITING_PASSWORD = 3 # Așteaptă parolă Oracle
```
---
## 📦 Componente Noi (Arhitectură)
### 1. Email Service (SMTP Client)
**Fișier NOU**: `reports-app/telegram-bot/app/utils/email_service.py`
```python
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import os
class EmailService:
def __init__(self):
self.smtp_host = os.getenv("SMTP_HOST", "mail.romfast.ro")
self.smtp_port = int(os.getenv("SMTP_PORT", "587"))
self.smtp_user = os.getenv("SMTP_USER", "ups@romfast.ro")
self.smtp_password = os.getenv("SMTP_PASSWORD", "#Ups2020#")
self.from_email = os.getenv("SMTP_FROM_EMAIL", "ups@romfast.ro")
self.from_name = os.getenv("SMTP_FROM_NAME", "ROA2WEB")
async def send_auth_code(self, to_email: str, code: str, username: str) -> bool:
"""
Trimite cod de autentificare pe email
Returns: True dacă email trimis cu succes
"""
subject = "Codul tău de autentificare ROA2WEB"
html_body = f"""
<html>
<body style="font-family: Arial, sans-serif;">
<h2>🔐 Autentificare Telegram Bot</h2>
<p>Salut <strong>{username}</strong>,</p>
<p>Codul tău de autentificare este:</p>
<h1 style="background-color: #f0f0f0; padding: 20px; text-align: center; letter-spacing: 5px;">
{code}
</h1>
<p><strong>Codul expiră în 5 minute.</strong></p>
<p>Dacă nu ai solicitat acest cod, te rugăm să ignori acest email.</p>
<hr>
<p style="color: #666; font-size: 12px;">
ROA2WEB - ERP Reports Application
</p>
</body>
</html>
"""
# Implementare SMTP cu error handling
```
**Funcții:**
- `send_auth_code(email, code, username)` - trimite cod pe email
- `_send_email(to, subject, html_body)` - helper SMTP generic
- Error handling cu retry logic (3 încercări)
---
### 2. Tabela SQLite Nouă: `email_auth_codes`
**Locație**: `reports-app/telegram-bot/app/db/database.py`
**Schema SQL:**
```sql
CREATE TABLE IF NOT EXISTS email_auth_codes (
code TEXT PRIMARY KEY, -- 6-digit numeric code (e.g., "123456")
email TEXT NOT NULL, -- Email utilizator din Oracle
oracle_username TEXT NOT NULL, -- Username Oracle asociat
telegram_user_id INTEGER NOT NULL, -- Telegram user ID care a solicitat
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL, -- Current time + 5 minute
used BOOLEAN DEFAULT 0, -- 0 = nefolosit, 1 = folosit
used_at TIMESTAMP, -- Timestamp când a fost folosit
-- Index pentru query performance
INDEX idx_email (email),
INDEX idx_expires_at (expires_at),
INDEX idx_telegram_user_id (telegram_user_id)
);
```
**Operații CRUD** (în `app/db/operations.py`):
```python
async def create_email_auth_code(code: str, email: str, username: str, telegram_user_id: int) -> bool
async def get_email_auth_code(code: str) -> Optional[Dict]
async def mark_email_code_used(code: str) -> bool
async def delete_expired_email_codes() -> int
async def get_pending_email_code_for_user(telegram_user_id: int) -> Optional[Dict]
```
**Caracteristici:**
- Cod 6-digit random numeric (000000 - 999999)
- Expirare 5 minute
- One-time use (marcat ca `used=1`)
- Auto-cleanup de către job-ul existent (hourly)
---
### 3. Backend Endpoint NOU: `POST /api/telegram/auth/login-with-email`
**Locație**: `reports-app/backend/app/routers/telegram.py`
**Request Schema:**
```python
class TelegramEmailLoginRequest(BaseModel):
email: EmailStr
password: str
telegram_user_id: int
class TelegramEmailLoginResponse(BaseModel):
success: bool
access_token: str
refresh_token: str
token_type: str = "bearer"
user_id: int
username: str
companies: List[CompanyInfo]
message: str
```
**Endpoint Logic:**
```python
@router.post("/auth/login-with-email", response_model=TelegramEmailLoginResponse)
async def login_with_email(request: TelegramEmailLoginRequest):
"""
Autentificare Telegram prin email + parolă
Flow:
1. Caută username în Oracle UTILIZATORI by email
2. Verifică parolă prin pack_drepturi.verificautilizator(username, password)
3. Dacă valid: generează JWT tokens
4. Returnează tokens + user data
"""
async with oracle_pool.get_connection() as connection:
# 1. Find username by email
cursor = connection.cursor()
cursor.execute("""
SELECT ID_UTIL, UTILIZATOR
FROM CONTAFIN_ORACLE.UTILIZATORI
WHERE UPPER(EMAIL) = UPPER(:email)
""", {"email": request.email})
user_row = cursor.fetchone()
if not user_row:
raise HTTPException(status_code=404, detail="Email not found")
user_id, username = user_row
# 2. Verify password via stored procedure
cursor.execute("""
SELECT pack_drepturi.verificautilizator(:username, :password)
FROM DUAL
""", {"username": username, "password": request.password})
result = cursor.fetchone()[0]
if result == -1:
raise HTTPException(status_code=401, detail="Invalid password")
# 3. Get user companies
companies = get_user_companies(user_id, connection)
# 4. Generate JWT tokens
access_token = create_access_token(...)
refresh_token = create_refresh_token(...)
return TelegramEmailLoginResponse(
success=True,
access_token=access_token,
refresh_token=refresh_token,
user_id=user_id,
username=username,
companies=companies,
message="Authentication successful"
)
```
**Securitate:**
- Rate limiting: 5 requests / 5 minutes per telegram_user_id
- Password validation prin Oracle stored procedure (nu stocăm parole)
- HTTPS required în producție
- Logging pentru failed attempts
---
### 4. Bot Handlers NOI: `/login` + ConversationHandler
**Fișier NOU**: `reports-app/telegram-bot/app/bot/email_handlers.py`
#### Command: `/login`
```python
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import ContextTypes, ConversationHandler, CommandHandler, MessageHandler, filters
# Conversation states
AWAITING_EMAIL, AWAITING_CODE, AWAITING_PASSWORD = range(3)
async def login_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""
Handler pentru /login command
Inițiază conversation pentru email auth
"""
user = update.effective_user
# Check dacă e deja autentificat
if await is_user_authenticated(user.id):
await update.message.reply_text(
"✅ Ești deja autentificat!\n"
"Folosește /unlink pentru a te deautentifica."
)
return ConversationHandler.END
# Afișează metode de autentificare
keyboard = [
[InlineKeyboardButton("🔐 Login cu Email", callback_data="email_login")],
[InlineKeyboardButton("🌐 Login din Web App", callback_data="web_login")],
[InlineKeyboardButton("❌ Anulează", callback_data="cancel")]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(
"🔑 Alege metoda de autentificare:\n\n"
"📧 **Email + Parolă**: Primești cod pe email, apoi introduci parola\n"
"🌐 **Web App**: Generează cod din aplicația web",
reply_markup=reply_markup
)
return AWAITING_EMAIL
async def email_login_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Callback pentru butonul 'Login cu Email'"""
query = update.callback_query
await query.answer()
await query.edit_message_text(
"📧 **Autentificare prin Email**\n\n"
"Te rugăm să introduci adresa ta de email Oracle:\n"
"(Exemplu: nume.prenume@companie.ro)"
)
return AWAITING_EMAIL
async def receive_email(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""
Handler pentru primirea email-ului
"""
email = update.message.text.strip()
user_id = update.effective_user.id
# Validare format email
if not is_valid_email(email):
await update.message.reply_text(
"❌ Email invalid. Te rugăm să introduci o adresă de email validă."
)
return AWAITING_EMAIL
# Verifică email în Oracle + trimite cod
try:
# 1. Verifică email în Oracle UTILIZATORI
username = await verify_email_in_oracle(email)
if not username:
await update.message.reply_text(
"❌ Email-ul nu este înregistrat în sistem.\n"
"Contactează administratorul pentru a-ți adăuga email-ul."
)
return ConversationHandler.END
# 2. Generează cod 6-digit
code = generate_6digit_code()
# 3. Salvează în email_auth_codes
await save_email_auth_code(
code=code,
email=email,
username=username,
telegram_user_id=user_id
)
# 4. Trimite email
email_sent = await email_service.send_auth_code(email, code, username)
if not email_sent:
await update.message.reply_text(
"❌ Eroare la trimiterea email-ului. Te rugăm să încerci din nou."
)
return ConversationHandler.END
# 5. Salvează email în context
context.user_data['pending_email'] = email
context.user_data['pending_username'] = username
await update.message.reply_text(
f"✉️ **Cod trimis pe {email}**\n\n"
"Verifică inbox-ul (și spam) și introdu codul de 6 cifre.\n"
"⏱ Codul expiră în **5 minute**.\n\n"
"Scrie /cancel pentru a anula."
)
return AWAITING_CODE
except Exception as e:
logger.error(f"Email login error: {e}")
await update.message.reply_text(
"❌ Eroare internă. Te rugăm să încerci din nou mai târziu."
)
return ConversationHandler.END
async def receive_code(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""
Handler pentru primirea codului din email
"""
code = update.message.text.strip()
user_id = update.effective_user.id
# Validare format cod (6 digits)
if not code.isdigit() or len(code) != 6:
await update.message.reply_text(
"❌ Cod invalid. Introdu cele 6 cifre din email."
)
return AWAITING_CODE
# Verifică cod în DB
try:
code_data = await get_email_auth_code(code)
if not code_data:
await update.message.reply_text(
"❌ Cod invalid sau expirat. Te rugăm să reîncepi cu /login"
)
return ConversationHandler.END
# Verificări
if code_data['used']:
await update.message.reply_text(
"❌ Cod deja folosit. Te rugăm să reîncepi cu /login"
)
return ConversationHandler.END
if code_data['telegram_user_id'] != user_id:
await update.message.reply_text(
"❌ Codul nu îți aparține."
)
return ConversationHandler.END
if datetime.now() > code_data['expires_at']:
await update.message.reply_text(
"❌ Codul a expirat. Te rugăm să reîncepi cu /login"
)
return ConversationHandler.END
# Marchează cod ca folosit
await mark_email_code_used(code)
# Salvează username în context
context.user_data['verified_username'] = code_data['oracle_username']
context.user_data['verified_email'] = code_data['email']
await update.message.reply_text(
"✅ **Cod validat cu succes!**\n\n"
"🔐 Acum introdu parola ta Oracle:\n"
"(Parola va fi ștearsă automat după verificare)"
)
return AWAITING_PASSWORD
except Exception as e:
logger.error(f"Code validation error: {e}")
await update.message.reply_text(
"❌ Eroare la validarea codului. Te rugăm să încerci din nou."
)
return ConversationHandler.END
async def receive_password(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""
Handler pentru primirea parolei
"""
password = update.message.text.strip()
user_id = update.effective_user.id
# Șterge imediat mesajul cu parola
try:
await update.message.delete()
except:
pass
username = context.user_data.get('verified_username')
email = context.user_data.get('verified_email')
if not username or not email:
await update.effective_chat.send_message(
"❌ Sesiune expirată. Te rugăm să reîncepi cu /login"
)
return ConversationHandler.END
# Trimite loading message
loading_msg = await update.effective_chat.send_message(
"⏳ Verificare credențiale..."
)
try:
# Call backend endpoint
response = await backend_client.login_with_email(
email=email,
password=password,
telegram_user_id=user_id
)
if not response['success']:
await loading_msg.edit_text(
"❌ Parolă incorectă. Te rugăm să reîncepi cu /login"
)
return ConversationHandler.END
# Salvează user în telegram_users
await save_telegram_user(
telegram_user_id=user_id,
username=update.effective_user.username,
first_name=update.effective_user.first_name,
last_name=update.effective_user.last_name,
oracle_username=response['username'],
jwt_token=response['access_token'],
jwt_refresh_token=response['refresh_token']
)
# Success message
companies_list = "\n".join([f"• {c['name']}" for c in response['companies'][:5]])
await loading_msg.edit_text(
f"✅ **Autentificat cu succes!**\n\n"
f"👤 Utilizator: **{response['username']}**\n"
f"🏢 Companii disponibile: {len(response['companies'])}\n\n"
f"{companies_list}\n\n"
f"Folosește /help pentru comenzi disponibile."
)
# Clear context
context.user_data.clear()
return ConversationHandler.END
except Exception as e:
logger.error(f"Password verification error: {e}")
await loading_msg.edit_text(
"❌ Eroare la autentificare. Te rugăm să încerci din nou cu /login"
)
return ConversationHandler.END
async def cancel_login(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Cancel conversation"""
context.user_data.clear()
await update.message.reply_text("❌ Autentificare anulată.")
return ConversationHandler.END
# ConversationHandler setup
email_login_handler = ConversationHandler(
entry_points=[
CommandHandler('login', login_command),
CallbackQueryHandler(email_login_callback, pattern='^email_login$')
],
states={
AWAITING_EMAIL: [MessageHandler(filters.TEXT & ~filters.COMMAND, receive_email)],
AWAITING_CODE: [MessageHandler(filters.TEXT & ~filters.COMMAND, receive_code)],
AWAITING_PASSWORD: [MessageHandler(filters.TEXT & ~filters.COMMAND, receive_password)],
},
fallbacks=[
CommandHandler('cancel', cancel_login),
CallbackQueryHandler(cancel_login, pattern='^cancel$')
],
conversation_timeout=300 # 5 minute timeout
)
```
**Handler Registration** (în `app/bot/handlers.py`):
```python
from app.bot.email_handlers import email_login_handler
def setup_handlers(application):
# ... existing handlers ...
# Email login handler
application.add_handler(email_login_handler)
```
---
### 5. Backend API Client Method
**Locație**: `reports-app/telegram-bot/app/api/client.py`
```python
class BackendAPIClient:
# ... existing methods ...
async def login_with_email(
self,
email: str,
password: str,
telegram_user_id: int
) -> dict:
"""
Login via email + password
"""
try:
response = await self.client.post(
f"{self.base_url}/api/telegram/auth/login-with-email",
json={
"email": email,
"password": password,
"telegram_user_id": telegram_user_id
}
)
response.raise_for_status()
return response.json()
except httpx.HTTPError as e:
logger.error(f"Email login failed: {e}")
raise
```
---
## 🔧 Environment Variables
### Backend `.env` (nu necesită modificări - doar pentru referință)
```bash
# Existente (nu modificăm)
ORACLE_USER=CONTAFIN_ORACLE
ORACLE_PASSWORD=your_password
ORACLE_HOST=localhost
ORACLE_PORT=1526
ORACLE_SID=ROA
JWT_SECRET_KEY=your_secret_key
JWT_ALGORITHM=HS256
JWT_EXPIRE_MINUTES=30
```
### Telegram Bot `.env` (ADAUGĂ ACESTEA)
```bash
# Existing
TELEGRAM_BOT_TOKEN=your_bot_token
BACKEND_URL=http://localhost:8000
DATABASE_PATH=data/telegram_bot.db
# NEW: SMTP Configuration
SMTP_HOST=mail.romfast.ro
SMTP_PORT=587
SMTP_USER=ups@romfast.ro
SMTP_PASSWORD=#Ups2020#
SMTP_FROM_EMAIL=ups@romfast.ro
SMTP_FROM_NAME=ROA2WEB
SMTP_USE_TLS=true
# NEW: Email Auth Settings
EMAIL_CODE_EXPIRY_MINUTES=5
EMAIL_CODE_LENGTH=6
MAX_EMAIL_ATTEMPTS_PER_HOUR=3
```
---
## 🔒 Securitate & Validări
### 1. Email Validation
```python
import re
def is_valid_email(email: str) -> bool:
"""Validare format email"""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
async def verify_email_in_oracle(email: str) -> Optional[str]:
"""
Verifică dacă email există în Oracle UTILIZATORI
Returns: username dacă există, None altfel
"""
async with oracle_pool.get_connection() as connection:
cursor = connection.cursor()
cursor.execute("""
SELECT UTILIZATOR
FROM CONTAFIN_ORACLE.UTILIZATORI
WHERE UPPER(EMAIL) = UPPER(:email)
AND ACTIV = 1
""", {"email": email})
row = cursor.fetchone()
return row[0] if row else None
```
### 2. Code Generation & Storage
```python
import random
import string
from datetime import datetime, timedelta
def generate_6digit_code() -> str:
"""Generează cod random 6-digit"""
return ''.join(random.choices(string.digits, k=6))
async def save_email_auth_code(
code: str,
email: str,
username: str,
telegram_user_id: int
) -> bool:
"""Salvează cod în DB cu expiry 5 minute"""
expires_at = datetime.now() + timedelta(minutes=5)
async with get_db_connection() as db:
await db.execute("""
INSERT INTO email_auth_codes
(code, email, oracle_username, telegram_user_id, expires_at)
VALUES (?, ?, ?, ?, ?)
""", (code, email, username, telegram_user_id, expires_at))
await db.commit()
return True
```
### 3. Rate Limiting
```python
from collections import defaultdict
from datetime import datetime, timedelta
# In-memory rate limiter (sau Redis în producție)
email_attempts = defaultdict(list)
async def check_rate_limit(identifier: str, max_attempts: int = 3, window_minutes: int = 60) -> bool:
"""
Rate limiting pentru email requests
identifier poate fi: email sau telegram_user_id
"""
now = datetime.now()
cutoff = now - timedelta(minutes=window_minutes)
# Curăță attempts vechi
email_attempts[identifier] = [
attempt for attempt in email_attempts[identifier]
if attempt > cutoff
]
# Verifică limita
if len(email_attempts[identifier]) >= max_attempts:
return False # Rate limit exceeded
# Adaugă attempt nou
email_attempts[identifier].append(now)
return True # OK to proceed
```
### 4. Password Security
```python
async def receive_password(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handler cu securitate mărite pentru parolă"""
password = update.message.text.strip()
# 1. Șterge IMEDIAT mesajul cu parola
try:
await update.message.delete()
except Exception as e:
logger.warning(f"Could not delete password message: {e}")
# 2. NU loga parola niciodată
logger.info(f"Password received for user {update.effective_user.id}")
# 3. Verifică parola prin backend (Oracle)
# ... (vezi cod de mai sus)
# 4. Șterge parola din memorie
del password
```
### 5. Auto-Cleanup Job
```python
async def cleanup_expired_codes():
"""Job periodic pentru curățarea codurilor expirate"""
while True:
try:
async with get_db_connection() as db:
# Șterge email codes expirate
await db.execute("""
DELETE FROM email_auth_codes
WHERE expires_at < ?
OR (used = 1 AND used_at < ?)
""", (
datetime.now(),
datetime.now() - timedelta(days=1) # Cleanup used codes după 1 zi
))
await db.commit()
deleted = db.total_changes
logger.info(f"Cleaned up {deleted} expired email auth codes")
except Exception as e:
logger.error(f"Cleanup job error: {e}")
# Run every hour
await asyncio.sleep(3600)
# Start cleanup job în main.py
asyncio.create_task(cleanup_expired_codes())
```
---
## 📝 Fișiere Modificate/Create
### **CREATE** (6 fișiere noi):
1. **`reports-app/telegram-bot/app/utils/email_service.py`**
- SMTP client pentru trimitere email-uri
- Funcție: `send_auth_code(email, code, username)`
- Error handling + retry logic
2. **`reports-app/telegram-bot/app/auth/email_auth.py`**
- Logică autentificare email
- Funcții: `verify_email_in_oracle()`, `generate_6digit_code()`, `check_rate_limit()`
3. **`reports-app/telegram-bot/app/bot/email_handlers.py`**
- ConversationHandler pentru `/login`
- States: AWAITING_EMAIL → AWAITING_CODE → AWAITING_PASSWORD
- Handlers: `receive_email()`, `receive_code()`, `receive_password()`
4. **`reports-app/backend/app/schemas/telegram_email_auth.py`**
- Pydantic schemas pentru email auth
- `TelegramEmailLoginRequest`, `TelegramEmailLoginResponse`
5. **`reports-app/telegram-bot/tests/test_email_auth.py`**
- Unit tests pentru email auth flow
- Mock SMTP, Oracle, backend API
6. **`TELEGRAM_EMAIL_AUTH_PLAN.md`**
- Acest document (plan detaliat)
### **MODIFY** (6 fișiere existente):
1. **`reports-app/telegram-bot/app/db/database.py`**
- **Adaugă**: Schema tabela `email_auth_codes`
- **Linie**: ~40-60 (în funcția `init_database()`)
2. **`reports-app/telegram-bot/app/db/operations.py`**
- **Adaugă**: CRUD operations pentru `email_auth_codes`
- Funcții noi: `create_email_auth_code()`, `get_email_auth_code()`, `mark_email_code_used()`, etc.
- **Linii**: ~200-300 (la final de fișier)
3. **`reports-app/telegram-bot/app/bot/handlers.py`**
- **Adaugă**: Import și register `email_login_handler`
- **Linie**: ~10 (import), ~150 (register în `setup_handlers()`)
- **Modificare**: Update `/start` message pentru a menționa `/login`
4. **`reports-app/backend/app/routers/telegram.py`**
- **Adaugă**: Endpoint `POST /auth/login-with-email`
- **Linii**: ~200-300 (la final de fișier)
- **Import**: Adaugă schema `TelegramEmailLoginRequest/Response`
5. **`reports-app/telegram-bot/app/api/client.py`**
- **Adaugă**: Method `login_with_email(email, password, telegram_user_id)`
- **Linii**: ~150-180 (în clasa `BackendAPIClient`)
6. **`reports-app/telegram-bot/.env`**
- **Adaugă**: SMTP configuration variables (vezi secțiunea Environment Variables)
### **NO TOUCH** (rămân 100% neschimbate):
- ✅ Fluxul actual de linking (web app → cod → /start)
- ✅ Tabele existente: `telegram_users`, `telegram_auth_codes`, `telegram_sessions`
- ✅ Endpoint-uri existente: `/generate-code`, `/verify-user`, `/refresh-token`
- ✅ Handler-e existente: `/start [code]`, `/companies`, `/dashboard`, `/facturi`, etc.
- ✅ Toate comenzile Telegram existente (funcționalitate păstrată 100%)
---
## 🧪 Testing Strategy
### 1. Unit Tests
**Fișier**: `reports-app/telegram-bot/tests/test_email_auth.py`
```python
import pytest
from unittest.mock import AsyncMock, patch
from app.auth.email_auth import verify_email_in_oracle, generate_6digit_code, check_rate_limit
from app.utils.email_service import EmailService
@pytest.mark.asyncio
async def test_generate_6digit_code():
"""Test generare cod 6-digit"""
code = generate_6digit_code()
assert len(code) == 6
assert code.isdigit()
@pytest.mark.asyncio
async def test_verify_email_in_oracle_success():
"""Test verificare email valid în Oracle"""
with patch('app.auth.email_auth.oracle_pool') as mock_pool:
# Mock Oracle response
mock_cursor = AsyncMock()
mock_cursor.fetchone.return_value = ("test_user",)
username = await verify_email_in_oracle("test@example.com")
assert username == "test_user"
@pytest.mark.asyncio
async def test_verify_email_not_found():
"""Test email inexistent în Oracle"""
with patch('app.auth.email_auth.oracle_pool') as mock_pool:
mock_cursor = AsyncMock()
mock_cursor.fetchone.return_value = None
username = await verify_email_in_oracle("notfound@example.com")
assert username is None
@pytest.mark.asyncio
async def test_rate_limiting():
"""Test rate limiting pentru email requests"""
identifier = "test_user_123"
# First 3 attempts should pass
for i in range(3):
result = await check_rate_limit(identifier, max_attempts=3, window_minutes=60)
assert result is True
# 4th attempt should fail
result = await check_rate_limit(identifier, max_attempts=3, window_minutes=60)
assert result is False
@pytest.mark.asyncio
async def test_email_service_send_code():
"""Test trimitere email cu cod"""
email_service = EmailService()
with patch('app.utils.email_service.smtplib.SMTP') as mock_smtp:
result = await email_service.send_auth_code(
to_email="test@example.com",
code="123456",
username="test_user"
)
assert result is True
assert mock_smtp.called
```
### 2. Integration Tests
```python
@pytest.mark.asyncio
async def test_full_email_auth_flow():
"""Test complet: email → cod → parolă → JWT"""
# 1. Request email code
email = "mmarius28@gmail.com"
telegram_user_id = 123456789
with patch('app.auth.email_auth.verify_email_in_oracle') as mock_verify:
mock_verify.return_value = "marius_user"
# Generate code
code = generate_6digit_code()
await save_email_auth_code(code, email, "marius_user", telegram_user_id)
# 2. Validate code
code_data = await get_email_auth_code(code)
assert code_data is not None
assert code_data['email'] == email
assert code_data['used'] is False
# 3. Mock backend login
with patch('app.api.client.BackendAPIClient.login_with_email') as mock_login:
mock_login.return_value = {
'success': True,
'access_token': 'test_jwt_token',
'refresh_token': 'test_refresh_token',
'username': 'marius_user',
'user_id': 42,
'companies': []
}
# Verify password
response = await backend_client.login_with_email(
email=email,
password="test_password",
telegram_user_id=telegram_user_id
)
assert response['success'] is True
assert 'access_token' in response
```
### 3. Manual Testing Checklist
**Fișier**: `reports-app/telegram-bot/tests/MANUAL_EMAIL_AUTH_TESTING.md`
```markdown
# Manual Testing Checklist - Email Authentication
## Prerequisites
- [ ] Backend running on port 8000
- [ ] Telegram bot running
- [ ] SMTP credentials configured in .env
- [ ] Test email: mmarius28@gmail.com exists in Oracle UTILIZATORI table
## Test Cases
### 1. Successful Authentication Flow
- [ ] Start bot: `/start`
- [ ] Type: `/login`
- [ ] Select "🔐 Login cu Email"
- [ ] Enter email: `mmarius28@gmail.com`
- [ ] Verify email received with 6-digit code
- [ ] Enter code from email
- [ ] Enter Oracle password
- [ ] Verify success message with companies list
- [ ] Test command: `/companies` (should work)
### 2. Invalid Email
- [ ] `/login`
- [ ] Enter email: `nonexistent@example.com`
- [ ] Verify error message: "Email-ul nu este înregistrat"
### 3. Expired Code
- [ ] `/login` → enter valid email
- [ ] Wait 6 minutes (code expiry = 5 min)
- [ ] Enter code
- [ ] Verify error: "Cod expirat"
### 4. Wrong Code
- [ ] `/login` → enter valid email
- [ ] Enter wrong code: `999999`
- [ ] Verify error: "Cod invalid"
### 5. Wrong Password
- [ ] `/login` → enter email → enter valid code
- [ ] Enter wrong password
- [ ] Verify error: "Parolă incorectă"
### 6. Rate Limiting
- [ ] `/login` → enter email (attempt 1)
- [ ] `/cancel`
- [ ] `/login` → enter email (attempt 2)
- [ ] `/cancel`
- [ ] `/login` → enter email (attempt 3)
- [ ] `/cancel`
- [ ] `/login` → enter email (attempt 4)
- [ ] Verify error: "Prea multe încercări. Încearcă în 60 minute"
### 7. Password Message Deletion
- [ ] `/login` → enter email → enter code
- [ ] Enter password
- [ ] Verify password message is deleted immediately
- [ ] Check chat history - password should not be visible
### 8. Cancel Flow
- [ ] `/login`
- [ ] `/cancel` (should cancel and clear state)
### 9. Already Authenticated
- [ ] Complete successful login
- [ ] Type `/login` again
- [ ] Verify message: "Ești deja autentificat"
### 10. Parallel Method (Web App Linking) Still Works
- [ ] Login to web app: http://localhost:3000
- [ ] Generate Telegram linking code
- [ ] Use `/start ABC123XY` in Telegram
- [ ] Verify linking works as before (OLD method)
### 11. Database Cleanup
- [ ] Create expired codes (manually set expires_at in past)
- [ ] Wait for hourly cleanup job
- [ ] Verify expired codes deleted from `email_auth_codes` table
```
---
## 📊 Estimare Efort & Timeline
### Breakdown Detaliat
| Componentă | Efort (ore) | Detalii |
|-----------|-------------|---------|
| **Backend** | | |
| → Endpoint `/login-with-email` | 1.5h | Logic + Oracle queries + JWT |
| → Schema Pydantic | 0.5h | Request/Response models |
| → Rate limiting | 0.5h | Middleware sau decorator |
| → Testing backend | 0.5h | Unit tests endpoint |
| **Telegram Bot** | | |
| → Email service (SMTP) | 1.5h | Client SMTP + templates + retry |
| → DB schema + operations | 1h | Tabela nouă + CRUD |
| → Email auth logic | 1h | Verificare email, cod generation |
| → ConversationHandler | 2h | States + handlers + callbacks |
| → API client method | 0.5h | Method nou în BackendAPIClient |
| → Testing bot | 1h | Unit + integration tests |
| **Integration** | | |
| → End-to-end testing | 1h | Flow complet manual |
| → Bug fixes | 1h | Edge cases |
| **Documentation** | | |
| → Update README | 0.5h | Secțiune nouă pentru email auth |
| → Update TELEGRAM_COMMANDS.md | 0.5h | Documentare `/login` |
| **TOTAL** | **~13h** | ~2 zile lucru (6-7h/zi) |
### Timeline Recomandat
**Ziua 1 (6-7h):**
1. Setup environment (SMTP config) - 0.5h
2. Backend: Endpoint + schema - 2h
3. Telegram Bot: Email service - 1.5h
4. Telegram Bot: DB schema + operations - 1h
5. Testing preliminar - 1h
**Ziua 2 (6-7h):**
1. Telegram Bot: ConversationHandler - 2h
2. Email auth logic - 1h
3. Integration - 1h
4. End-to-end testing - 1.5h
5. Documentation - 0.5h
6. Bug fixes - 1h
---
## ✅ Avantaje Plan
| Avantaj | Detalii |
|---------|---------|
| ✅ **Minimal Invaziv** | 0 modificări la logica existentă, doar adăugiri |
| ✅ **Ambele Metode** | Users pot alege: email+parolă SAU web app linking |
| ✅ **Backward Compatible** | Utilizatori fără email folosesc metoda actuală |
| ✅ **2FA Real** | Email possession (cod) + parolă Oracle = 2 factori |
| ✅ **Simplu de Testat** | Un singur utilizator cu email (mmarius28@gmail.com) |
| ✅ **Extensibil** | Ușor de activat pentru alți useri când adaugi email-uri |
| ✅ **Securitate Mărite** | Rate limiting, code expiry, password verification în Oracle |
| ✅ **Auto-Cleanup** | Job existent curăță automat coduri expirate |
| ✅ **User-Friendly** | Conversation flow natural în Telegram |
| ✅ **Error Handling** | Mesaje clare pentru fiecare caz de eroare |
---
## 🚀 Ordine Implementare (Step-by-Step)
### **Step 1: Environment Setup** (15 min)
1. Adaugă SMTP credentials în `.env`:
```bash
cd reports-app/telegram-bot/
echo "SMTP_HOST=mail.romfast.ro" >> .env
echo "SMTP_PORT=587" >> .env
echo "SMTP_USER=ups@romfast.ro" >> .env
echo "SMTP_PASSWORD=#Ups2020#" >> .env
echo "SMTP_FROM_EMAIL=ups@romfast.ro" >> .env
echo "SMTP_FROM_NAME=ROA2WEB" >> .env
```
2. Install dependencies:
```bash
pip install aiosmtplib # Async SMTP client
```
### **Step 2: Backend - Endpoint** (2h)
1. Create schema: `backend/app/schemas/telegram_email_auth.py`
2. Add endpoint în `backend/app/routers/telegram.py`
3. Test cu Postman/curl
### **Step 3: Telegram Bot - Infrastructure** (2.5h)
1. Create `app/utils/email_service.py` (SMTP client)
2. Update `app/db/database.py` (add `email_auth_codes` table)
3. Update `app/db/operations.py` (CRUD operations)
4. Test DB operations
### **Step 4: Telegram Bot - Auth Logic** (1h)
1. Create `app/auth/email_auth.py`
2. Implement: `verify_email_in_oracle()`, `generate_6digit_code()`, `check_rate_limit()`
3. Unit tests
### **Step 5: Telegram Bot - Handlers** (2h)
1. Create `app/bot/email_handlers.py`
2. Implement ConversationHandler
3. Register în `app/bot/handlers.py`
### **Step 6: Backend API Client** (30 min)
1. Add `login_with_email()` method în `app/api/client.py`
### **Step 7: Integration Testing** (1h)
1. Start backend + bot
2. Test flow complet cu mmarius28@gmail.com
3. Fix bugs
### **Step 8: Comprehensive Testing** (1.5h)
1. Run all test cases from checklist
2. Test edge cases
3. Verify both methods work in parallel
### **Step 9: Documentation** (30 min)
1. Update README.md
2. Update TELEGRAM_COMMANDS.md
3. Create MANUAL_EMAIL_AUTH_TESTING.md
---
## 🐛 Troubleshooting Guide
### Issue: Email nu ajunge
**Cauze posibile:**
1. SMTP credentials incorecte
2. Port blocat (587)
3. Email în spam
**Debugging:**
```python
# Test SMTP connection
import smtplib
try:
server = smtplib.SMTP('mail.romfast.ro', 587)
server.starttls()
server.login('ups@romfast.ro', '#Ups2020#')
print("✅ SMTP connection OK")
server.quit()
except Exception as e:
print(f"❌ SMTP error: {e}")
```
### Issue: Cod invalid/expirat
**Debugging:**
```sql
-- Check code in DB
SELECT * FROM email_auth_codes WHERE code = '123456';
-- Check expiry
SELECT code, expires_at,
datetime('now') as now,
(julianday(expires_at) - julianday('now')) * 24 * 60 as minutes_remaining
FROM email_auth_codes
WHERE code = '123456';
```
### Issue: Parolă incorectă (dar știu că e corectă)
**Cauze posibile:**
1. Username găsit greșit din email
2. Oracle stored procedure error
**Debugging:**
```python
# Test Oracle auth direct
async with oracle_pool.get_connection() as connection:
cursor = connection.cursor()
# 1. Check email → username mapping
cursor.execute("""
SELECT UTILIZATOR, EMAIL
FROM CONTAFIN_ORACLE.UTILIZATORI
WHERE UPPER(EMAIL) = UPPER('mmarius28@gmail.com')
""")
print(cursor.fetchone())
# 2. Test stored procedure
cursor.execute("""
SELECT pack_drepturi.verificautilizator('marius_user', 'parola_mea')
FROM DUAL
""")
result = cursor.fetchone()[0]
print(f"Auth result: {result}") # -1 = fail, user_id = success
```
### Issue: Rate limiting prea restrictiv
**Quick fix:**
```python
# În email_auth.py, increase limits
MAX_EMAIL_ATTEMPTS = 5 # was 3
RATE_LIMIT_WINDOW_MINUTES = 30 # was 60
```
---
## 📚 Additional Documentation
### Email Template (HTML)
```html
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; border-radius: 10px 10px 0 0; }
.code-box { background-color: #f4f4f4; border: 2px solid #667eea; padding: 30px; margin: 20px 0; text-align: center; border-radius: 10px; }
.code { font-size: 36px; font-weight: bold; letter-spacing: 10px; color: #667eea; font-family: 'Courier New', monospace; }
.content { padding: 20px; background: white; }
.warning { background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0; }
.footer { text-align: center; color: #666; font-size: 12px; padding: 20px; border-top: 1px solid #ddd; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔐 ROA2WEB</h1>
<p>Autentificare Telegram Bot</p>
</div>
<div class="content">
<p>Salut <strong>{{username}}</strong>,</p>
<p>Ai solicitat autentificarea în aplicația ROA2WEB Telegram Bot.</p>
<div class="code-box">
<p style="margin: 0; font-size: 14px; color: #666;">Codul tău de autentificare:</p>
<p class="code">{{code}}</p>
</div>
<div class="warning">
<strong>⏱️ Important:</strong> Acest cod expiră în <strong>5 minute</strong>.
</div>
<p>Introdu acest cod în conversația cu Telegram Bot, apoi vei fi solicitat să introduci parola.</p>
<p style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd;">
<strong>Nu ai solicitat acest cod?</strong><br>
Dacă nu ai inițiat această autentificare, te rugăm să ignori acest email.
Nimeni nu va avea acces la contul tău fără parola ta.
</p>
</div>
<div class="footer">
<p><strong>ROA2WEB</strong> - ERP Reports Application</p>
<p>Acest email a fost trimis automat. Te rugăm să nu răspunzi.</p>
</div>
</div>
</body>
</html>
```
### Command Reference Update
Adaugă în `TELEGRAM_COMMANDS.md`:
```markdown
## /login
**Descriere**: Autentificare în Telegram Bot prin email + parolă (2FA)
**Sintaxă**: `/login`
**Flow**:
1. Selectezi metoda de autentificare (Email sau Web App)
2. Pentru Email:
- Introduci adresa de email Oracle
- Primești cod pe email (6 cifre)
- Introduci codul din email
- Introduci parola Oracle
- Primești confirmare și acces la comenzi
**Exemple**:
```
/login
→ Selectează "🔐 Login cu Email"
→ Introdu: mmarius28@gmail.com
→ Verifică email și introdu cod: 123456
→ Introdu parola
→ ✅ Autentificat cu succes!
```
**Securitate**:
- Cod expiră în 5 minute
- Cod utilizabil o singură dată
- Parolă verificată în Oracle
- Mesajul cu parola este șters automat
- Rate limiting: max 3 încercări/oră
**Comandă alternativă**: Buton "🔐 Login cu Email" în meniu principal
```
---
## 🎓 Learning Points & Best Practices
### 1. De ce 6-digit code în loc de 8-char alphanumeric?
**Răspuns**:
- Mai ușor de tastat pe mobile
- Mai puțin prone la erori (0/O, 1/I confusion)
- Suficient de securizat pentru 5 minute expiry
- Standard în industrie (Google, GitHub, etc.)
### 2. De ce 5 minute expiry pentru cod?
**Răspuns**:
- Balans între security și UX
- Suficient timp pentru check email + type code
- Redus attack window pentru code guessing
- Similar cu alte aplicații 2FA
### 3. De ce șterge mesajul cu parola?
**Răspuns**:
- Securitate: parola nu rămâne în chat history
- Telegram API permite ștergerea mesajelor
- Best practice în bot-uri care cer credențiale
- Protecție dacă telefonul e compromis
### 4. De ce în-memory rate limiting și nu Redis?
**Răspuns**:
- **Simplitate**: Nu adaugă dependency nou
- **Suficient pentru scale mic-mediu**:
- In-memory e OK pentru <1000 users concurrent
- Planul curent: test cu 1 user, extend la 10-20 max
- **Upgrade path clar**: Dacă scală, migrăm la Redis
- **Code unchanged**: Interfața rămâne aceeași
### 5. De ce SQLite și nu PostgreSQL pentru bot DB?
**Răspuns**:
- **Consistency**: Sistemul actual folosește SQLite
- **Zero infrastructure**: No server setup needed
- **Sufficient performance**: Telegram bot = low traffic
- **Easy backup**: Single file (`telegram_bot.db`)
- **Deployment simplicity**: Works în Windows Service
---
## 🔗 Integrare cu Sistemul Existent
### Flow Diagram - Ambele Metode
```
┌─────────────────────────────────┐
│ USER VREA SĂ SE AUTENTIFICE │
│ ÎN TELEGRAM BOT │
└─────────────┬───────────────────┘
┌─────────────────────────────────┐
│ ALEGE METODA: │
│ [Email + Parolă] sau [Web App] │
└─────────┬───────────┬───────────┘
│ │
┌────────────┘ └────────────┐
│ │
▼ ▼
┌────────────────────────┐ ┌──────────────────────────┐
│ METODA NOUĂ │ │ METODA EXISTENTĂ │
│ Email + Parolă │ │ Web App Linking │
└────────────────────────┘ └──────────────────────────┘
│ │
▼ ▼
1. /login în Telegram 1. Login în web app (username+pass)
2. Introdu email 2. Click "Link Telegram"
3. Primești cod pe email 3. Backend generează cod 8-char
4. Introdu cod în Telegram 4. Backend → POST /internal/save-code → Bot
5. Introdu parolă 5. User: /start ABC123XY
6. Bot verifică în Oracle 6. Bot auto-link (fără parolă din nou)
│ │
└──────────────┬──────────────────────┘
┌───────────────────────────────────┐
│ USER AUTENTIFICAT ÎN BOT │
│ JWT tokens saved în telegram_users│
└───────────────────────────────────┘
┌───────────────────────────────────┐
│ COMENZI DISPONIBILE: │
│ /companies, /dashboard, │
│ /facturi, /trezorerie, etc. │
└───────────────────────────────────┘
```
### Database Schema - Complete View
```sql
-- EXISTING TABLE (no changes)
CREATE TABLE telegram_users (
telegram_user_id INTEGER PRIMARY KEY,
username TEXT,
first_name TEXT,
last_name TEXT,
oracle_username TEXT,
jwt_token TEXT,
jwt_refresh_token TEXT,
token_expires_at TIMESTAMP,
linked_at TIMESTAMP,
last_active_at TIMESTAMP,
is_active BOOLEAN
);
-- EXISTING TABLE (no changes)
CREATE TABLE telegram_auth_codes (
code TEXT PRIMARY KEY, -- 8-char code from web app
telegram_user_id INTEGER,
oracle_username TEXT,
created_at TIMESTAMP,
expires_at TIMESTAMP,
used BOOLEAN DEFAULT 0,
used_at TIMESTAMP
);
-- NEW TABLE (for email auth)
CREATE TABLE email_auth_codes (
code TEXT PRIMARY KEY, -- 6-digit code sent via email
email TEXT NOT NULL,
oracle_username TEXT NOT NULL,
telegram_user_id INTEGER NOT NULL,
created_at TIMESTAMP,
expires_at TIMESTAMP,
used BOOLEAN DEFAULT 0,
used_at TIMESTAMP,
INDEX idx_email (email),
INDEX idx_telegram_user_id (telegram_user_id)
);
-- EXISTING TABLE (no changes)
CREATE TABLE telegram_sessions (
session_id TEXT PRIMARY KEY,
telegram_user_id INTEGER,
active_company_id INTEGER,
active_company_name TEXT,
conversation_state TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
expires_at TIMESTAMP
);
```
**Observații:**
- `telegram_auth_codes` = pentru web app linking (EXISTING)
- `email_auth_codes` = pentru email auth (NEW)
- Ambele tabele coexistă, fiecare pentru flow-ul propriu
- `telegram_users` = comună pentru ambele metode (linked users)
---
## 🚦 Success Criteria
### Implementarea e considerată SUCCESS dacă:
- [x] User poate facă `/login` în Telegram
- [x] User poate introduce email și primește cod pe email
- [x] User poate introduce cod din email (valid 5 min)
- [x] User poate introduce parola Oracle
- [x] Bot verifică credențialele în Oracle și generează JWT
- [x] User e autentificat și poate folosi comenzi (/companies, /dashboard, etc.)
- [x] Metoda veche (web app /start cod) funcționează în continuare
- [x] Ambele metode pot fi folosite de același user (switch între ele)
- [x] Codurile expirate sunt auto-șterse
- [x] Mesajul cu parola e șters automat din chat
- [x] Rate limiting funcționează (max 3 emails/oră)
- [x] Error handling: mesaje clare pentru fiecare caz de eroare
- [x] Testare cu mmarius28@gmail.com funcționează end-to-end
---
## 📅 Next Steps După Implementare
### Immediate (după deploy)
1. **Testare cu user real (Marius)**
- Verifică flow complet cu mmarius28@gmail.com
- Test ambele metode de autentificare
- Validează email delivery și formatting
2. **Monitoring**
- Watch logs pentru erori SMTP
- Monitor rate limiting triggers
- Check database growth (`email_auth_codes` table)
### Short-term (1-2 săptămâni)
1. **Adaugă email pentru alți useri**
- Identifică 5-10 useri pentru beta testing
- Adaugă email-uri în Oracle `UTILIZATORI` table
- Anunță disponibilitatea noii metode
2. **Collect feedback**
- UX pentru conversation flow
- Timing email delivery (sunt 5 min suficienți?)
- Edge cases descoperite
### Long-term (1-3 luni)
1. **Analytics**
- Câți useri folosesc fiecare metodă?
- Success rate pentru fiecare flow
- Common error patterns
2. **Optimizări**
- Migrate rate limiting la Redis (dacă scală)
- Adaugă email templates multiple (română + engleză)
- Consider migration de la SQLite la PostgreSQL (dacă >1000 users)
3. **Feature extensions**
- Email verification pentru schimbare parolă
- Email notifications pentru events (reports ready, etc.)
- Multi-language support
---
## 📞 Contact & Support
**Developer**: Claude Code (Anthropic)
**Project**: ROA2WEB - ERP Reports Application
**Date**: 2025-11-07
**Pentru întrebări despre acest plan:**
- Review cod în `reports-app/telegram-bot/` și `reports-app/backend/`
- Consultă `CLAUDE.md` pentru context general
- Check `TELEGRAM_COMMANDS.md` pentru comenzi existente
**Resurse utile:**
- python-telegram-bot docs: https://docs.python-telegram-bot.org/
- FastAPI docs: https://fastapi.tiangolo.com/
- aiosmtplib docs: https://aiosmtplib.readthedocs.io/
---
## 🎉 Conclusion
Acest plan implementează autentificare **email + parolă (2FA)** pentru Telegram Bot în mod **minimal invaziv**, păstrând întreaga funcționalitate existentă.
**Key highlights:**
-**0 breaking changes** - metoda veche funcționează în continuare
-**Simplu de implementat** - ~13h efort total
-**Securizat** - 2FA real cu rate limiting
-**Extensibil** - ușor de scalat pentru mai mulți useri
-**User-friendly** - conversation flow natural
**Ready to implement!** 🚀
---
*Document generat: 2025-11-07*
*Versiune: 1.0*
*Status: Planning Phase - Ready for Implementation*