Add authentication schemas for user registration and login: - UserRegister: email, password (with strength validation), password_confirm - UserLogin: email, password - UserResponse: id, email, created_at, is_active (orm_mode=True) - TokenResponse: access_token, token_type, expires_in - TokenData: user_id, exp Includes field validators for password strength and password confirmation matching. Test coverage: 19 tests for all schemas
8.2 KiB
Prompt di Ingaggio: User Authentication (T17-T22)
🎯 MISSIONE
Implementare la fase User Authentication (T17-T22) del progetto OpenRouter API Key Monitor seguendo rigorosamente TDD.
📋 CONTEXTO
AGENTE: @tdd-developer
Repository: /home/google/Sources/LucaSacchiNet/openrouter-watcher
Stato Attuale:
- ✅ Setup completato (T01-T05): 59 test
- ✅ Database & Models (T06-T11): 73 test
- ✅ Security Services (T12-T16): 70 test
- 🎯 Totale: 202 test passanti, 100% coverage sui moduli implementati
Servizi Pronti da utilizzare:
hash_password(),verify_password()- inservices/password.pycreate_access_token(),decode_access_token()- inservices/jwt.pyEncryptionService- inservices/encryption.pygenerate_api_token(),verify_api_token()- inservices/token.pyUser,ApiKey,UsageStats,ApiTokenmodelsget_db(),Base- indatabase.py
Documentazione:
- PRD:
/home/google/Sources/LucaSacchiNet/openrouter-watcher/prd.md - Architecture:
/home/google/Sources/LucaSacchiNet/openrouter-watcher/export/architecture.md - Prompt Dettagliato:
/home/google/Sources/LucaSacchiNet/openrouter-watcher/prompt/prompt-authentication.md
🔧 TASK DA IMPLEMENTARE
T17: Creare Pydantic Schemas per Autenticazione
File: src/openrouter_monitor/schemas/auth.py
Requisiti:
UserRegister: email (EmailStr), password (min 12), password_confirm- Validatore: richiama
validate_password_strength() - Root validator: password == password_confirm
- Validatore: richiama
UserLogin: email, passwordUserResponse: id, email, created_at, is_active (orm_mode=True)TokenResponse: access_token, token_type="bearer", expires_inTokenData: user_id (sub), exp
Test: tests/unit/schemas/test_auth_schemas.py
T18: Implementare Endpoint POST /api/auth/register
File: src/openrouter_monitor/routers/auth.py
Requisiti:
- Endpoint:
POST /api/auth/register - Riceve:
UserRegisterschema - Logica:
- Verifica email non esista:
db.query(User).filter(User.email == ...).first() - Se esiste: HTTPException 400 "Email already registered"
- Hash password:
hash_password(user_data.password) - Crea User:
User(email=..., password_hash=...) - Salva:
db.add(),db.commit(),db.refresh() - Ritorna:
UserResponse, status 201
- Verifica email non esista:
Test: Register success, email duplicata (400), password debole (422)
T19: Implementare Endpoint POST /api/auth/login
File: src/openrouter_monitor/routers/auth.py
Requisiti:
- Endpoint:
POST /api/auth/login - Riceve:
UserLoginschema - Logica:
- Trova utente per email
- Se non trovato o inattivo: HTTPException 401 "Invalid credentials"
- Verifica password:
verify_password(credentials.password, user.password_hash) - Se fallita: HTTPException 401
- Genera JWT:
create_access_token(data={"sub": str(user.id)}) - Ritorna:
TokenResponsecon access_token
Test: Login success (200 + token), email inesistente (401), password sbagliata (401), utente inattivo (401)
T20: Implementare Endpoint POST /api/auth/logout
File: src/openrouter_monitor/routers/auth.py
Requisiti:
- Endpoint:
POST /api/auth/logout - Richiede:
current_user: User = Depends(get_current_user) - Logica: JWT stateless, logout gestito lato client
- Ritorna:
{"message": "Successfully logged out"}
Test: Logout con token valido (200), senza token (401)
T21: Implementare Dipendenza get_current_user
File: src/openrouter_monitor/dependencies/auth.py
Requisiti:
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security),
db: Session = Depends(get_db)
) -> User:
token = credentials.credentials
try:
payload = decode_access_token(token)
user_id = int(payload.get("sub"))
if not user_id:
raise HTTPException(401, "Invalid token payload")
except JWTError:
raise HTTPException(401, "Invalid or expired token")
user = db.query(User).filter(User.id == user_id).first()
if not user or not user.is_active:
raise HTTPException(401, "User not found or inactive")
return user
Test: Token valido ritorna utente, token mancante (401), token scaduto (401), token invalido (401), utente inesistente (401), utente inattivo (401)
T22: Scrivere Test per Auth Endpoints
File: tests/unit/routers/test_auth.py
Requisiti:
- Usare
TestClientda FastAPI - Fixture:
test_user,auth_token,authorized_client - Test coverage >= 90%
Test da implementare:
- Register: success (201), email duplicata (400), password debole (422), email invalida (422)
- Login: success (200 + token), email inesistente (401), password sbagliata (401), utente inattivo (401)
- Logout: success (200), senza token (401)
- get_current_user: protetto con token valido, senza token (401), token scaduto (401)
🔄 WORKFLOW TDD
Per OGNI task:
- RED: Scrivi test che fallisce (prima del codice!)
- GREEN: Implementa codice minimo per passare il test
- REFACTOR: Migliora codice, test rimangono verdi
📁 STRUTTURA FILE DA CREARE
src/openrouter_monitor/
├── schemas/
│ ├── __init__.py
│ └── auth.py # T17
├── routers/
│ ├── __init__.py
│ └── auth.py # T18, T19, T20
└── dependencies/
├── __init__.py
└── auth.py # T21
tests/unit/
├── schemas/
│ ├── __init__.py
│ └── test_auth_schemas.py # T17 + T22
└── routers/
├── __init__.py
└── test_auth.py # T18-T21 + T22
🧪 ESEMPI TEST
Test Schema
@pytest.mark.unit
def test_user_register_valid_data_passes_validation():
data = UserRegister(
email="test@example.com",
password="SecurePass123!",
password_confirm="SecurePass123!"
)
assert data.email == "test@example.com"
Test Endpoint
@pytest.mark.asyncio
async def test_register_new_user_returns_201(client, db_session):
response = client.post("/api/auth/register", json={
"email": "test@example.com",
"password": "SecurePass123!",
"password_confirm": "SecurePass123!"
})
assert response.status_code == 201
assert response.json()["email"] == "test@example.com"
✅ CRITERI DI ACCETTAZIONE
- T17: Schemas auth con validazione completa
- T18: POST /api/auth/register (201/400/422)
- T19: POST /api/auth/login (200/401)
- T20: POST /api/auth/logout (200)
- T21: get_current_user dependency funzionante
- T22: Test auth coverage >= 90%
- Tutti i test passano:
pytest tests/unit/routers/test_auth.py -v - 6 commit atomici con conventional commits
- progress.md aggiornato
📝 COMMIT MESSAGES
feat(schemas): T17 add Pydantic auth schemas
feat(auth): T18 implement user registration endpoint
feat(auth): T19 implement user login endpoint
feat(auth): T20 implement user logout endpoint
feat(deps): T21 implement get_current_user dependency
test(auth): T22 add comprehensive auth endpoint tests
🚀 VERIFICA FINALE
cd /home/google/Sources/LucaSacchiNet/openrouter-watcher
# Test schemas
pytest tests/unit/schemas/test_auth_schemas.py -v
# Test routers
pytest tests/unit/routers/test_auth.py -v --cov=src/openrouter_monitor/routers
# Test completo
pytest tests/unit/ -v --cov=src/openrouter_monitor
⚠️ NOTE IMPORTANTI
- Path assoluti: Usa sempre
/home/google/Sources/LucaSacchiNet/openrouter-watcher/ - Servizi esistenti: Riutilizza
hash_password,verify_password,create_access_token,decode_access_token - Database: Usa
get_db()dadatabase.pyper dependency injection - Models: Importa da
modelspackage (User, ApiKey, etc.) - Sicurezza: Mai loggare password o token in plaintext
- Errori: Errori generici per credenziali invalide (non leakare info)
AGENTE: @tdd-developer
INIZIA CON: T17 - Pydantic schemas
QUANDO FINITO: Conferma completamento e aggiorna progress.md