# ============================================================ # routers/authentification.py — Login JWT et profil courant # # Routes publiques (aucune dépendance d'authentification) : # c'est ici qu'on obtient le jeton exigé par les autres routeurs. # ============================================================ from fastapi import APIRouter, Depends, HTTPException, Request, status from fastapi.security import OAuth2PasswordRequestForm from auth import create_access_token, get_current_user, verify_password from config import get_cursor from domain import AuditAction from rate_limit import limiter router = APIRouter(tags=["Auth"]) @router.post("/auth/login") @limiter.limit("5/minute") def login(request: Request, form: OAuth2PasswordRequestForm = Depends()): """Authentifie un utilisateur et retourne un JWT.""" with get_cursor() as cursor: cursor.execute( "SELECT id_user, username, password_hash, role, actif " "FROM [USER] WHERE username = ?", form.username ) row = cursor.fetchone() if not row or not row[4] or not verify_password(form.password, row[2]): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Identifiants invalides" ) ip = request.client.host if request.client else None cursor.execute("UPDATE [USER] SET last_login = SYSDATETIME() WHERE id_user = ?", row[0]) cursor.execute( "INSERT INTO JOURNAL_AUDIT (id_user, username, action, ip) VALUES (?, ?, ?, ?)", row[0], row[1], AuditAction.LOGIN.value, ip ) token = create_access_token({"sub": row[1], "role": row[3], "uid": row[0]}) return {"access_token": token, "token_type": "bearer", "user": {"username": row[1], "role": row[3]}} @router.get("/auth/me") def me(user: dict = Depends(get_current_user)): """Profil de l'utilisateur connecté.""" return user