feat(auth): per-email lockout dopo 5 fallimenti consecutivi
In topologia NAT corporate il rate-limit per-IP non discrimina utenti reali (tutti CoinSafe escono da 192.168.0.1 verso Caddy). HDRCR-117 ha alzato il limite a 60/min per non bloccare utenti legittimi, ma serviva una difesa per brute-force su una singola credenziale che fosse indipendente dall'IP. `app/auth/lockout.py` implementa counter+flag su redis con TTL: - `auth:failed:<email>` counter di fallimenti consecutivi (TTL 5 min) - `auth:locked:<email>` flag di lockout attivo (TTL 15 min) 5 fallimenti su `alice@x` dentro 5 min lockano `alice@x` per 15 min, da qualunque IP. Login riuscito resetta il counter. L'errore restituito è sempre lo stesso 401 generico per evitare user-enumeration. Se redis è giù degrada silenziosamente: niente lockout, ma il rate-limit per-IP del Limiter resta come backstop. Test fakeano `redis.asyncio.from_url` con uno store in-memory che implementa solo i comandi usati — niente redis richiesto in CI. HDRCR-118 #comment Aggiunto app/auth/lockout.py e integrato nel /auth/login
Loading