# 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"""

๐Ÿ” Autentificare Telegram Bot

Salut {username},

Codul tฤƒu de autentificare este:

{code}

Codul expirฤƒ รฎn 5 minute.

Dacฤƒ nu ai solicitat acest cod, te rugฤƒm sฤƒ ignori acest email.


ROA2WEB - ERP Reports Application

""" # 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

๐Ÿ” ROA2WEB

Autentificare Telegram Bot

Salut {{username}},

Ai solicitat autentificarea รฎn aplicaศ›ia ROA2WEB Telegram Bot.

Codul tฤƒu de autentificare:

{{code}}

โฑ๏ธ Important: Acest cod expirฤƒ รฎn 5 minute.

Introdu acest cod รฎn conversaศ›ia cu Telegram Bot, apoi vei fi solicitat sฤƒ introduci parola.

Nu ai solicitat acest cod?
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.

``` ### 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 sฤƒ 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*